You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@qontoctl/core: card list / card show no longer fail for the entire account when Qonto returns a card_type, card_level, or status value outside the previously-hardcoded enum sets. These three CardSchema fields were closed z.enum() unions (card_type 2 values, card_level 7, status 13), so a single card carrying any beyond-enum value failed the whole list/show response parse — not just that one card. All three are pure pass-through (every consumer echoes them; nothing branches exhaustively), so they are now modelled as open z.string(), matching the sibling CardTypeAppearancesSchema that already treated card_type as z.string(); the lower-churn type_of_print and appearance.theme enums are kept closed by design. Hand-written Card types widened to match, with regression tests asserting beyond-enum values parse. The v3.0.1 pre-release contract-probe run reports zero drift against the live /v2/cards response (#672, #678).
@qontoctl/core + @qontoctl/mcp + @qontoctl/cli: an SCA-gated write (e.g. MCP transfer_create, CLI transfer create) that requires Strong Customer Authentication no longer fails with a bare, misleading HTTP 404 not_found while leaving an orphaned SCA push on the user's device. Root cause: after extracting the session token from the transfer's 428 response, the continuation polled the session at GET /v2/sca/sessions/{token} (slash) and parsed a { sca_session: { status } } body — both unverified guesses baked into the original SCA implementation. The authoritative Qonto endpoint is GET /v2/sca_sessions/{token} (underscore, not sca/sessions) returning { result, canceled_at }. The slash path has no gateway route, so the reporter's --debug capture on #669 shows a gateway-level 404 (Tyk text/plain, x-tyk-trace-id, no x-request-id — the request never reached the Rails app); the raw QontoApiError(404) then propagated through executeWithSca and lost the session token, so both surfaces rendered Qonto API error (HTTP 404): not_found: Not found and the user was left with a challenge that, once approved, completed nothing (no transfer created; account and authorized balances unchanged). getScaSession now builds the correct underscore path and parses the { result } shape on both production and sandbox — the sandbox mocked_sca_sessions sibling already used exactly this path/shape form (the only variant ever exercised, in E2E, which is why the production bug stayed hidden). Defensive safety net (independent of the path fix):executeWithSca now also wraps any failed poll (anything other than a clean deny / timeout) in a new typed ScaPollingFailedError that preserves the session token and the underlying cause, so a future transient / 5xx / expired-session (Qonto returns 412) poll failure can no longer lose the token and strand the user behind a bare error; both consumer surfaces handle it with actionable, duplicate-payment-safe guidance: the MCP wrapper returns a structured isError: true response that pins the underlying failure, warns that the operation's outcome is unknown and to verify via transfer_list / the Qonto app before retrying, and documents the token-bound retry (sca_session_token) that skips the failing poll; the CLI's global error handler prints the parallel guidance with the preserved token. Verifiability — the corrected production path is reachable from NO automated test: the path/shape correction and the token-preservation are unit-tested at all three layers (core service + handler, MCP wrapper + withClient fallback, CLI error handler), but the sandbox routes SCA to the mocked_sca_sessions sibling and CI is api-key-only (no OAuth/SCA write path), so the live production sca_sessions poll is exercised by no sandbox or CI run — its correctness rests on the Qonto API reference and must be live-verified against a real SCA-gated transfer with a paired device before being relied upon. New ScaPollingFailedError is additively exported from @qontoctl/core (#669).
Changed
Tooling / dependencies: adopt the npm-major dependency group — TypeScript 5.9 → 6.0, ESLint 9.39 → 10.6 (@eslint/js 10), @types/node 24 → 26, and commander 14 → 15. commander is the group's only production dependency, and its major bump required no code changes. Adaptations were build-time only: restore the root typescript catalog specifier in the lockfile, add "types": ["node"] to tsconfig.base.json (TypeScript 6.0 no longer auto-includes @types/node globals), and resolve the two rules ESLint 10 promoted into recommended (preserve-caught-error, no-useless-assignment). No runtime behaviour change (#668).
Docs
Docs: strengthened the npm-facing README and positioning — added an explicit "unofficial, not affiliated with Qonto" disclaimer plus positioning relative to Qonto's official MCP offering, and synced the advertised MCP tool list with the shipped tools for parity (590c436, 66e2323).