warpmetal 0.7.1 → 0.7.3

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 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.3
37
+ npm install --global @x402api/agent-wallet-cli@0.2.4
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.1 or newer.
61
+ artifact from the npm CLI and requires `warpmetal` CLI version 0.7.3 or newer.
62
62
 
63
63
  To test the repository marketplace after the plugin lands on `main`:
64
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "warpmetal",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Agent-safe CLI and skill for purchasing, renewing, and managing WarpMetal VPS servers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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.1` or newer for this
14
+ 1. Run `warpmetal --version` and require version `0.7.3` 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`.
@@ -61,8 +61,12 @@ arrays. The default envelope and suggested artifact paths live under the
61
61
  private WarpMetal state directory. An explicit output path must not already
62
62
  contain different content. `challengeHandle` is intentionally absent from the
63
63
  wallet envelope: it is neither a buyer payment identifier nor signing input.
64
+ If a previously issued unsigned sponsorship reservation expired, WarpMetal may
65
+ retire that attempt and return a new `paymentAttemptId`. The CLI replaces the
66
+ saved challenge and stale wallet-attempt metadata; use only the newly returned
67
+ workflow.
64
68
 
65
- The current integration targets `@x402api/agent-wallet-cli@0.2.3`. A compatible
69
+ The current integration targets `@x402api/agent-wallet-cli@0.2.4`. A compatible
66
70
  live term is marked `agentWalletSupported: true` and must use the sponsored
67
71
  Base USDC or Solana USDC/USDT launch profile with buyer native fees disabled.
68
72
  For the current declaration, x402api pays actual gas from its platform treasury
@@ -16,10 +16,16 @@ The separate x402api Agent Wallet owns encrypted network keys, balance checks,
16
16
  payment authorization, and durable payment attempts. Never merge their state
17
17
  files or pass a WarpMetal credential to `x402api`.
18
18
 
19
+ The CLI includes the matching `@x402api/agent-wallet-core` SDK. That SDK is the
20
+ authoritative buyer-side parser and signer contract. WarpMetal retains its own
21
+ merchant-side validation because the backend is Python and additionally binds
22
+ the challenge to WarpMetal's exact resource, amount, recipient, and receipt
23
+ contract.
24
+
19
25
  WarpMetal runs on Node.js 20 or 22. The x402api Agent Wallet currently requires
20
26
  Node.js 22. Use the exact published package reported by
21
27
  `paymentWorkflow.signerPackage.spec`; the current contract is
22
- `@x402api/agent-wallet-cli@0.2.3`. Do not add it as a WarpMetal dependency,
28
+ `@x402api/agent-wallet-cli@0.2.4`. Do not add it as a WarpMetal dependency,
23
29
  install executable wallet code from an unpinned repository URL, or substitute
24
30
  a similarly named package.
25
31
 
@@ -130,7 +136,7 @@ reservation or any buyer-funded, unsupported, or unbound alternative.
130
136
 
131
137
  x402api pays actual gas from its platform treasury. The merchant tenant's
132
138
  active sponsorship allowance controls admission, but actual gas is not a
133
- tenant debit. During the coordinated rollout, accept only the exact 0.2.3
139
+ tenant debit. During the coordinated rollout, accept only the exact 0.2.4
134
140
  platform-treasury declaration or the matched legacy tenant-credit declaration;
135
141
  never accept a mixed billing and final-charge policy.
136
142
 
@@ -165,6 +171,12 @@ current `termEndsAt` generation are different.
165
171
 
166
172
  ## Retries and recovery
167
173
 
174
+ - Expired unsigned gas sponsorship: run the WarpMetal challenge flow again.
175
+ WarpMetal retires the expired merchant attempt and requests fresh instructions
176
+ under a new payment-attempt ID and idempotency key. The CLI replaces stale
177
+ wallet-attempt metadata when it saves the new challenge. Inspect the new
178
+ terms and authorize only the new workflow; never reuse an expired envelope or
179
+ artifact.
168
180
  - `payment_pending` or `payment_finalizing`: keep the same checkout bytes and
169
181
  artifact. `--wait` performs bounded retries with that exact authorization.
170
182
  - Timeout or process restart after authorization: use the saved x402api attempt
@@ -182,6 +194,6 @@ current `termEndsAt` generation are different.
182
194
  a fresh challenge; never retry the artifact or fall back to buyer-funded gas.
183
195
  - `manual_review`: payment or fulfillment may be final. Stop all payment and
184
196
  mutation retries.
185
- - Expired or corrupt artifact, changed request digest, unexpected recipient,
197
+ - Expired or corrupt signed artifact, changed request digest, unexpected recipient,
186
198
  unsupported network, asset, profile, sponsorship error, or request-binding
