@visa/cli 4.1.0-rc.25 → 4.1.0-rc.250

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +273 -227
  2. package/dist/checkout-engine/adapters/generic.d.ts +69 -0
  3. package/dist/checkout-engine/adapters/generic.js +383 -58
  4. package/dist/checkout-engine/adapters/index.d.ts +4 -1
  5. package/dist/checkout-engine/adapters/index.js +10 -3
  6. package/dist/checkout-engine/adapters/shopify.d.ts +80 -0
  7. package/dist/checkout-engine/adapters/shopify.js +688 -0
  8. package/dist/checkout-engine/amount.d.ts +15 -0
  9. package/dist/checkout-engine/amount.js +72 -0
  10. package/dist/checkout-engine/browser-launch.d.ts +9 -4
  11. package/dist/checkout-engine/browser-launch.js +19 -4
  12. package/dist/checkout-engine/browserbase-browser.d.ts +24 -0
  13. package/dist/checkout-engine/browserbase-browser.js +186 -0
  14. package/dist/checkout-engine/cli-engine.d.ts +297 -4
  15. package/dist/checkout-engine/cli-engine.js +942 -48
  16. package/dist/checkout-engine/confirmed-merchants.d.ts +31 -0
  17. package/dist/checkout-engine/confirmed-merchants.js +165 -0
  18. package/dist/checkout-engine/detect.d.ts +1 -1
  19. package/dist/checkout-engine/detect.js +26 -0
  20. package/dist/checkout-engine/evidence.d.ts +4 -1
  21. package/dist/checkout-engine/evidence.js +51 -6
  22. package/dist/checkout-engine/executor.d.ts +64 -4
  23. package/dist/checkout-engine/executor.js +625 -146
  24. package/dist/checkout-engine/hosted-approval.d.ts +124 -7
  25. package/dist/checkout-engine/hosted-approval.js +384 -54
  26. package/dist/checkout-engine/index.d.ts +9 -2
  27. package/dist/checkout-engine/index.js +8 -1
  28. package/dist/checkout-engine/instrument.d.ts +7 -0
  29. package/dist/checkout-engine/instrument.js +4 -0
  30. package/dist/checkout-engine/known-merchants.d.ts +10 -0
  31. package/dist/checkout-engine/known-merchants.js +38 -0
  32. package/dist/checkout-engine/live-fill-approval.d.ts +5 -20
  33. package/dist/checkout-engine/live-fill-approval.js +20 -51
  34. package/dist/checkout-engine/mandate/card-mandate.d.ts +121 -0
  35. package/dist/checkout-engine/mandate/card-mandate.js +226 -0
  36. package/dist/checkout-engine/mandate/mandate-ledger.d.ts +174 -0
  37. package/dist/checkout-engine/mandate/mandate-ledger.js +410 -0
  38. package/dist/checkout-engine/outcome.d.ts +2 -2
  39. package/dist/checkout-engine/outcome.js +36 -1
  40. package/dist/checkout-engine/owner-only-file.d.ts +9 -0
  41. package/dist/checkout-engine/owner-only-file.js +20 -1
  42. package/dist/checkout-engine/receipt-dir.d.ts +6 -0
  43. package/dist/checkout-engine/receipt-dir.js +8 -0
  44. package/dist/checkout-engine/receipt.d.ts +56 -2
  45. package/dist/checkout-engine/receipt.js +55 -16
  46. package/dist/checkout-engine/shopify-primary-domain.d.ts +25 -0
  47. package/dist/checkout-engine/shopify-primary-domain.js +96 -0
  48. package/dist/checkout-engine/trace-handles.d.ts +8 -0
  49. package/dist/checkout-engine/trace-handles.js +12 -0
  50. package/dist/checkout-engine/types.d.ts +28 -2
  51. package/dist/checkout-engine/unresolved-charges.d.ts +34 -0
  52. package/dist/checkout-engine/unresolved-charges.js +134 -0
  53. package/dist/checkout-engine/vgs-gateway/server-mint-client.d.ts +53 -1
  54. package/dist/checkout-engine/vgs-gateway/server-mint-client.js +78 -10
  55. package/dist/checkout-engine/vgs-live-instrument.d.ts +38 -35
  56. package/dist/checkout-engine/vgs-live-instrument.js +51 -74
  57. package/dist/checkout-engine/vic-confirmation.d.ts +18 -0
  58. package/dist/checkout-engine/vic-confirmation.js +9 -3
  59. package/dist/checkout-engine/web-bot-auth.d.ts +98 -0
  60. package/dist/checkout-engine/web-bot-auth.js +218 -0
  61. package/dist/cli.js +905 -505
  62. package/dist/mcp-apps/ucp-checkout.html +280 -0
  63. package/dist/mcp-server/index.js +744 -175
  64. package/dist/merchant-ucp-mcp/index.js +6 -0
  65. package/dist/skills/pair-visa-agent/RUNTIMES.md +122 -79
  66. package/dist/skills/pair-visa-agent/SKILL.md +436 -279
  67. package/dist/skills/pair-visa-agent/scripts/__tests__/setup.test.mjs +407 -0
  68. package/dist/skills/pair-visa-agent/scripts/setup.mjs +310 -30
  69. package/dist/skills/visa-shopify-checkout/SKILL.md +122 -0
  70. package/dist/skills/visa-shopify-checkout/references/evidence-and-states.md +37 -0
  71. package/dist/skills/visa-ucp-shopping/SKILL.md +86 -0
  72. package/dist/subway-direct.mjs +1 -0
  73. package/install.ps1 +9 -46
  74. package/install.sh +5 -37
  75. package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
  76. package/package.json +32 -28
  77. package/server.json +4 -4
  78. package/dist/checkout-engine/inline-target.d.ts +0 -13
  79. package/dist/checkout-engine/inline-target.js +0 -37
  80. package/dist/checkout-engine/pay-args.d.ts +0 -14
  81. package/dist/checkout-engine/pay-args.js +0 -44
  82. package/dist/checkout-engine/pay.d.ts +0 -1
  83. package/dist/checkout-engine/pay.js +0 -13
  84. package/dist/checkout-engine/repo-env.d.ts +0 -11
  85. package/dist/checkout-engine/repo-env.js +0 -23
  86. package/dist/checkout-engine/run-live-fill.d.ts +0 -1
  87. package/dist/checkout-engine/run-live-fill.js +0 -443
  88. package/dist/checkout-engine/vgs-gateway/fetch-credential.d.mts +0 -74
  89. package/dist/checkout-engine/vgs-gateway/fetch-credential.mjs +0 -248
