warpmetal 0.7.5 → 0.7.6
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/README.md +2 -2
- package/package.json +1 -1
- package/skills/warpmetal/SKILL.md +1 -1
- package/skills/warpmetal/references/cli-reference.md +1 -1
- package/skills/warpmetal/references/payments.md +6 -5
- package/skills/warpmetal/references/safety.md +4 -3
- package/src/cli.js +19 -4
- package/src/payment.js +1 -1
- package/src/state.js +49 -0
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ published wallet CLI with the exact version WarpMetal reports:
|
|
|
34
34
|
npm install --global warpmetal
|
|
35
35
|
warpmetal --help
|
|
36
36
|
|
|
37
|
-
npm install --global @x402api/agent-wallet-cli@0.2.
|
|
37
|
+
npm install --global @x402api/agent-wallet-cli@0.2.7
|
|
38
38
|
x402api help --json
|
|
39
39
|
```
|
|
40
40
|
|
|
@@ -58,7 +58,7 @@ integration.
|
|
|
58
58
|
|
|
59
59
|
The repository also contains a skills-only WarpMetal plugin for the public
|
|
60
60
|
Plugins Directory shared by Codex and ChatGPT. The plugin remains a separate
|
|
61
|
-
artifact from the npm CLI and requires `warpmetal` CLI version 0.7.
|
|
61
|
+
artifact from the npm CLI and requires `warpmetal` CLI version 0.7.6 or newer.
|
|
62
62
|
|
|
63
63
|
To test the repository marketplace after the plugin lands on `main`:
|
|
64
64
|
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@ with ad hoc HTTP commands.
|
|
|
11
11
|
|
|
12
12
|
## Start safely
|
|
13
13
|
|
|
14
|
-
1. Run `warpmetal --version` and require version `0.7.
|
|
14
|
+
1. Run `warpmetal --version` and require version `0.7.6` or newer for this
|
|
15
15
|
plugin. If it is missing or older, explain the compatibility requirement,
|
|
16
16
|
ask before installing or upgrading software, and use only the official npm
|
|
17
17
|
package from `https://www.npmjs.com/package/warpmetal`.
|
|
@@ -69,7 +69,7 @@ retire that attempt and return a new `paymentAttemptId`. The CLI replaces the
|
|
|
69
69
|
saved challenge and stale wallet-attempt metadata; use only the newly returned
|
|
70
70
|
workflow.
|
|
71
71
|
|
|
72
|
-
The current integration targets `@x402api/agent-wallet-cli@0.2.
|
|
72
|
+
The current integration targets `@x402api/agent-wallet-cli@0.2.7`. A compatible
|
|
73
73
|
live term is marked `agentWalletSupported: true` and must use the sponsored
|
|
74
74
|
Base USDC or Solana USDC/USDT launch profile with buyer native fees disabled.
|
|
75
75
|
For the current declaration, x402api pays actual gas from its platform treasury
|
|
@@ -25,7 +25,7 @@ contract.
|
|
|
25
25
|
WarpMetal runs on Node.js 20 or 22. The x402api Agent Wallet currently requires
|
|
26
26
|
Node.js 22. Use the exact published package reported by
|
|
27
27
|
`paymentWorkflow.signerPackage.spec`; the current contract is
|
|
28
|
-
`@x402api/agent-wallet-cli@0.2.
|
|
28
|
+
`@x402api/agent-wallet-cli@0.2.7`. Do not add it as a WarpMetal dependency,
|
|
29
29
|
install executable wallet code from an unpinned repository URL, or substitute
|
|
30
30
|
a similarly named package.
|
|
31
31
|
|
|
@@ -149,7 +149,7 @@ reservation or any buyer-funded, unsupported, or unbound alternative.
|
|
|
149
149
|
|
|
150
150
|
x402api pays actual gas from its platform treasury. The merchant tenant's
|
|
151
151
|
active sponsorship allowance controls admission, but actual gas is not a
|
|
152
|
-
tenant debit. During the coordinated rollout, accept only the exact 0.2.
|
|
152
|
+
tenant debit. During the coordinated rollout, accept only the exact 0.2.7
|
|
153
153
|
platform-treasury declaration or the matched legacy tenant-credit declaration;
|
|
154
154
|
never accept a mixed billing and final-charge policy.
|
|
155
155
|
|
|
@@ -191,12 +191,13 @@ current `termEndsAt` generation are different.
|
|
|
191
191
|
terms and authorize only the new workflow; never reuse an expired envelope or
|
|
192
192
|
artifact.
|
|
193
193
|
- `payment_pending` or `payment_finalizing`: keep the same checkout bytes and
|
|
194
|
-
artifact.
|
|
194
|
+
artifact. Preserve the returned durable `paymentId`; `--wait` records it in
|
|
195
|
+
private WarpMetal state and performs bounded retries with that exact authorization.
|
|
195
196
|
- Timeout or process restart after authorization: use the saved x402api attempt
|
|
196
197
|
ID and artifact. Reconcile with WarpMetal and reuse its submit argv. Never
|
|
197
198
|
authorize again merely because submission is unknown.
|
|
198
|
-
- Signed HTTP 402: the artifact was definitively rejected.
|
|
199
|
-
challenge flow again, inspect the replacement terms, and authorize one new
|
|
199
|
+
- Signed HTTP 402: the artifact was definitively rejected. Preserve its
|
|
200
|
+
`paymentId`, then run the WarpMetal challenge flow again, inspect the replacement terms, and authorize one new
|
|
200
201
|
artifact only when the protocol and current interactive or autonomous
|
|
201
202
|
payment authority allow it.
|
|
202
203
|
- `sponsorship_allowance_unavailable`, `sponsorship_payment_cap_exceeded`,
|
|
@@ -115,9 +115,10 @@ Confirm the plan, hostname, OS, and public key before preparing it.
|
|
|
115
115
|
replaces its saved challenge metadata. Re-evaluate the new terms before
|
|
116
116
|
authorization; never sign or submit the expired challenge.
|
|
117
117
|
- For `payment_pending`, retry the exact checkout body and the exact same
|
|
118
|
-
payment artifact.
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
payment artifact. Preserve the returned `paymentId` as the durable x402api
|
|
119
|
+
reconciliation key. Do not create a replacement payment.
|
|
120
|
+
- A signed HTTP 402 rejects that signature. Preserve its `paymentId`, request a
|
|
121
|
+
new live challenge, and create one replacement authorization after checking the new terms against the current
|
|
121
122
|
interactive or autonomous payment authority.
|
|
122
123
|
- Treat `sponsorship_allowance_unavailable`,
|
|
123
124
|
`sponsorship_payment_cap_exceeded`,
|
package/src/cli.js
CHANGED
|
@@ -379,13 +379,18 @@ async function pollGrant(
|
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
function challengeResult(
|
|
382
|
+
function challengeResult(
|
|
383
|
+
taskId,
|
|
384
|
+
checkoutBody,
|
|
385
|
+
response,
|
|
386
|
+
{ requireChallenge = true } = {},
|
|
387
|
+
) {
|
|
383
388
|
const paymentRequired = response.headers["payment-required"];
|
|
384
389
|
const challengeHandle = response.headers["x-x402api-challenge-handle"];
|
|
385
|
-
if (response.status === 402 && !paymentRequired) {
|
|
390
|
+
if (requireChallenge && response.status === 402 && !paymentRequired) {
|
|
386
391
|
throw new CliError("WarpMetal returned HTTP 402 without PAYMENT-REQUIRED.");
|
|
387
392
|
}
|
|
388
|
-
if (response.status === 402 && !challengeHandle) {
|
|
393
|
+
if (requireChallenge && response.status === 402 && !challengeHandle) {
|
|
389
394
|
throw new CliError(
|
|
390
395
|
"WarpMetal returned HTTP 402 without X-X402API-Challenge-Handle.",
|
|
391
396
|
);
|
|
@@ -736,12 +741,17 @@ async function handleCheckoutSubmit(client, store, options, context) {
|
|
|
736
741
|
token,
|
|
737
742
|
paymentSignature,
|
|
738
743
|
});
|
|
744
|
+
if (response.data?.paymentId) {
|
|
745
|
+
await store.saveOrderPaymentId(taskId, response.data.paymentId);
|
|
746
|
+
}
|
|
739
747
|
const safe = await attachPaymentWorkflow(
|
|
740
748
|
client,
|
|
741
749
|
store,
|
|
742
750
|
taskId,
|
|
743
751
|
order,
|
|
744
|
-
challengeResult(taskId, order.checkoutBody, response
|
|
752
|
+
challengeResult(taskId, order.checkoutBody, response, {
|
|
753
|
+
requireChallenge: false,
|
|
754
|
+
}),
|
|
745
755
|
);
|
|
746
756
|
const retryable =
|
|
747
757
|
response.status === 202 &&
|
|
@@ -755,6 +765,7 @@ async function handleCheckoutSubmit(client, store, options, context) {
|
|
|
755
765
|
const output = {
|
|
756
766
|
...safe,
|
|
757
767
|
task: response.data?.task,
|
|
768
|
+
paymentId: response.data?.paymentId,
|
|
758
769
|
message: response.data?.message,
|
|
759
770
|
...(walletPayment
|
|
760
771
|
? {
|
|
@@ -1327,6 +1338,9 @@ async function handleRenewalSubmit(client, store, options, context) {
|
|
|
1327
1338
|
paymentSignature: walletPayment.paymentSignature,
|
|
1328
1339
|
},
|
|
1329
1340
|
);
|
|
1341
|
+
if (response.data?.paymentId) {
|
|
1342
|
+
await store.saveRenewalPaymentId(serverId, response.data.paymentId);
|
|
1343
|
+
}
|
|
1330
1344
|
const retryable =
|
|
1331
1345
|
response.status === 202 &&
|
|
1332
1346
|
["payment_pending", "payment_finalizing"].includes(response.data?.status);
|
|
@@ -1339,6 +1353,7 @@ async function handleRenewalSubmit(client, store, options, context) {
|
|
|
1339
1353
|
status: response.data?.status,
|
|
1340
1354
|
serverId,
|
|
1341
1355
|
task: response.data?.task,
|
|
1356
|
+
paymentId: response.data?.paymentId,
|
|
1342
1357
|
paymentAttemptId:
|
|
1343
1358
|
response.data?.paymentAttemptId ||
|
|
1344
1359
|
response.headers["x-warpmetal-payment-attempt"],
|
package/src/payment.js
CHANGED
|
@@ -15,7 +15,7 @@ const MAX_ARTIFACT_BYTES = 1024 * 1024;
|
|
|
15
15
|
const MAX_SIGNATURE_BYTES = 512 * 1024;
|
|
16
16
|
|
|
17
17
|
export const AGENT_WALLET_PACKAGE = "@x402api/agent-wallet-cli";
|
|
18
|
-
export const AGENT_WALLET_VERSION = "0.2.
|
|
18
|
+
export const AGENT_WALLET_VERSION = "0.2.7";
|
|
19
19
|
const AGENT_WALLET_SPEC = `${AGENT_WALLET_PACKAGE}@${AGENT_WALLET_VERSION}`;
|
|
20
20
|
const GAS_SPONSORSHIP_EXTENSION = "com.x402api.gas-sponsorship";
|
|
21
21
|
const BASE_NETWORK = "eip155:8453";
|
package/src/state.js
CHANGED
|
@@ -13,7 +13,10 @@ const WALLET_PAYMENT_STATE_FIELDS = [
|
|
|
13
13
|
"walletPayerAddress",
|
|
14
14
|
"paymentArtifactExpiresAt",
|
|
15
15
|
"paymentArtifactSavedAt",
|
|
16
|
+
"gatewayPaymentId",
|
|
16
17
|
];
|
|
18
|
+
const X402API_PAYMENT_ID =
|
|
19
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
17
20
|
|
|
18
21
|
function replaceChallenge(record, challenge) {
|
|
19
22
|
const changed =
|
|
@@ -361,6 +364,50 @@ export class StateStore {
|
|
|
361
364
|
});
|
|
362
365
|
}
|
|
363
366
|
|
|
367
|
+
async saveOrderPaymentId(taskId, paymentId) {
|
|
368
|
+
if (!X402API_PAYMENT_ID.test(paymentId || "")) {
|
|
369
|
+
throw new CliError("WarpMetal returned an invalid x402api payment ID.", {
|
|
370
|
+
exitCode: 3,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
await this.update((state) => {
|
|
374
|
+
const order = state.orders[taskId];
|
|
375
|
+
if (!order) {
|
|
376
|
+
throw new CliError(`No local order state exists for ${taskId}.`, {
|
|
377
|
+
exitCode: 2,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
if (order.gatewayPaymentId && order.gatewayPaymentId !== paymentId) {
|
|
381
|
+
throw new CliError("WarpMetal changed the x402api payment ID for this attempt.", {
|
|
382
|
+
exitCode: 3,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
order.gatewayPaymentId = paymentId;
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async saveRenewalPaymentId(serverId, paymentId) {
|
|
390
|
+
if (!X402API_PAYMENT_ID.test(paymentId || "")) {
|
|
391
|
+
throw new CliError("WarpMetal returned an invalid x402api payment ID.", {
|
|
392
|
+
exitCode: 3,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
await this.update((state) => {
|
|
396
|
+
const renewal = state.renewals[serverId];
|
|
397
|
+
if (!renewal) {
|
|
398
|
+
throw new CliError(`No local renewal state exists for ${serverId}.`, {
|
|
399
|
+
exitCode: 2,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (renewal.gatewayPaymentId && renewal.gatewayPaymentId !== paymentId) {
|
|
403
|
+
throw new CliError("WarpMetal changed the x402api payment ID for this renewal.", {
|
|
404
|
+
exitCode: 3,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
renewal.gatewayPaymentId = paymentId;
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
364
411
|
async saveAccessToken(serverId, accessToken, expiresAt) {
|
|
365
412
|
await this.update((state) => {
|
|
366
413
|
const server = state.servers[serverId] || { serverId };
|
|
@@ -488,6 +535,7 @@ export class StateStore {
|
|
|
488
535
|
serverId: order.serverId,
|
|
489
536
|
planId: order.planId,
|
|
490
537
|
paymentAttemptId: order.paymentAttemptId,
|
|
538
|
+
paymentId: order.gatewayPaymentId,
|
|
491
539
|
challengeHandle: order.challengeHandle,
|
|
492
540
|
walletPaymentAttemptId: order.walletPaymentAttemptId,
|
|
493
541
|
walletName: order.walletName,
|
|
@@ -523,6 +571,7 @@ export class StateStore {
|
|
|
523
571
|
refillTargetAtomic: renewal.refillTargetAtomic,
|
|
524
572
|
policy: renewal.policy,
|
|
525
573
|
paymentAttemptId: renewal.paymentAttemptId,
|
|
574
|
+
paymentId: renewal.gatewayPaymentId,
|
|
526
575
|
challengeHandle: renewal.challengeHandle,
|
|
527
576
|
paymentArtifactExpiresAt: renewal.paymentArtifactExpiresAt,
|
|
528
577
|
})),
|