@metalntwx/metal-agent 0.1.1 → 0.3.0

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
@@ -1,8 +1,8 @@
1
1
  # @metalntwx/metal-agent
2
2
 
3
3
  The `metal-agent` CLI an agent pays x402 payees on Metal with: create or
4
- import a key, fund it, pay a URL, look up a settlement, or serve the same
5
- commands as MCP tools.
4
+ import a key, fund it, pay a URL, look up a settlement, or serve `pay`,
5
+ `fund` and `status` as MCP tools.
6
6
 
7
7
  ```bash
8
8
  npm install -g @metalntwx/metal-agent
@@ -25,10 +25,12 @@ endpoints) and the `*USDZ` address come from `@metalntwx/metal-pay`, and
25
25
  `networks.json` holds its demo payee URL and, on testnet, its faucet and
26
26
  explorer URLs.
27
27
  `METAL_RPC_URL` and `METAL_FAUCET_URL` override those defaults, and
28
- `--rpc-url` and `--faucet-url` override both.
28
+ `--rpc-url` and `--faucet-url` override both; `METAL_FACILITATOR_URL` and
29
+ `--facilitator-url` do the same for `mandate pull`.
29
30
 
30
- On testnet, `pay` and `fund` need no RPC. `status --network testnet` needs
31
- an endpoint of your own: sign up in the Metal console,
31
+ On testnet, `pay` and `fund` need no RPC. `status`, `account create` and
32
+ every `mandate` command but `credential create` need an endpoint of your
33
+ own: sign up in the Metal console,
32
34
  https://console-testnet.metalntwx.com, create an endpoint, and pass it with
33
35
  `--rpc-url` or `METAL_RPC_URL`. On testnet, `pay` and `fund` print
34
36
  an `explorer` line with the transaction's page on
@@ -48,7 +50,8 @@ which the devnet funds at genesis.
48
50
 
49
51
  `pay` refuses, before it signs, an offer in any token but `*USDZ` and any
50
52
  offer above `--max`, 1 `*USDZ` per payment by default. A refusal at
51
- verification prints `payment refused: <reason>`.
53
+ verification, by the facilitator or the seller's own check, prints
54
+ `payment refused: <reason>`.
52
55
  A refusal at settlement prints `payment refused at settlement: <reason>`,
53
56
  with the transaction hash when there is one.
54
57
 
@@ -77,68 +80,83 @@ for the mandate's whole life, an expiry and, when `--payee` names any, the
77
80
  addresses it may pay; with none named it may pay any address.
78
81
 
79
82
  ```bash
80
- metal-agent mandate credential create --principal <MetalAccount address> [--payee <address>]... \
81
- --cap 5 --budget 10 --out ./mandate.json --network testnet
83
+ metal-agent mandate credential create --principal <MetalAccount address> [--payee <address>[,<name>[,<website>]]]... \
84
+ --cap 5 --budget 10 [--description <text>] --key <owner key file> --out ./mandate.json --network testnet
82
85
  metal-agent mandate bind --credential ./mandate.json --key <owner key file> --network testnet --rpc-url <endpoint>
83
86
  metal-agent pay <url> --mandate ./mandate.json --network testnet
84
87
  metal-agent mandate status --credential ./mandate.json --network testnet --rpc-url <endpoint>
88
+ metal-agent mandate pull --credential ./mandate.json --to <address> --amount 2.50 --network testnet --rpc-url <endpoint>
89
+ metal-agent mandate verify --credential ./mandate.json --issuer <issuer public key file> --network testnet --rpc-url <endpoint>
90
+ metal-agent mandate verify --credential ./mandate.json --issuer <issuer public key file> --tx <hash> --signed-offer <text> --network testnet --rpc-url <endpoint>
85
91
  metal-agent mandate replace --credential ./wider.json --replaces ./mandate.json --key <owner key file> --network testnet --rpc-url <endpoint>
92
+ metal-agent mandate renew --credential ./next.json --replaces ./mandate.json --key <owner key file> --network testnet --rpc-url <endpoint>
86
93
  metal-agent mandate revoke --credential ./wider.json --key <owner key file> --network testnet --rpc-url <endpoint>