@@ -1,19 +1,55 @@
1
1
  import { type Browser } from 'playwright-core';
2
- import { prepareCheckout as realPrepareCheckout, submitApprovedCheckout as realSubmitApprovedCheckout, type PreparedCheckoutSessionStore } from './executor.js';
3
- import { runHostedApproval as realRunHostedApproval } from './hosted-approval.js';
2
+ import { prepareCheckout as realPrepareCheckout, submitApprovedCheckout as realSubmitApprovedCheckout, type CheckoutMode, type CheckoutOutcome, type CheckoutFailureCode, type CheckoutResult, type PreparedCheckoutSessionStore } from './executor.js';
3
+ import { claimMandatePickup as realClaimMandatePickup, runHostedApproval as realRunHostedApproval } from './hosted-approval.js';
4
4
  import { type VgsCheckoutTarget } from './vgs-live-instrument.js';
5
+ import { serverFetchCryptogram, serverPostConfirmation } from './vgs-gateway/server-mint-client.js';
6
+ import { type CardMandateFacts } from './mandate/card-mandate.js';
7
+ import { MandateLedger } from './mandate/mandate-ledger.js';
5
8
  import { writeReceipt as realWriteReceipt } from './receipt.js';
6
9
  import { reportVicOutcome as realReportVicOutcome, type VicConfirmationReport } from './vic-confirmation.js';
