warpmetal 0.7.2 → 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
@@ -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.2",
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,6 +61,10 @@ 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
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
@@ -171,6 +171,12 @@ current `termEndsAt` generation are different.
171
171
 
172
172
  ## Retries and recovery
173
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.
174
180
  - `payment_pending` or `payment_finalizing`: keep the same checkout bytes and
175
181
  artifact. `--wait` performs bounded retries with that exact authorization.
176
182
  - Timeout or process restart after authorization: use the saved x402api attempt
@@ -188,6 +194,6 @@ current `termEndsAt` generation are different.
188
194
  a fresh challenge; never retry the artifact or fall back to buyer-funded gas.
189
195
  - `manual_review`: payment or fulfillment may be final. Stop all payment and
190
196
  mutation retries.
191
- - Expired or corrupt artifact, changed request digest, unexpected recipient,
197
+ - Expired or corrupt signed artifact, changed request digest, unexpected recipient,
192
198
  unsupported network, asset, profile, sponsorship error, or request-binding
193
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/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
  }