87
- metal-agent mandate pull --credential ./mandate.json --to <address> --amount 2.50 --network testnet --rpc-url <endpoint>
88
- metal-agent mandate verify --credential ./mandate.json --network testnet --rpc-url <endpoint>
89
- metal-agent mandate verify --credential ./mandate.json --tx <hash> --signed-offer <text> --network testnet --rpc-url <endpoint>
90
94
  ```
91
95
 
92
96
  `credential create` writes the limits into a credential file, a Verifiable
93
- Intent mandate, and the agent's keys beside it: its mandate key in
94
- `mandate.agent-key.json`, and in `mandate.gas-key.json` the key that pays
95
- for its pulls. `--agent-key <file>` names a mandate key the agent already
96
- has instead, with its gas key beside that. The issuer keys that sign the
97
- credential are created on first use (`~/.metal-agent/issuer.json`, with its
98
- public half beside it in `issuer.public.json`). Every other command takes
99
- that credential file: the owner's account, the mandate's reference, its
100
- terms and the agent's key files derive from it. Amounts are whole cents; a
101
- mandate lasts 30 days unless `--expires-in` says otherwise.
102
-
103
- `account create --out <owner key file>` writes the owner's key and names the
104
- owner's Metal account, a smart account that key controls with threshold one,
105
- which the owner's first `bind` creates on chain; `--key <file>` uses a key
106
- that exists. Fund the account and name it as the credential's `--principal`.
97
+ Intent mandate, and beside it the mandate key the agent signs with:
98
+ `mandate.key.json` for `mandate.json`. `--mandate-key-file <file>` names a
99
+ mandate key that exists instead, as a replacement does. The keys
100
+ that sign the credential are the owner's: they live beside the owner's key file, created
101
+ on first use (`owner-key.issuer.json`, with its public half in
102
+ `owner-key.issuer.public.json`), unless `--issuer-key` names their file.
103
+ `--description` says what the mandate is for, and `--payee` takes a name
104
+ and a website after the address. Every other command takes
105
+ that credential file: the owner's account, the mandate's id and its
106
+ terms derive from it. Amounts are in `*USDZ`, to the cent; a
107
+ mandate lasts 30 days unless `--expires-in` says otherwise, and its budget
108
+ never resets.
109
+
110
+ `account create --out <owner key file>` writes the owner's key and creates the
111
+ owner's Metal account on chain, a smart account that key controls with
112
+ threshold one; `--key <file>` uses a key that exists. The owner's key pays the
113
+ gas, and one that holds none is funded first. Fund the account and name it as
114
+ the credential's `--principal`; `bind` refuses an address with no account.
107
115
  The account factory is the one `@metalntwx/metal-pay` names for the network;
108
116
  on a devnet `just deploy` deploys it there, and `--factory <address>` names
109
- another. The testnet's factory is redeployed with each reset, and
110
- `@metalntwx/metal-pay` names the current one.
111
-
112
- `bind`, `replace` and `revoke` execute self-calls through the account's weighted
113
- quorum. `--key` names the gas sponsor and defaults as the sole signer; repeat
114
- `--signer-key <file>` to supply the EOA signers of an existing multisig instead.
115
- The SDK's `authorizeMandateCall` accepts a quorum-signing callback for mixed
116
- keys and passkeys. Keep owner keys outside the agent's reach: quorum-approved
117
- transfers remain independent of mandates. `replace` revokes the mandate
118
- `--replaces` names and carries its spend into the new one.
117
+ another. `@metalntwx/metal-pay` names the testnet's.
118
+
119
+ `bind`, `replace` and `revoke` are signed by the account's owners, as many as
120
+ its threshold needs. `--key` names the gas sponsor and defaults as the sole
121
+ signer; repeat `--signer-key <file>` to supply the owners' keys of an account
122
+ with several instead. The SDK's `authorizeMandateCall` accepts a signing
123
+ callback for mixed keys and passkeys. Keep owner keys outside the agent's
124
+ reach: an owner key moves the account's funds outside any mandate's limits. `replace` revokes the mandate
125
+ `--replaces` names and carries its spend into the new one; `renew` binds the
126
+ new one and revokes the old in one batch, starting its spend at nothing, which
127
+ is how a budget starts over.
128
+ `bind`, `replace` and `renew` trust the issuer beside `--key` unless
129
+ `--issuer` names others.
119
130
 
120
131
  Verification trusts that factory, never one a payer names: it checks the
121
- account's immutable clone code against the factory's implementation at the
122
- binding and payment blocks, and checks successful token-transfer receipts.
132
+ account's immutable clone code against the factory's current
133
+ implementation, and checks successful token-transfer receipts.
123
134
 
124
135
  `pay --mandate <credential>` pays from the owner's account, signed by the
125
- agent's mandate key, and takes neither `--key` nor `--max`. A payment
126
- outside the mandate is refused at the facilitator's simulation, which names
127
- the limit: `payment refused: mandate_cap_exceeded (…)`. `pay` prints the
136
+ mandate key beside the credential, or the one `--mandate-key-file` names.
137
+ It takes neither `--key` nor `--max`. A payment
138
+ outside the mandate is refused and moves nothing, usually with a
139
+ `mandate_*` reason: `payment refused: mandate_cap_exceeded (…)`. `pay` prints the
128
140
  payment's `nonce` as soon as it signs; the facilitator's
129
141
  `/settlements?payTo=<payee>` lists a settled payment by an id ending in it.
130
142
 
131
143
  `mandate pull` pays an address the mandate allows directly, with no seller:
132
- the agent's mandate key signs, and the `--key` account, the agent's gas key
133
- by default, submits the transaction and pays its gas, so fund it with a
134
- little `*USDZ`. A pull outside the mandate is recorded on chain as a
135
- refusal, in a transaction that succeeds and moves nothing, and exits 1.
144
+ the agent's mandate key signs, and the facilitator submits the transaction
145
+ and pays its gas; `--key` names a funded key to submit it instead. A pull
146
+ the mandate refuses when checked is not sent: it reports the reason and
147
+ exits 1. With `--key`, `--record-refusal` sends it, so the refusal is
148
+ recorded on chain in a transaction that succeeds and moves nothing. The
149
+ outcome is read from the chain: `paid`, `refused`, `held` when the payee's
150
+ receive policy sends it to escrow, or `pending`. A pending pull is kept in
151
+ the directory beside the credential, `mandate.pulls` for `mandate.json`, and
152
+ running the same command again sends that same pull, which pays at most once.
136
153
 
137
154
  `mandate verify` checks the mandate and every payment under it from the
138
155
  chain alone, through any RPC endpoint: the binding on the owner's account,
139
156
  the credential against it and under its issuer, and each payment and
140
- refusal as the agent key's own signed per-payment mandate, rebuilt from its
141
- transaction. Anyone holding the credential can run it; `--tx <hash>`
157
+ refusal as the mandate key's own signed per-payment mandate, rebuilt from its
158
+ transaction. Anyone holding the credential and the issuer's public file,
159
+ passed as `--issuer`, can run it; `--tx <hash>`
142
160
  verifies one transaction, with `--signed-offer <text>` the offer the agent
143
161
  signed for it, and a check that fails exits 1. A seller that asks for the
144
162
  agent's signed order (`requireSignedOffer` in `@metalntwx/metal-pay/mandates`,
@@ -149,17 +167,25 @@ the same, the latter for a payee that holds no credential.
149
167
  `skills/metal-agent/SKILL.md` is an installable skill: the setup steps for
150
168
  an agent, then the commands.
151
169
 
152
- `metal-agent mcp` serves `pay`, `fund` and `status` over stdio,
153
- each taking a `network`, and `pay` takes the same limit as an optional
154
- `max`, the request as optional `method`, `body` and `headers`, and a
155
- credential file as an optional `mandate`, with its `agentKey`. Keys
156
- enter only through the CLI, and so do the mandate commands: the server has
157
- no tool that sets or changes a mandate. An MCP host starts it with:
170
+ `metal-agent mcp` serves `pay`, `fund` and `status` over stdio, each taking
171
+ a `network`, and `pay` the request as optional `method`, `body` and
172
+ `headers`. What `pay` spends is fixed when the server starts: its key and
173
+ `--max` (the default key file and 1 `*USDZ` when omitted), or one mandate
174
+ with `--mandate <credential>`. The model calling the tools chooses no key,
175
+ no mandate and no higher limit; under a key, a `max` it passes can only
176
+ lower the server's. The mandate commands enter only through the CLI. An MCP host
177
+ starts it with:
158
178
 
159
179
  ```json
