@visa/cli 4.1.0-rc.112 → 4.1.0-rc.114
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.
- package/dist/checkout-engine/cli-engine.d.ts +6 -0
- package/dist/checkout-engine/cli-engine.js +1 -0
- package/dist/checkout-engine/hosted-approval.d.ts +7 -0
- package/dist/checkout-engine/hosted-approval.js +29 -2
- package/dist/cli.js +369 -365
- package/dist/mcp-server/index.js +239 -235
- package/native/bin/win32-x64/visa-keychain-win.exe +0 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -98,6 +98,12 @@ export type CliClaimMandateInput = {
|
|
|
98
98
|
agentRef?: string;
|
|
99
99
|
/** Single-use pickup code the owner handed over from the panel. */
|
|
100
100
|
pickupCode: string;
|
|
101
|
+
/**
|
|
102
|
+
* The agent handoff this runtime just redeemed, when the pickup code rode
|
|
103
|
+
* inside its claim code. A dialog-minted budget pins that handoff instead of
|
|
104
|
+
* a key — there was no key yet — so this is what the pin is checked against.
|
|
105
|
+
*/
|
|
106
|
+
expectedHandoffId?: string;
|
|
101
107
|
credentialPath: string;
|
|
102
108
|
/** See {@link CardInstrumentSource}. */
|
|
103
109
|
cardTokenId?: string;
|
|
@@ -374,6 +374,7 @@ export function createCliCheckoutEngine(deps = {}) {
|
|
|
374
374
|
baseUrl: input.approvalBaseUrl,
|
|
375
375
|
pickupCode: input.pickupCode,
|
|
376
376
|
agentJkt: registerCap.agentJkt,
|
|
377
|
+
...(input.expectedHandoffId ? { expectedHandoffId: input.expectedHandoffId } : {}),
|
|
377
378
|
tokenId: credential.tokenId,
|
|
378
379
|
});
|
|
379
380
|
const ceilingMinor = decimalToMinor(claim.ceiling);
|
|
@@ -174,6 +174,13 @@ export type ClaimMandatePickupOptions = {
|
|
|
174
174
|
pickupCode: string;
|
|
175
175
|
/** This runtime's exact current request-key JKT — the only key the code may fund. */
|
|
176
176
|
agentJkt: string;
|
|
177
|
+
/**
|
|
178
|
+
* The agent handoff this runtime just redeemed, when the code came from one.
|
|
179
|
+
* A budget minted in the Add-agent dialog exists before the agent does, so it
|
|
180
|
+
* pins the handoff rather than a key; this is the value that pin is compared
|
|
181
|
+
* against. Absent for the panel flow, whose budgets pin a key instead.
|
|
182
|
+
*/
|
|
183
|
+
expectedHandoffId?: string;
|
|
177
184
|
/** The card token id this runtime's card authority points at. */
|
|
178
185
|
tokenId: string;
|
|
179
186
|
fetchImpl?: typeof fetch;
|
|
@@ -358,7 +358,7 @@ function pickupMismatch(field, detail) {
|
|
|
358
358
|
'mandate in the panel for this agent');
|
|
359
359
|
}
|
|
360
360
|
export async function claimMandatePickup(opts) {
|
|
361
|
-
const { baseUrl, pickupCode, agentJkt, tokenId, fetchImpl = fetch, sleep = defaultSleep, now = Date.now, attempts = 3, retryDelayMs = 1_000, } = opts;
|
|
361
|
+
const { baseUrl, pickupCode, agentJkt, expectedHandoffId, tokenId, fetchImpl = fetch, sleep = defaultSleep, now = Date.now, attempts = 3, retryDelayMs = 1_000, } = opts;
|
|
362
362
|
// Both producers (runHostedApproval and the panel initiate route) mint the
|
|
363
363
|
// verifier as randomBytes(32).base64url = 43 chars; refuse anything else
|
|
364
364
|
// before it travels.
|
|
@@ -412,7 +412,34 @@ export async function claimMandatePickup(opts) {
|
|
|
412
412
|
if (ctx.budget !== true) {
|
|
413
413
|
throw pickupMismatch('budget', 'the approval is a one-purchase approval, not a spend budget');
|
|
414
414
|
}
|
|
415
|
-
|
|
415
|
+
// A budget names exactly ONE agent, by one of two pins.
|
|
416
|
+
//
|
|
417
|
+
// - `agentJkt` — the panel flow: the owner picked an agent that already
|
|
418
|
+
// existed, so the approval names its key and this runtime compares keys.
|
|
419
|
+
// - `handoffId` — the Add-agent dialog: the budget was minted before any
|
|
420
|
+
// agent existed, so it names the handoff the agent would go on to redeem.
|
|
421
|
+
// This runtime redeemed one moments ago and knows which, so the comparison
|
|
422
|
+
// is just as concrete — it is simply a different identifier.
|
|
423
|
+
//
|
|
424
|
+
// EXACTLY one, enforced before either is compared. Neither pin is an unbound
|
|
425
|
+
// budget, and "no pin" must never be the quiet way past a binding check.
|
|
426
|
+
// BOTH pins is worse: it names two agents, and checking whichever one happens
|
|
427
|
+
// to match would let the weaker claim decide. The signer refuses to mint such
|
|
428
|
+
// a context and the approval store refuses to hold one, so this is the third
|
|
429
|
+
// and last place to say it — the one that runs on material already in hand.
|
|
430
|
+
const hasHandoffPin = typeof ctx.handoffId === 'string';
|
|
431
|
+
const hasAgentPin = typeof ctx.agentJkt === 'string';
|
|
432
|
+
if (hasHandoffPin === hasAgentPin) {
|
|
433
|
+
throw pickupMismatch('agentJkt', hasHandoffPin
|
|
434
|
+
? 'this budget names two different agents at once — it binds to a handoff or to a key, never both'
|
|
435
|
+
: 'this budget names no agent at all, so nothing can prove it belongs to this runtime');
|
|
436
|
+
}
|
|
437
|
+
if (hasHandoffPin) {
|
|
438
|
+
if (!expectedHandoffId || ctx.handoffId !== expectedHandoffId) {
|
|
439
|
+
throw pickupMismatch('handoffId', 'this budget belongs to a different agent handoff than the one this runtime just claimed');
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
else if (ctx.agentJkt !== agentJkt) {
|
|
416
443
|
throw pickupMismatch('agentJkt', 'the owner approved this mandate for a different agent key than this runtime holds');
|
|
417
444
|
}
|
|
418
445
|
if (ctx.tokenId !== tokenId) {
|