7
- import type { Contact } from './types.js';
10
+ import type { Contact, OtpResolver } from './types.js';
11
+ /**
12
+ * A card-mandate draw failed transiently (retryable) rather than definitively.
13
+ * Gateway 5xx, "server cryptogram not completed / try again", and network
14
+ * reset/timeout errors are transient: the mandate stays healthy and must NOT be
15
+ * disabled. Walks the error's cause chain so a wrapped MandateDrawDeclinedError
16
+ * is classified by its underlying gateway error. Exported for tests.
17
+ */
18
+ export declare function isTransientDrawFailure(err: unknown): boolean;
19
+ /**
20
+ * A verdict refusal may be wrapped by drawFromMandate after its local
21
+ * reservation is released. Walk the cause chain so the original auth status +
22
+ * reasons still decide whether the mandate is permanently disabled.
23
+ */
24
+ export declare function classifyCardDrawVerdictFailure(err: unknown): {
25
+ transient: boolean;
26
+ grantCapacity: boolean;
27
+ reasons: string[];
28
+ } | null;
8
29
  export type CliReviewInput = {
9
30
  url: string;
31
+ checkoutRoute: 'guest-card';
10
32
  amount: string;
11
33
  currency: string;
12
34
  credentialPath: string;
35
+ /** See {@link CardInstrumentSource}. */
36
+ cardTokenId?: string;
37
+ /** Exact request-key identity selected by the caller. */
38
+ agentJkt?: string;
39
+ /** Owner-facing selected-agent label for the compact local receipt. */
40
+ agentName?: string;
41
+ /** Safe display suffix derived from the selected card grant label. */
42
+ cardLast4?: string;
13
43
  contact: Contact;
14
44
  approvalBaseUrl: string;
15
45
  merchantName?: string;
16
46
  merchantCountryCode?: string;
47
+ trustedMerchantIdentity?: Readonly<{
48
+ handoffId: string;
49
+ checkoutId: string;
50
+ allowedOrigins: readonly string[];
51
+ expiresAt: string;
52
+ }>;
17
53
  };