187
199
  mismatch: stop instead of falling back or asking the buyer to fund ETH/SOL.
@@ -105,6 +105,11 @@ Confirm the plan, hostname, OS, and public key before preparing it.
105
105
  ## Retry and terminal-state rules
106
106
 
107
107
  - Reuse the same idempotency key only for the exact same logical request.
108
+ - If an unsigned gas-sponsorship reservation expires, run `warpmetal checkout
109
+ challenge` again. WarpMetal retires the old merchant attempt, requests fresh
110
+ instructions with a new payment-attempt ID and idempotency key, and the CLI
111
+ replaces its saved challenge metadata. Re-evaluate the new terms before
112
+ authorization; never sign or submit the expired challenge.
108
113
  - For `payment_pending`, retry the exact checkout body and the exact same
109
114
  payment artifact. Do not create a replacement payment.
110
115
  - A signed HTTP 402 rejects that signature. Use the new live challenge for one
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.3";
18
+ export const AGENT_WALLET_VERSION = "0.2.4";
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";
@@ -183,6 +183,9 @@ function paymentResource(value, label) {
183
183
  function validateGasSponsorship(declaration) {
184
184
  const info = declaration.info;
185
185
  const schema = declaration.schema;
186
+ // Mirror @x402api/agent-wallet-core: platform treasury is canonical, the
187
+ // matched tenant-credit pair remains rollout-compatible, and mixed pairs
188
+ // must fail closed.
186
189
  const platformTreasuryPolicy =
187
190
  isObject(info) &&
188
191
  info.billingParty === "platform_treasury" &&
package/src/state.js CHANGED
@@ -6,6 +6,26 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
6
6
  import { CliError } from "./errors.js";
7
7
 
8
8
  const STATE_VERSION = 3;
9
+ const WALLET_PAYMENT_STATE_FIELDS = [
10
+ "walletPaymentAttemptId",
11
+ "walletBuyerPaymentIdentifier",
12
+ "walletName",
13
+ "walletPayerAddress",
14
+ "paymentArtifactExpiresAt",
15
+ "paymentArtifactSavedAt",
16
+ ];
17
+
18
+ function replaceChallenge(record, challenge) {
19
+ const changed =
20
+ (record.paymentAttemptId &&
21
+ record.paymentAttemptId !== challenge.paymentAttemptId) ||
22
+ (record.paymentChallengeDigest &&
23
+ record.paymentChallengeDigest !== challenge.paymentChallengeDigest);
24
+ if (changed) {
25
+ for (const name of WALLET_PAYMENT_STATE_FIELDS) delete record[name];
26
+ }
27
+ Object.assign(record, challenge);
28
+ }
9
29
 
10
30
  function emptyState() {
11
31
  return {
@@ -235,9 +255,8 @@ export class StateStore {
235
255
  async saveRenewalChallenge(serverId, challenge) {
236
256
  await this.update((state) => {
237
257
  const renewal = state.renewals[serverId] || { serverId };
238
- Object.assign(renewal, challenge, {
239
- challengeSavedAt: new Date().toISOString(),
240
- });
258
+ replaceChallenge(renewal, challenge);
259
+ renewal.challengeSavedAt = new Date().toISOString();
241
260
  state.renewals[serverId] = renewal;
242
261
  });
243
262
  }
@@ -292,13 +311,15 @@ export class StateStore {
292
311
  throw new CliError(`No local order state exists for ${taskId}.`, {
293
312
  exitCode: 2,
294
313
  });
295
- order.paymentRequired = paymentRequired;
296
- order.paymentAttemptId = paymentAttemptId;
297
- order.challengeHandle = challengeHandle;
298
- order.paymentRequestDigest = paymentRequestDigest;
299
- order.paymentChallengeDigest = paymentChallengeDigest;
300
- order.paymentRequestEnvelopePath = paymentWorkflow?.requestEnvelopePath;
301
- order.paymentArtifactPath = paymentWorkflow?.paymentArtifactPath;
314
+ replaceChallenge(order, {
315
+ paymentRequired,
316
+ paymentAttemptId,
317
+ challengeHandle,
318
+ paymentRequestDigest,
319
+ paymentChallengeDigest,
320
+ paymentRequestEnvelopePath: paymentWorkflow?.requestEnvelopePath,
321
+ paymentArtifactPath: paymentWorkflow?.paymentArtifactPath,
322
+ });
302
323
  order.paymentChallengeSavedAt = new Date().toISOString();
303
324
  });
304
325
  }