160
180
  { "command": "npx", "args": ["-y", "@metalntwx/metal-agent", "mcp"] }
161
181
  ```
162
182
 
183
+ or, under one mandate:
184
+
185
+ ```json
186
+ { "command": "npx", "args": ["-y", "@metalntwx/metal-agent", "mcp", "--mandate", "/path/to/mandate.json"] }
187
+ ```
188
+
163
189
  ## Develop
164
190
 
165
191
  In the `mx56db/metal` repository, `just agent <command>` runs the CLI from
package/dist/chain.js CHANGED
@@ -22,7 +22,7 @@ export function resolveSettings(network, file, flags, env) {
22
22
  const faucetUrl = flags.faucetUrl ?? env["METAL_FAUCET_URL"] ?? file.faucetUrl;
23
23
  return {
24
24
  ...(rpcUrl !== undefined && { rpcUrl }),
25
- facilitatorUrl: env["METAL_FACILITATOR_URL"] ?? network.facilitatorUrl,
25
+ facilitatorUrl: flags.facilitatorUrl ?? env["METAL_FACILITATOR_URL"] ?? network.facilitatorUrl,
26
26
  demoPayeeUrl: env["METAL_DEMO_PAYEE_URL"] ?? file.demoPayeeUrl,
27
27
  ...(faucetUrl !== undefined && { faucetUrl }),
28
28
  ...(file.explorerUrl !== undefined && { explorerUrl: file.explorerUrl }),