18
54
  export type CliReviewFacts = {
19
55
  reviewId: string;
@@ -23,38 +59,295 @@ export type CliReviewFacts = {
23
59
  submitTargetFingerprint: string;
24
60
  detectedRoles: string[];
25
61
  };
62
+ /**
63
+ * A checkout was inspected successfully but cannot be reviewed safely.
64
+ *
65
+ * The explicit fields survive the CLI's copied-engine boundary structurally,
66
+ * so MCP callers do not have to parse the human-readable message.
67
+ */
68
+ export declare class CheckoutReviewRefusedError extends Error {
69
+ readonly code = "CHECKOUT_REVIEW_REFUSED";
70
+ readonly checkoutOutcome: CheckoutOutcome;
71
+ readonly failureCode?: CheckoutFailureCode;
72
+ readonly requiresAdapter: string[];
73
+ readonly detectedRoles: string[];
74
+ readonly receiptWrite: ReceiptWriteObservation;
75
+ readonly detail?: string;
76
+ constructor(result: CheckoutResult, receiptWrite: ReceiptWriteObservation);
77
+ }
26
78
  export type CliPayInput = CliReviewInput & {
27
79
  reviewId: string;
28
80
  submit: boolean;
81
+ /**
82
+ * Headless handoff for this payment attempt. The MCP layer uses it to return
83
+ * the hosted passkey URL to a messaging surface while the engine continues
84
+ * waiting in-process.
85
+ */
86
+ onApprovalUrl?: (url: string) => void;
29
87
  };
30
88
  export type CliReceiptFacts = {
31
- outcome: string;
89
+ outcome: CheckoutOutcome;
32
90
  confirmationRef: string | null;
33
91
  receiptPath: string | null;
34
92
  detail: string | null;
35
93
  vicConfirmation: VicConfirmationReport | null;
94
+ /**
95
+ * Which credential path actually ran: `mandate` = tap-free draw against an
96
+ * existing card mandate; `null` = none ran (a pre-flight refusal, e.g. no
97
+ * prepared review or no covering mandate). Transparency, never magic — the
98
+ * caller can always see whether a passkey was skipped.
99
+ */
100
+ source: 'mandate' | null;
101
+ /** Remaining mandate budget (minor units) after a mandate draw; else null. */
102
+ remainingMinor: number | null;
103
+ /**
104
+ * Immutable VIC/VGS intent used for server-side receipt correlation. This is
105
+ * an opaque non-credential identifier; it never carries the mint/verdict
106
+ * bearer that authorizes a cryptogram or confirmation. Present only after a
107
+ * card confirmation target exists.
108
+ */
109
+ processorIntentId?: string | null;
110
+ /** True only when the checkout engine recorded a credential-minted step. */
111
+ credentialIssued: boolean;
112
+ /** True only when at least one payment field was successfully filled. */
113
+ credentialDisclosed: boolean;
114
+ /** Privacy-safe local receipt persistence outcome for centralized correlation. */
115
+ receiptWrite?: ReceiptWriteObservation;
116
+ };
117
+ type PayAttempt = {
118
+ fingerprint: string;
119
+ promise: Promise<CliReceiptFacts>;
120
+ };
121
+ export type CliStartMandateInput = {
122
+ /** Optional local card-capability selector (legacy name or exact request-key JKT). */
123
+ agentRef?: string;
124
+ ceiling: string;
125
+ currency: string;
126
+ credentialPath: string;
127
+ /** See {@link CardInstrumentSource}. */
128
+ cardTokenId?: string;
129
+ contact: Contact;
130
+ approvalBaseUrl: string;
131
+ /**
132
+ * Per-purchase cap (decimal string, > 0 and <= ceiling). Registered with the
133
+ * approval context so the operator reads it as a worst-case term, and carried
134
+ * onto the budget mint token so it is enforced at draw time.
135
+ */
136
+ perTransaction?: string;
137
+ /**
138
+ * Agent-supplied one-liner shown on the approval page in a labeled
139
+ * "written by the agent" block — provenance for the human, never trusted.
140
+ */
141
+ intent?: string;
142
+ /**
143
+ * Per-call relay of the hosted-approval URL, the moment it is known and
144
+ * BEFORE the (up-to-timeout) wait. Takes precedence over the engine-level
145
+ * `deps.onApprovalUrl`.
146
+ *
147
+ * The engine seam was deps-only, and the MCP surface builds its engine once
148
+ * per process without it — so `start_card_mandate` produced an approval URL,
149
+ * printed it to stderr, and blocked the agent's turn for minutes with no way
150
+ * to tell the human what to open. A per-call hook lets one caller relay the
151
+ * URL without every caller sharing one engine-wide callback.
152
+ */
153
+ onApprovalUrl?: (url: string) => void;
154
+ };
155
+ export type CliClaimMandateInput = {
156
+ /** Optional local card-capability selector (legacy name or exact request-key JKT). */
157
+ agentRef?: string;
158
+ /** Single-use pickup code the owner handed over from the panel. */
159
+ pickupCode: string;
160
+ /**
161
+ * The agent handoff this runtime just redeemed, when the pickup code rode
162
+ * inside its claim code. A dialog-minted budget pins that handoff instead of
163
+ * a key — there was no key yet — so this is what the pin is checked against.
164
+ */
165
+ expectedHandoffId?: string;
166
+ credentialPath: string;
167
+ /** See {@link CardInstrumentSource}. */
168
+ cardTokenId?: string;
169
+ approvalBaseUrl: string;
170
+ };
171
+ export type CliMandateFacts = CardMandateFacts & {
172
+ merchantHost: string;
173
+ /**
174
+ * True when the mandate minted its ceiling intent but the #5942 register
175
+ * handshake failed, so `findCovering` will SKIP it and no tap-free draw is
176
+ * possible. The mandate exists but is not usable — the caller must surface
177
+ * this (not report a plain success). Absent/false means registration succeeded;
178
+ * mandate-start now refuses before approval when no capability can register.
179
+ */
180
+ registerFailed?: boolean;
181
+ /**
182
+ * The server's refusal reason when {@link registerFailed} is true, verbatim.
183
+ * Carried out so the CLI can distinguish causes that need DIFFERENT operator
184
+ * actions — notably `token_mismatch`, which means the on-device grant record's
185
+ * cached token no longer matches the owner's live agentic token (they
186
+ * re-enrolled a card after the grant) and is fixed by re-running `grant-card`,
187
+ * not by retrying `mandate start`. Without the reason every failure reads as
188
+ * "the auth server was unreachable", which sends the operator in a loop.
189
+ */
190
+ registerFailureReason?: string;
36
191
  };
37
192
  type Session = {
38
193
  browser: Browser;
194
+ /** Exact caller URL repeated at pay time; may contain a UCP capability. */
195
+ requestUrl: string;
196
+ checkoutRoute: 'guest-card';
39
197
  target: VgsCheckoutTarget;
40
198
  amountMinor: number;
41
199
  currency: string;
42
200
  contact: Contact;
201
+ agentJkt?: string;
43
202
  cleanupTimer: ReturnType<typeof setTimeout>;
44
203
  };
204
+ export interface CardDrawVerdictDraw {
205
+ tokenId: string;
206
+ amount: string;
207
+ currency: string;
208
+ merchantName: string;
209
+ merchantUrl: string;
210
+ merchantCountryCode: string;
211
+ }
212
+ export interface CardDrawVerdictCapability {
213
+ /** Opaque to the engine — passed straight back to {@link CardDrawVerdictSeam.fetchVerdict}. */
214
+ agentKey: unknown;
215
+ /** Root-signed delegation for a short-lived device key. */
216
+ runtimeCertificate?: string;
217
+ agentJkt: string;
218
+ /** Auth origin that minted the binding and hosts the /v4/card/draw* routes. */
219
+ authBaseUrl: string;
220
+ }
221
+ export interface CardDrawVerdictSeam {
222
+ loadCapability: (agentRef?: string) => CardDrawVerdictCapability | null;
223
+ fetchVerdict: (input: {
224
+ authBaseUrl: string;
225
+ agentKey: unknown;
226
+ runtimeCertificate?: string;
227
+ mandateId: string;
228
+ drawId: string;
229
+ draw: CardDrawVerdictDraw;
230
+ }) => Promise<{
231
+ verdict: string;
232
+ remainingCents: number;
233
+ }>;
234
+ }
235
+ export interface CardMandateRegisterSeam {
236
+ loadCapability: (agentRef?: string) => {
237
+ agentKey: unknown;
238
+ runtimeCertificate?: string;
239
+ agentJkt: string;
240
+ authBaseUrl: string;
241
+ } | null;
242
+ register: (input: {
243
+ authBaseUrl: string;
244
+ agentKey: unknown;
245
+ runtimeCertificate?: string;
246
+ mandateId: string;
247
+ mintToken: string;
248
+ ceiling: string;
249
+ currency: string;
250
+ }) => Promise<{
251
+ ok: boolean;
252
+ reason?: string;
253
+ /**
254
+ * The ceiling the server committed — `min(requested, the owner's live card
255
+ * grant cap)`. Omitted by an older auth that does not report it, in which
256
+ * case the requested ceiling stands.
257
+ */
258
+ approvedCeilingMinor?: number;
259
+ }>;
260
+ }
45
261
  export type CliEngineDeps = {
46
262
  launchBrowser?: () => Promise<Browser>;
47
263
  prepareCheckout?: typeof realPrepareCheckout;
48
264
  submitApprovedCheckout?: typeof realSubmitApprovedCheckout;
49
265
  runHostedApproval?: typeof realRunHostedApproval;
266
+ /** Injectable pickup-code redeem for claimCardMandate — tests pass a fake. */
267
+ claimMandatePickup?: typeof realClaimMandatePickup;
268
+ /**
269
+ * Relay the hosted-approval URL to the caller as DATA the moment it is known,
270
+ * before the (up-to-timeout) poll wait. A headless agent surface wires this to
271
+ * hand the URL to its operator; the raw CLI leaves it unset (the URL prints to
272
+ * stderr and best-effort opens a browser).
273
+ */
274
+ onApprovalUrl?: (url: string) => void;
50
275
  reportVicOutcome?: typeof realReportVicOutcome;
51
276
  writeReceipt?: typeof realWriteReceipt;
277
+ /**
278
+ * Privacy-safe receipt persistence lifecycle signal. The event deliberately
279
+ * excludes the receipt body, checkout URL, filesystem path, and raw error.
280
+ * Observer failures are swallowed so logging can never alter checkout state.
281
+ */
282
+ onReceiptWrite?: (event: ReceiptWriteObservation) => void | Promise<void>;
283
+ /** Injectable privacy-safe id source for terminal observations before reviewId exists. */
284
+ createObservationId?: () => string;
52
285
  store?: PreparedCheckoutSessionStore;
53
286
  sessions?: Map<string, Session>;
287
+ /** Exact-review singleflight registry; tests inject a fresh map for isolation. */
288
+ payAttempts?: Map<string, PayAttempt>;
54
289
  ttlMs?: number;
290
+ /** Owner-only card-mandate ledger — defaults to the ~/.visa-mcp singleton. */
291
+ ledger?: MandateLedger;
292
+ /** Injectable clock for mandate expiry decisions (tests pin it). */
293
+ now?: () => Date;
294
+ /**
295
+ * Cryptogram transport for the TAP-FREE mandate draw — defaults to the real
296
+ * server-side mint route. Injectable so a test can drive the draw-reject ->
297
+ * markUnhonored path with no network.
298
+ */
299
+ serverFetchCryptogram?: typeof serverFetchCryptogram;
300
+ /** Injectable confirmation transport; defaults to verify-web. */
301
+ serverPostConfirmation?: typeof serverPostConfirmation;
302
+ /**
303
+ * #5923 delegated card-draw verdict seam (see {@link CardDrawVerdictSeam}).
304
+ * Injected by the CLI when the runtime holds separately provisioned card
305
+ * authority. When absent, a covering-mandate draw fails before cryptogram mint.
306
+ */
307
+ cardDrawVerdict?: CardDrawVerdictSeam;
308
+ /**
309
+ * #5942 delegated card-mandate register seam (see {@link CardMandateRegisterSeam}).
310
+ * Required by mandate-start. When absent, the ceremony is refused before
311
+ * passkey approval because a budget token cannot act as draw authority.
312
+ */
313
+ cardMandateRegister?: CardMandateRegisterSeam;
314
+ /**
315
+ * Canonical mailbox OTP reader supplied by the runtime. The checkout engine
316
+ * owns no mailbox credential and never imports an email provider package.
317
+ */
318
+ resolveEmailOtp?: OtpResolver;
319
+ };
320
+ export type ReceiptWriteObservation = {
321
+ event: 'checkout_receipt_write';
322
+ status: 'written' | 'failed';
323
+ mode: CheckoutMode;
324
+ checkoutOutcome: CheckoutOutcome;
325
+ merchantHost: string;
326
+ observationId: string;
327
+ failureCode: string | null;
328
+ errorCode: string | null;
329
+ panRedactions: number | null;
55
330
  };
56
331
  export declare function createCliCheckoutEngine(deps?: CliEngineDeps): {
332
+ startCardMandate(input: CliStartMandateInput): Promise<CliMandateFacts>;
333
+ claimCardMandate(input: CliClaimMandateInput): Promise<CliMandateFacts>;
57
334
  review(input: CliReviewInput): Promise<CliReviewFacts>;
335
+ /**
336
+ * Give up a prepared review without paying it (#7100).
337
+ *
338
+ * The retained browser is what lets a same-process MCP `review` → `pay`
339
+ * draw against the checkout it already inspected. A TERMINAL review-only
340
+ * run has no such second call — that process tells the operator to re-RUN
341
+ * with `--submit`, and the new process cannot reach this one's in-memory
342
+ * session. So the browser sat open for the full TTL, and because a live
343
+ * browser connection keeps the event loop alive, a command that had already
344
+ * succeeded looked hung until Ctrl-C.
345
+ *
346
+ * Idempotent and non-throwing: an unknown or already-released id is a
347
+ * no-op, so it is safe on an error path that may not have prepared anything
348
+ * and safe to call twice.
349
+ */
350
+ releaseReview(reviewId: string): Promise<void>;
58
351
  pay(input: CliPayInput): Promise<CliReceiptFacts>;
59
352
  };
60
353
  export {};