warpmetal 0.7.2 → 0.7.4
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 +29 -22
- package/package.json +1 -1
- package/skills/warpmetal/SKILL.md +13 -9
- package/skills/warpmetal/references/cli-reference.md +10 -3
- package/skills/warpmetal/references/payments.md +27 -8
- package/skills/warpmetal/references/safety.md +10 -1
- package/src/cli.js +26 -13
- package/src/payment.js +144 -3
- package/src/state.js +31 -10
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.5
|
|
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.4 or newer.
|
|
62
62
|
|
|
63
63
|
To test the repository marketplace after the plugin lands on `main`:
|
|
64
64
|
|
|
@@ -116,8 +116,8 @@ that WarpMetal uses for merchant-side reconciliation, the pinned
|
|
|
116
116
|
`paymentWorkflow`. It also writes an owner-only request envelope that contains
|
|
117
117
|
the exact checkout URL and body but no WarpMetal credential or challenge
|
|
118
118
|
handle. The handle is not a buyer payment identifier and is never a wallet
|
|
119
|
-
signing input. The published
|
|
120
|
-
|
|
119
|
+
signing input. The published launch wallet accepts sponsored Base USDC and
|
|
120
|
+
sponsored Solana USDC/USDT only;
|
|
121
121
|
the returned terms identify compatible alternatives and confirm that the buyer
|
|
122
122
|
does not need ETH or SOL. x402api pays the actual network fee from its platform
|
|
123
123
|
treasury; the merchant tenant's active allowance controls sponsorship
|
|
@@ -131,7 +131,31 @@ context:
|
|
|
131
131
|
limits. When the live terms fit those limits, authorize and submit without
|
|
132
132
|
waiting for conversational approval.
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
Follow the returned commands in order. `wallet setup` creates an owner-only,
|
|
135
|
+
managed unlock file inside the x402api home directory and is safe to repeat;
|
|
136
|
+
it never prints the generated passphrase. `X402API_WALLET_PASSWORD_FILE` is an
|
|
137
|
+
optional x402api override for an externally managed password file. It is not a
|
|
138
|
+
WarpMetal variable, and WarpMetal does not create, read, or receive that file.
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
x402api wallet setup --json
|
|
142
|
+
x402api wallet list --json
|
|
143
|
+
x402api wallet create --name <wallet-name> \
|
|
144
|
+
--network <exact-challenge-network> \
|
|
145
|
+
--maximum-payment-atomic <exact-live-amount> --json
|
|
146
|
+
x402api wallet address --wallet <wallet-name> --json
|
|
147
|
+
x402api wallet balance --wallet <wallet-name> \
|
|
148
|
+
--asset <exact-challenge-asset> --json
|
|
149
|
+
x402api wallet funding --wallet <wallet-name> \
|
|
150
|
+
--asset <exact-challenge-asset> \
|
|
151
|
+
--target-balance-atomic <exact-live-amount> --json
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Create a wallet only when no compatible dedicated wallet exists. The funding
|
|
155
|
+
command reports the public payer address, QR payload, current balance, target,
|
|
156
|
+
and exact deficit in atomic and normal six-decimal units. After the selected
|
|
157
|
+
wallet is sufficiently funded, invoke the returned authorization and WarpMetal
|
|
158
|
+
submission argv:
|
|
135
159
|
|
|
136
160
|
```sh
|
|
137
161
|
x402api payment authorize \
|
|
@@ -147,23 +171,6 @@ warpmetal checkout submit \
|
|
|
147
171
|
--json
|
|
148
172
|
```
|
|
149
173
|
|
|
150
|
-
Check the payer wallet address and balance with:
|
|
151
|
-
|
|
152
|
-
```sh
|
|
153
|
-
x402api wallet show --wallet <wallet-name> --json
|
|
154
|
-
x402api wallet address --wallet <wallet-name> --json
|
|
155
|
-
x402api wallet balance --wallet <wallet-name> --json
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
For a new dedicated wallet, configure the enforceable local per-payment
|
|
159
|
-
ceiling when it is created:
|
|
160
|
-
|
|
161
|
-
```sh
|
|
162
|
-
x402api wallet create --name <wallet-name> \
|
|
163
|
-
--network <exact-challenge-network> \
|
|
164
|
-
--maximum-payment-atomic <per-payment-policy-cap> --json
|
|
165
|
-
```
|
|
166
|
-
|
|
167
174
|
Require `maximumPaymentAtomic` to cover the live charge without exceeding the
|
|
168
175
|
task or operator limit. Among valid sponsored terms, honor an explicit network
|
|
169
176
|
or asset preference, otherwise prefer an already funded compatible wallet,
|
package/package.json
CHANGED
|
@@ -11,20 +11,20 @@ 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.4` 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`.
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
2. Use `--json` for every agent-driven command.
|
|
19
|
+
3. Read [references/safety.md](references/safety.md) before preparing an order,
|
|
20
20
|
authorizing payment, using an SSH identity, or changing a server.
|
|
21
|
-
|
|
21
|
+
4. Read [references/cli-reference.md](references/cli-reference.md) when choosing
|
|
22
22
|
a command or interpreting an exit code.
|
|
23
|
-
|
|
23
|
+
5. Read [references/payments.md](references/payments.md) before creating or
|
|
24
24
|
funding a wallet, authorizing payment, or resolving an ambiguous attempt.
|
|
25
|
-
|
|
25
|
+
6. Read [references/runtime.md](references/runtime.md) before requesting,
|
|
26
26
|
installing, accessing, expiring, or deleting Agent Runtime sandboxes.
|
|
27
|
-
|
|
27
|
+
7. Read [references/renewals.md](references/renewals.md) before configuring or
|
|
28
28
|
executing an autonomous renewal or requesting a wallet refill.
|
|
29
29
|
|
|
30
30
|
Never read, print, summarize, upload, or commit the WarpMetal state file,
|
|
@@ -99,8 +99,12 @@ when the exact live terms fit those limits.
|
|
|
99
99
|
|
|
100
100
|
Follow [references/payments.md](references/payments.md) to install the exact
|
|
101
101
|
`paymentWorkflow.signerPackage.spec`, verify the V1 machine contract, install
|
|
102
|
-
its matching `x402api-pay` skill, and
|
|
103
|
-
network-specific wallet
|
|
102
|
+
its matching `x402api-pay` skill, and invoke the returned wallet sequence:
|
|
103
|
+
`wallet setup`, `wallet list`, network-specific `wallet create` only if needed,
|
|
104
|
+
then the exact address, asset-balance, and funding commands. `wallet setup`
|
|
105
|
+
owns the managed unlock file; `X402API_WALLET_PASSWORD_FILE` is only an
|
|
106
|
+
optional x402api override and is never a WarpMetal input. Require
|
|
107
|
+
`agentWalletSupported: true`,
|
|
104
108
|
`sponsoredNetworkFee: true`, and `buyerNativeFeeRequired: false` on the chosen
|
|
105
109
|
live term. Never fall back to a historical buyer-funded or TRON profile.
|
|
106
110
|
Honor an explicit task or operator network and asset preference. Otherwise use
|
|
@@ -56,13 +56,20 @@ On HTTP 402, `checkout challenge` validates and displays exact payment terms,
|
|
|
56
56
|
writes the opaque x402api `challengeHandle` to private WarpMetal state for
|
|
57
57
|
merchant reconciliation, and writes a credential-free x402api V1 request envelope with owner-only
|
|
58
58
|
permissions, and returns the exact pinned wallet package, V1 contract probe,
|
|
59
|
-
matching wallet-skill install,
|
|
60
|
-
|
|
59
|
+
matching wallet-skill install, setup, list, network-specific create,
|
|
60
|
+
address/balance/funding, authorization, and WarpMetal submission argv arrays.
|
|
61
|
+
The returned top-level `paymentWorkflow.sequence` fixes their order. Funding
|
|
62
|
+
options remain paired by exact live network and asset. The default envelope and
|
|
63
|
+
suggested artifact paths live under the
|
|
61
64
|
private WarpMetal state directory. An explicit output path must not already
|
|
62
65
|
contain different content. `challengeHandle` is intentionally absent from the
|
|
63
66
|
wallet envelope: it is neither a buyer payment identifier nor signing input.
|
|
67
|
+
If a previously issued unsigned sponsorship reservation expired, WarpMetal may
|
|
68
|
+
retire that attempt and return a new `paymentAttemptId`. The CLI replaces the
|
|
69
|
+
saved challenge and stale wallet-attempt metadata; use only the newly returned
|
|
70
|
+
workflow.
|
|
64
71
|
|
|
65
|
-
The current integration targets `@x402api/agent-wallet-cli@0.2.
|
|
72
|
+
The current integration targets `@x402api/agent-wallet-cli@0.2.5`. A compatible
|
|
66
73
|
live term is marked `agentWalletSupported: true` and must use the sponsored
|
|
67
74
|
Base USDC or Solana USDC/USDT launch profile with buyer native fees disabled.
|
|
68
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.5`. 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
|
|
|
@@ -40,9 +40,15 @@ skill automatically.
|
|
|
40
40
|
1. Install the exact `paymentWorkflow.signerPackage.install.argv` under Node
|
|
41
41
|
22. Run `command -v x402api` and the returned
|
|
42
42
|
`paymentWorkflow.signerContract.probe.argv`. Require contract version 1.
|
|
43
|
-
2.
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
2. Run the exact `paymentWorkflow.walletWorkflow.setup.argv`. The idempotent
|
|
44
|
+
`x402api wallet setup --json` command creates an owner-only managed unlock
|
|
45
|
+
file inside the x402api home directory and never prints its generated
|
|
46
|
+
passphrase. `X402API_WALLET_PASSWORD_FILE` is an optional x402api override
|
|
47
|
+
for an externally managed password file; it is not a WarpMetal setting.
|
|
48
|
+
Never create it on WarpMetal's behalf, read it, or pass it to WarpMetal. A
|
|
49
|
+
supervised operator may instead use `--password-stdin` where supported.
|
|
50
|
+
3. Run the exact `paymentWorkflow.walletWorkflow.list.argv` and reuse a
|
|
51
|
+
dedicated persistent wallet for
|
|
46
52
|
the exact challenge network.
|
|
47
53
|
4. In an interactive conversation, create a wallet only with explicit
|
|
48
54
|
approval. In an unattended run, create one only when the automation policy
|
|
@@ -63,13 +69,20 @@ skill automatically.
|
|
|
63
69
|
higher than the task or operator limit. If an existing wallet has no ceiling
|
|
64
70
|
or an unsuitable one, do not treat its full balance as bounded authority;
|
|
65
71
|
use a suitably capped dedicated wallet or stop.
|
|
66
|
-
5. Use
|
|
72
|
+
5. Use the returned public address, exact asset-balance, and funding commands:
|
|
67
73
|
|
|
68
74
|
```sh
|
|
69
75
|
x402api wallet address --wallet <name> --json
|
|
70
|
-
x402api wallet balance --wallet <name>
|
|
76
|
+
x402api wallet balance --wallet <name> \
|
|
77
|
+
--asset <exact-challenge-asset> --json
|
|
78
|
+
x402api wallet funding --wallet <name> \
|
|
79
|
+
--asset <exact-challenge-asset> \
|
|
80
|
+
--target-balance-atomic <required-or-policy-target> --json
|
|
71
81
|
```
|
|
72
82
|
|
|
83
|
+
Use only the selected term's returned argv; do not combine one network with
|
|
84
|
+
another term's asset. The funding result provides the current balance,
|
|
85
|
+
target, exact deficit, payer address, and QR payload without moving funds.
|
|
73
86
|
6. If funding is short in an interactive conversation, show the exact deficit
|
|
74
87
|
in normal token units and atomic units, the exact network, token symbol and
|
|
75
88
|
contract/mint, and the payer wallet's public receiving address. Render only
|
|
@@ -136,7 +149,7 @@ reservation or any buyer-funded, unsupported, or unbound alternative.
|
|
|
136
149
|
|
|
137
150
|
x402api pays actual gas from its platform treasury. The merchant tenant's
|
|
138
151
|
active sponsorship allowance controls admission, but actual gas is not a
|
|
139
|
-
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.5
|
|
140
153
|
platform-treasury declaration or the matched legacy tenant-credit declaration;
|
|
141
154
|
never accept a mixed billing and final-charge policy.
|
|
142
155
|
|
|
@@ -171,6 +184,12 @@ current `termEndsAt` generation are different.
|
|
|
171
184
|
|
|
172
185
|
## Retries and recovery
|
|
173
186
|
|
|
187
|
+
- Expired unsigned gas sponsorship: run the WarpMetal challenge flow again.
|
|
188
|
+
WarpMetal retires the expired merchant attempt and requests fresh instructions
|
|
189
|
+
under a new payment-attempt ID and idempotency key. The CLI replaces stale
|
|
190
|
+
wallet-attempt metadata when it saves the new challenge. Inspect the new
|
|
191
|
+
terms and authorize only the new workflow; never reuse an expired envelope or
|
|
192
|
+
artifact.
|
|
174
193
|
- `payment_pending` or `payment_finalizing`: keep the same checkout bytes and
|
|
175
194
|
artifact. `--wait` performs bounded retries with that exact authorization.
|
|
176
195
|
- Timeout or process restart after authorization: use the saved x402api attempt
|
|
@@ -188,6 +207,6 @@ current `termEndsAt` generation are different.
|
|
|
188
207
|
a fresh challenge; never retry the artifact or fall back to buyer-funded gas.
|
|
189
208
|
- `manual_review`: payment or fulfillment may be final. Stop all payment and
|
|
190
209
|
mutation retries.
|
|
191
|
-
- Expired or corrupt artifact, changed request digest, unexpected recipient,
|
|
210
|
+
- Expired or corrupt signed artifact, changed request digest, unexpected recipient,
|
|
192
211
|
unsupported network, asset, profile, sponsorship error, or request-binding
|
|
193
212
|
mismatch: stop instead of falling back or asking the buyer to fund ETH/SOL.
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
- Install only the exact `@x402api/agent-wallet-cli` version and matching
|
|
47
47
|
bundled `x402api-pay` skill reported by WarpMetal. Never substitute an
|
|
48
48
|
unpinned repository checkout or similarly named wallet package.
|
|
49
|
+
- Let `x402api wallet setup` create and permission its managed unlock file.
|
|
50
|
+
`X402API_WALLET_PASSWORD_FILE` belongs to x402api only as an optional
|
|
51
|
+
externally managed override; WarpMetal must never create, read, or receive
|
|
52
|
+
it. Stop if x402api rejects unsafe ownership or permissions.
|
|
49
53
|
- Never read or display an x402api password file, encrypted keystore, request
|
|
50
54
|
envelope, payment artifact, or complete payment signature. Pass only their
|
|
51
55
|
paths to the documented commands.
|
|
@@ -61,7 +65,7 @@ explicit approval immediately before:
|
|
|
61
65
|
- installing the CLI or skill;
|
|
62
66
|
- installing or repairing the Agent Runtime supervisor;
|
|
63
67
|
- generating a new SSH key pair;
|
|
64
|
-
- creating or
|
|
68
|
+
- creating a wallet or asking the human to transfer funds to it;
|
|
65
69
|
- signing or submitting an x402 payment authorization;
|
|
66
70
|
- booting, rebooting, or shutting down a server; and
|
|
67
71
|
- creating a temporary sandbox after explaining its irreversible expiry;
|
|
@@ -105,6 +109,11 @@ Confirm the plan, hostname, OS, and public key before preparing it.
|
|
|
105
109
|
## Retry and terminal-state rules
|
|
106
110
|
|
|
107
111
|
- Reuse the same idempotency key only for the exact same logical request.
|
|
112
|
+
- If an unsigned gas-sponsorship reservation expires, run `warpmetal checkout
|
|
113
|
+
challenge` again. WarpMetal retires the old merchant attempt, requests fresh
|
|
114
|
+
instructions with a new payment-attempt ID and idempotency key, and the CLI
|
|
115
|
+
replaces its saved challenge metadata. Re-evaluate the new terms before
|
|
116
|
+
authorization; never sign or submit the expired challenge.
|
|
108
117
|
- For `payment_pending`, retry the exact checkout body and the exact same
|
|
109
118
|
payment artifact. Do not create a replacement payment.
|
|
110
119
|
- A signed HTTP 402 rejects that signature. Use the new live challenge for one
|
package/src/cli.js
CHANGED
|
@@ -147,6 +147,22 @@ function shellCommand(argv) {
|
|
|
147
147
|
.join(" ");
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
function walletWorkflowInstructions(workflow) {
|
|
151
|
+
const create = workflow.walletWorkflow.createOptions
|
|
152
|
+
.map(
|
|
153
|
+
(option) =>
|
|
154
|
+
`${option.network} ${option.asset}: ${shellCommand(option.argv)}`,
|
|
155
|
+
)
|
|
156
|
+
.join("\n");
|
|
157
|
+
const balanceAndFunding = workflow.fundingWorkflow.options
|
|
158
|
+
.map(
|
|
159
|
+
(option) =>
|
|
160
|
+
`${option.network} ${option.asset}:\n Balance: ${shellCommand(option.balance.argv)}\n Funding: ${shellCommand(option.funding.argv)}`,
|
|
161
|
+
)
|
|
162
|
+
.join("\n");
|
|
163
|
+
return `Wallet setup: ${shellCommand(workflow.walletWorkflow.setup.argv)}\nWallet list: ${shellCommand(workflow.walletWorkflow.list.argv)}\nCreate a compatible wallet only if needed:\n${create}\nCheck the selected wallet address: ${shellCommand(workflow.fundingWorkflow.address.argv)}\nCheck or fund the exact selected asset:\n${balanceAndFunding}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
150
166
|
function emit(stream, value, json, human) {
|
|
151
167
|
if (json) writeLine(stream, JSON.stringify(value, null, 2));
|
|
152
168
|
else writeLine(stream, human);
|
|
@@ -413,6 +429,7 @@ async function attachPaymentWorkflow(
|
|
|
413
429
|
);
|
|
414
430
|
const workflow = paymentWorkflow({
|
|
415
431
|
taskId,
|
|
432
|
+
terms: request.terms,
|
|
416
433
|
requestEnvelopePath,
|
|
417
434
|
paymentArtifactPath: defaults.paymentArtifactPath,
|
|
418
435
|
});
|
|
@@ -653,7 +670,7 @@ async function handleCheckoutChallenge(client, store, options, context) {
|
|
|
653
670
|
stringOption(options, "request-envelope-out"),
|
|
654
671
|
);
|
|
655
672
|
const paymentInstructions = safe.paymentWorkflow
|
|
656
|
-
? `\nWallet package: ${safe.paymentWorkflow.signerPackage.spec} (Node ${safe.paymentWorkflow.signerNodeRequirement})\nInstall: ${shellCommand(safe.paymentWorkflow.signerPackage.install.argv)}\nVerify: ${shellCommand(safe.paymentWorkflow.signerContract.probe.argv)}\
|
|
673
|
+
? `\nWallet package: ${safe.paymentWorkflow.signerPackage.spec} (Node ${safe.paymentWorkflow.signerNodeRequirement})\nInstall: ${shellCommand(safe.paymentWorkflow.signerPackage.install.argv)}\nVerify: ${shellCommand(safe.paymentWorkflow.signerContract.probe.argv)}\n${walletWorkflowInstructions(safe.paymentWorkflow)}\nRequest envelope: ${safe.paymentWorkflow.requestEnvelopePath}\nAuthorize only after selecting/funding one wallet: ${shellCommand(safe.paymentWorkflow.authorize.argv)}\nSubmit with WarpMetal: ${shellCommand(safe.paymentWorkflow.submit.argv)}`
|
|
657
674
|
: "";
|
|
658
675
|
emit(
|
|
659
676
|
context.stdout,
|
|
@@ -1040,25 +1057,21 @@ async function attachRenewalPaymentWorkflow(
|
|
|
1040
1057
|
requestedEnvelopePath || defaults.requestEnvelopePath,
|
|
1041
1058
|
request.envelope,
|
|
1042
1059
|
);
|
|
1060
|
+
const refillTarget =
|
|
1061
|
+
renewal.refillTargetAtomic &&
|
|
1062
|
+
BigInt(renewal.refillTargetAtomic) >= BigInt(term.amountAtomic)
|
|
1063
|
+
? renewal.refillTargetAtomic
|
|
1064
|
+
: term.amountAtomic;
|
|
1043
1065
|
const workflow = paymentWorkflow({
|
|
1044
1066
|
taskId: server.id,
|
|
1045
1067
|
serverId,
|
|
1046
1068
|
kind: "renewal",
|
|
1047
1069
|
wallet: renewal.wallet,
|
|
1070
|
+
terms: [term],
|
|
1071
|
+
fundingTargetAtomic: refillTarget,
|
|
1048
1072
|
requestEnvelopePath,
|
|
1049
1073
|
paymentArtifactPath: defaults.paymentArtifactPath,
|
|
1050
1074
|
});
|
|
1051
|
-
const refillTarget =
|
|
1052
|
-
renewal.refillTargetAtomic &&
|
|
1053
|
-
BigInt(renewal.refillTargetAtomic) >= BigInt(term.amountAtomic)
|
|
1054
|
-
? renewal.refillTargetAtomic
|
|
1055
|
-
: term.amountAtomic;
|
|
1056
|
-
Object.assign(workflow.fundingWorkflow, {
|
|
1057
|
-
network: term.network,
|
|
1058
|
-
asset: term.asset,
|
|
1059
|
-
requiredPaymentAtomic: term.amountAtomic,
|
|
1060
|
-
targetBalanceAtomic: refillTarget,
|
|
1061
|
-
});
|
|
1062
1075
|
const refillNotification = refillNotificationState(
|
|
1063
1076
|
notifications,
|
|
1064
1077
|
renewal.policy,
|
|
@@ -1191,7 +1204,7 @@ async function handleRenewalPrepare(client, store, options, context) {
|
|
|
1191
1204
|
const serverId = stringOption(options, "server", { required: true });
|
|
1192
1205
|
const safe = await prepareRenewal(client, store, serverId, options, context);
|
|
1193
1206
|
const fundingInstructions = safe.paymentWorkflow
|
|
1194
|
-
? `\
|
|
1207
|
+
? `\n${walletWorkflowInstructions(safe.paymentWorkflow)}. Show the returned payer address as both a QR code and copyable text, with the exact network, asset, and deficit.`
|
|
1195
1208
|
: "";
|
|
1196
1209
|
const refillInstructions = safe.refillWorkflow
|
|
1197
1210
|
? `\nFor verified email refill, set ${Object.entries(safe.refillWorkflow.environment).map(([key, value]) => `${key}=${value}`).join(" ")} and run: ${shellCommand(safe.refillWorkflow.argv)}`
|
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.5";
|
|
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";
|
|
@@ -707,10 +707,75 @@ export function paymentWorkflow({
|
|
|
707
707
|
serverId,
|
|
708
708
|
kind = "checkout",
|
|
709
709
|
wallet = "<wallet-name>",
|
|
710
|
+
terms = [],
|
|
711
|
+
fundingTargetAtomic,
|
|
710
712
|
requestEnvelopePath,
|
|
711
713
|
paymentArtifactPath,
|
|
712
714
|
}) {
|
|
715
|
+
const walletOptions = terms
|
|
716
|
+
.filter((term) => term?.agentWalletSupported)
|
|
717
|
+
.map((term) => {
|
|
718
|
+
const targetBalanceAtomic = fundingTargetAtomic || term.amountAtomic;
|
|
719
|
+
return {
|
|
720
|
+
network: term.network,
|
|
721
|
+
asset: term.asset,
|
|
722
|
+
requiredPaymentAtomic: term.amountAtomic,
|
|
723
|
+
targetBalanceAtomic,
|
|
724
|
+
create: {
|
|
725
|
+
argv: [
|
|
726
|
+
"x402api",
|
|
727
|
+
"wallet",
|
|
728
|
+
"create",
|
|
729
|
+
"--name",
|
|
730
|
+
"<wallet-name>",
|
|
731
|
+
"--network",
|
|
732
|
+
term.network,
|
|
733
|
+
"--maximum-payment-atomic",
|
|
734
|
+
term.amountAtomic,
|
|
735
|
+
"--json",
|
|
736
|
+
],
|
|
737
|
+
},
|
|
738
|
+
address: {
|
|
739
|
+
argv: ["x402api", "wallet", "address", "--wallet", wallet, "--json"],
|
|
740
|
+
},
|
|
741
|
+
balance: {
|
|
742
|
+
argv: [
|
|
743
|
+
"x402api",
|
|
744
|
+
"wallet",
|
|
745
|
+
"balance",
|
|
746
|
+
"--wallet",
|
|
747
|
+
wallet,
|
|
748
|
+
"--asset",
|
|
749
|
+
term.asset,
|
|
750
|
+
"--json",
|
|
751
|
+
],
|
|
752
|
+
},
|
|
753
|
+
funding: {
|
|
754
|
+
argv: [
|
|
755
|
+
"x402api",
|
|
756
|
+
"wallet",
|
|
757
|
+
"funding",
|
|
758
|
+
"--wallet",
|
|
759
|
+
wallet,
|
|
760
|
+
"--asset",
|
|
761
|
+
term.asset,
|
|
762
|
+
"--target-balance-atomic",
|
|
763
|
+
targetBalanceAtomic,
|
|
764
|
+
"--json",
|
|
765
|
+
],
|
|
766
|
+
},
|
|
767
|
+
};
|
|
768
|
+
});
|
|
769
|
+
const selectedFunding = walletOptions.length === 1 ? walletOptions[0] : null;
|
|
713
770
|
return {
|
|
771
|
+
sequence: [
|
|
772
|
+
"wallet_setup",
|
|
773
|
+
"wallet_list",
|
|
774
|
+
"wallet_create_if_needed",
|
|
775
|
+
"wallet_address_balance_funding",
|
|
776
|
+
"payment_authorize",
|
|
777
|
+
"warpmetal_submit",
|
|
778
|
+
],
|
|
714
779
|
signer: "x402api Agent Wallet",
|
|
715
780
|
signerExecutable: "x402api",
|
|
716
781
|
signerNodeRequirement: ">=22",
|
|
@@ -742,6 +807,28 @@ export function paymentWorkflow({
|
|
|
742
807
|
],
|
|
743
808
|
},
|
|
744
809
|
},
|
|
810
|
+
walletWorkflow: {
|
|
811
|
+
sequence: [
|
|
812
|
+
"setup",
|
|
813
|
+
"list",
|
|
814
|
+
"create_if_needed",
|
|
815
|
+
"address",
|
|
816
|
+
"balance",
|
|
817
|
+
"fund_if_needed",
|
|
818
|
+
],
|
|
819
|
+
setup: { argv: ["x402api", "wallet", "setup", "--json"] },
|
|
820
|
+
list: { argv: ["x402api", "wallet", "list", "--json"] },
|
|
821
|
+
createOptions: walletOptions.map(
|
|
822
|
+
({ network, asset, requiredPaymentAtomic, create }) => ({
|
|
823
|
+
network,
|
|
824
|
+
asset,
|
|
825
|
+
requiredPaymentAtomic,
|
|
826
|
+
...create,
|
|
827
|
+
}),
|
|
828
|
+
),
|
|
829
|
+
selection:
|
|
830
|
+
"Reuse a compatible capped wallet when available. Otherwise choose exactly one advertised option and replace only <wallet-name>.",
|
|
831
|
+
},
|
|
745
832
|
requestEnvelopePath,
|
|
746
833
|
paymentArtifactPath,
|
|
747
834
|
authorize: {
|
|
@@ -766,8 +853,62 @@ export function paymentWorkflow({
|
|
|
766
853
|
argv: ["x402api", "wallet", "address", "--wallet", wallet, "--json"],
|
|
767
854
|
},
|
|
768
855
|
balance: {
|
|
769
|
-
argv:
|
|
856
|
+
argv:
|
|
857
|
+
selectedFunding?.balance.argv ||
|
|
858
|
+
[
|
|
859
|
+
"x402api",
|
|
860
|
+
"wallet",
|
|
861
|
+
"balance",
|
|
862
|
+
"--wallet",
|
|
863
|
+
wallet,
|
|
864
|
+
"--asset",
|
|
865
|
+
"<asset-from-selected-term>",
|
|
866
|
+
"--json",
|
|
867
|
+
],
|
|
770
868
|
},
|
|
869
|
+
funding: {
|
|
870
|
+
argv:
|
|
871
|
+
selectedFunding?.funding.argv ||
|
|
872
|
+
[
|
|
873
|
+
"x402api",
|
|
874
|
+
"wallet",
|
|
875
|
+
"funding",
|
|
876
|
+
"--wallet",
|
|
877
|
+
wallet,
|
|
878
|
+
"--asset",
|
|
879
|
+
"<asset-from-selected-term>",
|
|
880
|
+
"--target-balance-atomic",
|
|
881
|
+
"<target-balance-atomic>",
|
|
882
|
+
"--json",
|
|
883
|
+
],
|
|
884
|
+
},
|
|
885
|
+
options: walletOptions.map(
|
|
886
|
+
({
|
|
887
|
+
network,
|
|
888
|
+
asset,
|
|
889
|
+
requiredPaymentAtomic,
|
|
890
|
+
targetBalanceAtomic,
|
|
891
|
+
address,
|
|
892
|
+
balance,
|
|
893
|
+
funding,
|
|
894
|
+
}) => ({
|
|
895
|
+
network,
|
|
896
|
+
asset,
|
|
897
|
+
requiredPaymentAtomic,
|
|
898
|
+
targetBalanceAtomic,
|
|
899
|
+
address,
|
|
900
|
+
balance,
|
|
901
|
+
funding,
|
|
902
|
+
}),
|
|
903
|
+
),
|
|
904
|
+
...(selectedFunding
|
|
905
|
+
? {
|
|
906
|
+
network: selectedFunding.network,
|
|
907
|
+
asset: selectedFunding.asset,
|
|
908
|
+
requiredPaymentAtomic: selectedFunding.requiredPaymentAtomic,
|
|
909
|
+
targetBalanceAtomic: selectedFunding.targetBalanceAtomic,
|
|
910
|
+
}
|
|
911
|
+
: {}),
|
|
771
912
|
presentation: {
|
|
772
913
|
qrPayloadField: "address",
|
|
773
914
|
textPayloadField: "address",
|
|
@@ -777,7 +918,7 @@ export function paymentWorkflow({
|
|
|
777
918
|
showNetworkAndAsset: true,
|
|
778
919
|
},
|
|
779
920
|
safety:
|
|
780
|
-
"Encode and fund only
|
|
921
|
+
"Run the exact selected funding argv. Encode and fund only its payer wallet public address. Never fund the token contract, mint, or WarpMetal merchant recipient; sponsored payments do not require ETH or SOL.",
|
|
781
922
|
},
|
|
782
923
|
submit: {
|
|
783
924
|
argv:
|
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
|
-
|
|
239
|
-
|
|
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
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
}
|