@absol-labs/agent 0.10.0 → 0.11.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 +107 -23
- package/dist/capability/invocation-capability.d.ts +18 -6
- package/dist/capability/invocation-capability.d.ts.map +1 -1
- package/dist/capability/invocation-capability.js.map +1 -1
- package/dist/cli/commands/claim.d.ts.map +1 -1
- package/dist/cli/commands/claim.js +20 -4
- package/dist/cli/commands/claim.js.map +1 -1
- package/dist/cli/commands/close.d.ts.map +1 -1
- package/dist/cli/commands/close.js +16 -4
- package/dist/cli/commands/close.js.map +1 -1
- package/dist/cli/commands/invoke.d.ts.map +1 -1
- package/dist/cli/commands/invoke.js +1 -0
- package/dist/cli/commands/invoke.js.map +1 -1
- package/dist/cli/commands/open.d.ts.map +1 -1
- package/dist/cli/commands/open.js +63 -11
- package/dist/cli/commands/open.js.map +1 -1
- package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
- package/dist/cli/commands/reclaim-unverified.js +12 -4
- package/dist/cli/commands/reclaim-unverified.js.map +1 -1
- package/dist/cli/commands/reclaim.d.ts.map +1 -1
- package/dist/cli/commands/reclaim.js +16 -5
- package/dist/cli/commands/reclaim.js.map +1 -1
- package/dist/cli/commands/streams.js +1 -1
- package/dist/cli/commands/streams.js.map +1 -1
- package/dist/cli/deps.d.ts +53 -7
- package/dist/cli/deps.d.ts.map +1 -1
- package/dist/cli/deps.js +65 -15
- package/dist/cli/deps.js.map +1 -1
- package/dist/cli/privy.d.ts +83 -0
- package/dist/cli/privy.d.ts.map +1 -0
- package/dist/cli/privy.js +230 -0
- package/dist/cli/privy.js.map +1 -0
- package/dist/cli/spend-policy.d.ts +60 -0
- package/dist/cli/spend-policy.d.ts.map +1 -0
- package/dist/cli/spend-policy.js +65 -0
- package/dist/cli/spend-policy.js.map +1 -0
- package/dist/cli/wallet.d.ts +108 -39
- package/dist/cli/wallet.d.ts.map +1 -1
- package/dist/cli/wallet.js +108 -43
- package/dist/cli/wallet.js.map +1 -1
- package/dist/gateway/caller-auth-gateway.d.ts +38 -3
- package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
- package/dist/gateway/caller-auth-gateway.js +25 -1
- package/dist/gateway/caller-auth-gateway.js.map +1 -1
- package/dist/gateway/http-server.d.ts +53 -0
- package/dist/gateway/http-server.d.ts.map +1 -1
- package/dist/gateway/http-server.js +189 -4
- package/dist/gateway/http-server.js.map +1 -1
- package/dist/gateway/x402-mode.d.ts +123 -0
- package/dist/gateway/x402-mode.d.ts.map +1 -0
- package/dist/gateway/x402-mode.js +212 -0
- package/dist/gateway/x402-mode.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/mandates/env.d.ts +3 -2
- package/dist/mandates/env.d.ts.map +1 -1
- package/dist/mandates/env.js +3 -2
- package/dist/mandates/env.js.map +1 -1
- package/dist/mcp/server.d.ts +49 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +513 -24
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/service-invoker.d.ts +97 -0
- package/dist/mcp/service-invoker.d.ts.map +1 -0
- package/dist/mcp/service-invoker.js +167 -0
- package/dist/mcp/service-invoker.js.map +1 -0
- package/dist/sdk/invoke.d.ts +19 -4
- package/dist/sdk/invoke.d.ts.map +1 -1
- package/dist/sdk/invoke.js +69 -5
- package/dist/sdk/invoke.js.map +1 -1
- package/dist/x402/delivery-oracle.d.ts +108 -0
- package/dist/x402/delivery-oracle.d.ts.map +1 -0
- package/dist/x402/delivery-oracle.js +174 -0
- package/dist/x402/delivery-oracle.js.map +1 -0
- package/dist/x402/facilitator-server-entry.d.ts +3 -0
- package/dist/x402/facilitator-server-entry.d.ts.map +1 -0
- package/dist/x402/facilitator-server-entry.js +27 -0
- package/dist/x402/facilitator-server-entry.js.map +1 -0
- package/dist/x402/facilitator-server.d.ts +73 -0
- package/dist/x402/facilitator-server.d.ts.map +1 -0
- package/dist/x402/facilitator-server.js +305 -0
- package/dist/x402/facilitator-server.js.map +1 -0
- package/dist/x402/facilitator.d.ts +69 -17
- package/dist/x402/facilitator.d.ts.map +1 -1
- package/dist/x402/facilitator.js +76 -33
- package/dist/x402/facilitator.js.map +1 -1
- package/dist/x402/payment-requirements.d.ts +106 -0
- package/dist/x402/payment-requirements.d.ts.map +1 -0
- package/dist/x402/payment-requirements.js +138 -0
- package/dist/x402/payment-requirements.js.map +1 -0
- package/dist/x402/relayer.d.ts +41 -0
- package/dist/x402/relayer.d.ts.map +1 -0
- package/dist/x402/relayer.js +64 -0
- package/dist/x402/relayer.js.map +1 -0
- package/dist/x402/resource-index.d.ts +75 -0
- package/dist/x402/resource-index.d.ts.map +1 -0
- package/dist/x402/resource-index.js +126 -0
- package/dist/x402/resource-index.js.map +1 -0
- package/dist/x402/verified-delivery-facilitator.d.ts +285 -0
- package/dist/x402/verified-delivery-facilitator.d.ts.map +1 -0
- package/dist/x402/verified-delivery-facilitator.js +423 -0
- package/dist/x402/verified-delivery-facilitator.js.map +1 -0
- package/dist/zktls/t2-delivery-proof.d.ts +6 -6
- package/docs/agent-layer.md +12 -8
- package/docs/cli.md +158 -22
- package/docs/mcp-hosted.md +4 -3
- package/docs/mcp-reference.md +175 -0
- package/docs/quickstart.md +18 -4
- package/docs/threat-model.md +4 -3
- package/docs/x402.md +648 -0
- package/package.json +18 -7
- package/src/capability/invocation-capability.ts +17 -1
- package/src/cli/commands/claim.ts +30 -4
- package/src/cli/commands/close.ts +20 -4
- package/src/cli/commands/invoke.ts +1 -0
- package/src/cli/commands/open.ts +76 -10
- package/src/cli/commands/reclaim-unverified.ts +16 -4
- package/src/cli/commands/reclaim.ts +22 -5
- package/src/cli/commands/streams.ts +1 -1
- package/src/cli/deps.ts +115 -24
- package/src/cli/privy.ts +396 -0
- package/src/cli/spend-policy.ts +139 -0
- package/src/cli/wallet.ts +188 -57
- package/src/gateway/caller-auth-gateway.ts +77 -6
- package/src/gateway/http-server.ts +280 -6
- package/src/gateway/x402-mode.ts +350 -0
- package/src/index.ts +40 -5
- package/src/mandates/env.ts +3 -2
- package/src/mcp/server.ts +681 -29
- package/src/mcp/service-invoker.ts +285 -0
- package/src/sdk/invoke.ts +90 -12
- package/src/x402/delivery-oracle.ts +285 -0
- package/src/x402/facilitator-server-entry.ts +35 -0
- package/src/x402/facilitator-server.ts +445 -0
- package/src/x402/facilitator.ts +125 -46
- package/src/x402/payment-requirements.ts +231 -0
- package/src/x402/relayer.ts +122 -0
- package/src/x402/resource-index.ts +228 -0
- package/src/x402/verified-delivery-facilitator.ts +744 -0
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absol-labs/agent",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Metrik agent layer:
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "Metrik agent layer: verified-streaming USDC payments metered by delivery, an MCP server, framework tools, and spend mandates so AI agents can hire and pay verified services safely.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Absol Labs",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"metrik",
|
|
9
|
-
"x402",
|
|
10
9
|
"mcp",
|
|
11
10
|
"ai-agents",
|
|
12
11
|
"spend-mandate",
|
|
@@ -21,7 +20,8 @@
|
|
|
21
20
|
"metrik": "dist/cli/bin.js",
|
|
22
21
|
"metrik-mcp": "dist/mcp/stdio.js",
|
|
23
22
|
"metrik-mcp-http": "dist/mcp/http-server.js",
|
|
24
|
-
"metrik-gateway": "dist/gateway/server-entry.js"
|
|
23
|
+
"metrik-gateway": "dist/gateway/server-entry.js",
|
|
24
|
+
"metrik-x402-facilitator": "dist/x402/facilitator-server-entry.js"
|
|
25
25
|
},
|
|
26
26
|
"exports": {
|
|
27
27
|
".": {
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
"types": "./dist/frameworks/crewai.d.ts",
|
|
49
49
|
"import": "./dist/frameworks/crewai.js"
|
|
50
50
|
},
|
|
51
|
+
"./x402-facilitator": {
|
|
52
|
+
"types": "./dist/x402/facilitator-server.d.ts",
|
|
53
|
+
"import": "./dist/x402/facilitator-server.js"
|
|
54
|
+
},
|
|
51
55
|
"./package.json": "./package.json",
|
|
52
56
|
"./*": {
|
|
53
57
|
"types": "./dist/*.d.ts",
|
|
@@ -92,7 +96,8 @@
|
|
|
92
96
|
"@elizaos/core": "^1.7.2",
|
|
93
97
|
"@langchain/core": "^1.2.0",
|
|
94
98
|
"@reclaimprotocol/js-sdk": "^5.6.0",
|
|
95
|
-
"@reclaimprotocol/zk-fetch": "^1.0.0"
|
|
99
|
+
"@reclaimprotocol/zk-fetch": "^1.0.0",
|
|
100
|
+
"x402": "^1.2.0"
|
|
96
101
|
},
|
|
97
102
|
"peerDependenciesMeta": {
|
|
98
103
|
"@coinbase/agentkit": {
|
|
@@ -112,6 +117,9 @@
|
|
|
112
117
|
},
|
|
113
118
|
"@reclaimprotocol/zk-fetch": {
|
|
114
119
|
"optional": true
|
|
120
|
+
},
|
|
121
|
+
"x402": {
|
|
122
|
+
"optional": true
|
|
115
123
|
}
|
|
116
124
|
},
|
|
117
125
|
"devDependencies": {
|
|
@@ -126,7 +134,9 @@
|
|
|
126
134
|
"prettier": "^3.4.0",
|
|
127
135
|
"tsx": "^4.19.0",
|
|
128
136
|
"typescript": "^5.7.0",
|
|
129
|
-
"vitest": "^2.1.0"
|
|
137
|
+
"vitest": "^2.1.0",
|
|
138
|
+
"x402": "1.2.0",
|
|
139
|
+
"x402-fetch": "1.2.0"
|
|
130
140
|
},
|
|
131
141
|
"scripts": {
|
|
132
142
|
"build": "tsc -p tsconfig.json",
|
|
@@ -144,6 +154,7 @@
|
|
|
144
154
|
"typecheck:examples": "tsc -p tsconfig.examples.json --noEmit",
|
|
145
155
|
"typecheck:e2e": "tsc -p tsconfig.e2e.json --noEmit",
|
|
146
156
|
"lint": "tsc -p tsconfig.json --noEmit",
|
|
147
|
-
"format": "prettier --write ."
|
|
157
|
+
"format": "prettier --write .",
|
|
158
|
+
"x402:facilitator": "tsx src/x402/facilitator-server-entry.ts"
|
|
148
159
|
}
|
|
149
160
|
}
|
|
@@ -172,11 +172,27 @@ export function buildInvocationCapabilityTypedData(
|
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/**
|
|
176
|
+
* The minimum an account must expose to sign a capability: its address, and
|
|
177
|
+
* EIP-712 signing.
|
|
178
|
+
*
|
|
179
|
+
* Deliberately narrower than viem's `LocalAccount`. Every wallet this package
|
|
180
|
+
* resolves signs typed data — a raw key, a CDP server account via `toAccount()`,
|
|
181
|
+
* a Privy embedded EOA — but only the raw-key one is a `LocalAccount` by TYPE.
|
|
182
|
+
* Demanding the full interface would have meant the MCP server could only ever
|
|
183
|
+
* invoke with a raw key, which is the wallet an MCP host is least likely to be
|
|
184
|
+
* configured with. Widening a parameter type breaks no existing caller.
|
|
185
|
+
*/
|
|
186
|
+
export type InvocationCapabilitySigner = Pick<
|
|
187
|
+
LocalAccount,
|
|
188
|
+
"address" | "signTypedData"
|
|
189
|
+
>;
|
|
190
|
+
|
|
175
191
|
/** Signs an `InvocationCapability` with the buyer's own wallet. */
|
|
176
192
|
export async function signInvocationCapability(
|
|
177
193
|
capability: InvocationCapability,
|
|
178
194
|
domain: InvocationCapabilityDomainInput,
|
|
179
|
-
account:
|
|
195
|
+
account: InvocationCapabilitySigner,
|
|
180
196
|
): Promise<Hex> {
|
|
181
197
|
const typedData = buildInvocationCapabilityTypedData(capability, domain);
|
|
182
198
|
return account.signTypedData(typedData);
|
|
@@ -3,7 +3,12 @@ import { assertTestnetRpc, parseBytes32 } from "../config.js";
|
|
|
3
3
|
import { CliError, describeError, usageError } from "../errors.js";
|
|
4
4
|
import { signClaimMandate } from "../mandate.js";
|
|
5
5
|
import { assertCanPayGas } from "../preflight.js";
|
|
6
|
-
import {
|
|
6
|
+
import { writeClientOptions } from "../deps.js";
|
|
7
|
+
import {
|
|
8
|
+
isPolicyEnforced,
|
|
9
|
+
PRIVATE_KEY_ENV,
|
|
10
|
+
resolveCliWallet,
|
|
11
|
+
} from "../wallet.js";
|
|
7
12
|
import type { CommandContext, CommandSpec } from "../context.js";
|
|
8
13
|
|
|
9
14
|
/**
|
|
@@ -30,7 +35,21 @@ export const claimCommand: CommandSpec = {
|
|
|
30
35
|
}
|
|
31
36
|
const streamId = parseBytes32(streamIdArg, "<streamId>");
|
|
32
37
|
|
|
33
|
-
const
|
|
38
|
+
const wallet = await resolveCliWallet(env, {
|
|
39
|
+
note: out.note,
|
|
40
|
+
rpcUrl: config.rpcUrl,
|
|
41
|
+
});
|
|
42
|
+
const { account } = wallet;
|
|
43
|
+
if (isPolicyEnforced(wallet)) {
|
|
44
|
+
// Said plainly rather than discovered mid-command. `claim` is the
|
|
45
|
+
// OPERATOR's side, and a Privy broker policy allowlists the BUYER
|
|
46
|
+
// actions only (openStream, close, reclaim, reclaimUnverified, and a
|
|
47
|
+
// bounded USDC approval). That is a property of an operator using a
|
|
48
|
+
// buyer-scoped wallet, not a defect in this command.
|
|
49
|
+
out.note(
|
|
50
|
+
"warning: this wallet's broker policy allowlists buyer actions only, so it will refuse to sign an operator claim. Claim with the operator's own key.",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
34
53
|
const read = deps.createReadClient(config);
|
|
35
54
|
await assertTestnetRpc(config, () => read.getChainId());
|
|
36
55
|
await assertCanPayGas(read, account.address);
|
|
@@ -74,10 +93,17 @@ export const claimCommand: CommandSpec = {
|
|
|
74
93
|
});
|
|
75
94
|
}
|
|
76
95
|
|
|
77
|
-
const write = deps.createWriteClient(
|
|
96
|
+
const write = deps.createWriteClient(
|
|
97
|
+
config,
|
|
98
|
+
account,
|
|
99
|
+
writeClientOptions(wallet),
|
|
100
|
+
);
|
|
78
101
|
const result = await write.claimStream({
|
|
79
102
|
streamId,
|
|
80
|
-
|
|
103
|
+
// `null` for a policy-enforced wallet — see `../spend-policy.ts`.
|
|
104
|
+
signedMandate: isPolicyEnforced(wallet)
|
|
105
|
+
? null
|
|
106
|
+
: await signClaimMandate(account, config.chainId),
|
|
81
107
|
spentSoFarUsdc: 0n,
|
|
82
108
|
nowSeconds: deps.nowSeconds(),
|
|
83
109
|
});
|
|
@@ -2,7 +2,8 @@ import { assertTestnetRpc, parseBytes32 } from "../config.js";
|
|
|
2
2
|
import { usageError } from "../errors.js";
|
|
3
3
|
import { signRecoveryMandate } from "../mandate.js";
|
|
4
4
|
import { assertCanPayGas } from "../preflight.js";
|
|
5
|
-
import {
|
|
5
|
+
import { writeClientOptions } from "../deps.js";
|
|
6
|
+
import { isPolicyEnforced, resolveCliWallet } from "../wallet.js";
|
|
6
7
|
import type { CommandContext, CommandSpec } from "../context.js";
|
|
7
8
|
|
|
8
9
|
export const closeCommand: CommandSpec = {
|
|
@@ -19,13 +20,28 @@ export const closeCommand: CommandSpec = {
|
|
|
19
20
|
}
|
|
20
21
|
const streamId = parseBytes32(streamIdArg, "<streamId>");
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
+
const wallet = await resolveCliWallet(env, {
|
|
24
|
+
note: out.note,
|
|
25
|
+
rpcUrl: config.rpcUrl,
|
|
26
|
+
});
|
|
27
|
+
const { account } = wallet;
|
|
23
28
|
const read = deps.createReadClient(config);
|
|
24
29
|
await assertTestnetRpc(config, () => read.getChainId());
|
|
25
30
|
await assertCanPayGas(read, account.address);
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
// A policy-enforced wallet signs no mandate of its own: this is a
|
|
33
|
+
// buyer-recovery action, the escrow gates it on the buyer address and the
|
|
34
|
+
// broker gates it again on `stream.buyer == wallet`. A locally self-signed
|
|
35
|
+
// placeholder would add nothing and the broker would refuse to sign it.
|
|
36
|
+
// See `../spend-policy.ts`.
|
|
37
|
+
const signedMandate = isPolicyEnforced(wallet)
|
|
38
|
+
? null
|
|
39
|
+
: await signRecoveryMandate(account, config.chainId);
|
|
40
|
+
const write = deps.createWriteClient(
|
|
41
|
+
config,
|
|
42
|
+
account,
|
|
43
|
+
writeClientOptions(wallet),
|
|
44
|
+
);
|
|
29
45
|
const result = await write.closeStream({
|
|
30
46
|
streamId,
|
|
31
47
|
signedMandate,
|
|
@@ -61,6 +61,7 @@ export const invokeCommand: CommandSpec = {
|
|
|
61
61
|
const { account } = await resolveCliWallet(env, {
|
|
62
62
|
purpose: "capability",
|
|
63
63
|
note: out.note,
|
|
64
|
+
rpcUrl: config.rpcUrl,
|
|
64
65
|
});
|
|
65
66
|
const read = deps.createReadClient(config);
|
|
66
67
|
// No transaction is signed here — but the capability is bound to
|
package/src/cli/commands/open.ts
CHANGED
|
@@ -8,10 +8,17 @@ import {
|
|
|
8
8
|
parseUsdcAmount,
|
|
9
9
|
} from "../amounts.js";
|
|
10
10
|
import { assertTestnetRpc, parseBytes32 } from "../config.js";
|
|
11
|
+
import { writeClientOptions } from "../deps.js";
|
|
11
12
|
import { CliError, configError } from "../errors.js";
|
|
13
|
+
import type { PrivySpendPolicy } from "../privy.js";
|
|
14
|
+
import {
|
|
15
|
+
assertWithinOperatorCaps,
|
|
16
|
+
assertWithinSpendPolicy,
|
|
17
|
+
} from "../spend-policy.js";
|
|
12
18
|
import { cliMandateCaps, signCliMandate } from "../mandate.js";
|
|
13
19
|
import { assertCanFundStream, assertCanPayGas } from "../preflight.js";
|
|
14
20
|
import {
|
|
21
|
+
isPolicyEnforced,
|
|
15
22
|
optionalCliWallet,
|
|
16
23
|
resolveCliWallet,
|
|
17
24
|
PRIVATE_KEY_ENV,
|
|
@@ -74,8 +81,8 @@ export const openCommand: CommandSpec = {
|
|
|
74
81
|
// the intended call and says which parts (allowance, simulation, mandate
|
|
75
82
|
// signature) could not be produced, rather than inventing them.
|
|
76
83
|
const wallet = dryRun
|
|
77
|
-
? await optionalCliWallet(env, { note: out.note })
|
|
78
|
-
: await resolveCliWallet(env, { note: out.note });
|
|
84
|
+
? await optionalCliWallet(env, { note: out.note, rpcUrl: config.rpcUrl })
|
|
85
|
+
: await resolveCliWallet(env, { note: out.note, rpcUrl: config.rpcUrl });
|
|
79
86
|
const account = wallet?.account ?? null;
|
|
80
87
|
const read = deps.createReadClient(config);
|
|
81
88
|
await assertTestnetRpc(config, () => read.getChainId());
|
|
@@ -101,16 +108,41 @@ export const openCommand: CommandSpec = {
|
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
const spend = { operator, budgetUsdc, ratePerSecondUsdc, durationSeconds };
|
|
111
|
+
const mandateCaps = cliMandateCaps(spend, env);
|
|
112
|
+
|
|
113
|
+
// A policy-enforced wallet (Privy) is capped by its broker, not by a
|
|
114
|
+
// mandate this process signs for itself. Read the REAL ceiling and hold it
|
|
115
|
+
// to that instead — see `../spend-policy.ts` for why that substitution is
|
|
116
|
+
// the stronger of the two, and why re-adding the self-signed mandate here
|
|
117
|
+
// would both weaken it and break the wallet outright.
|
|
118
|
+
const policy: PrivySpendPolicy | null =
|
|
119
|
+
wallet !== null && isPolicyEnforced(wallet)
|
|
120
|
+
? await wallet.readSpendPolicy()
|
|
121
|
+
: null;
|
|
122
|
+
if (policy !== null && !dryRun) {
|
|
123
|
+
assertWithinSpendPolicy(policy, spend);
|
|
124
|
+
// The operator's own standing ceiling still applies, and may be TIGHTER
|
|
125
|
+
// than the broker's. Enforced here because no mandate is signed to carry
|
|
126
|
+
// it — "policy-enforced" retires the self-signed mandate, not the caps
|
|
127
|
+
// the operator deliberately configured.
|
|
128
|
+
if (mandateCaps.source === "env") {
|
|
129
|
+
assertWithinOperatorCaps(mandateCaps.caps, spend);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
104
133
|
const mandate =
|
|
105
|
-
account === null
|
|
134
|
+
account === null || policy !== null
|
|
106
135
|
? null
|
|
107
136
|
: await signCliMandate(account, spend, {
|
|
108
137
|
chainId: config.chainId,
|
|
109
138
|
env,
|
|
110
139
|
});
|
|
111
|
-
const mandateCaps = cliMandateCaps(spend, env);
|
|
112
140
|
|
|
113
|
-
const write = deps.createWriteClient(
|
|
141
|
+
const write = deps.createWriteClient(
|
|
142
|
+
config,
|
|
143
|
+
account,
|
|
144
|
+
writeClientOptions(wallet),
|
|
145
|
+
);
|
|
114
146
|
const hire = {
|
|
115
147
|
operator,
|
|
116
148
|
serviceRef,
|
|
@@ -145,6 +177,19 @@ export const openCommand: CommandSpec = {
|
|
|
145
177
|
durationSeconds,
|
|
146
178
|
mandateSource: mandateCaps.source,
|
|
147
179
|
mandateSigned: mandate !== null,
|
|
180
|
+
spendPolicy:
|
|
181
|
+
policy === null
|
|
182
|
+
? null
|
|
183
|
+
: {
|
|
184
|
+
brokerUrl: policy.brokerUrl,
|
|
185
|
+
maxTotalUsdc: policy.maxTotalUsdc,
|
|
186
|
+
maxRatePerSecondUsdc: policy.maxRatePerSecondUsdc,
|
|
187
|
+
maxDurationSeconds: policy.maxDurationSeconds,
|
|
188
|
+
fits:
|
|
189
|
+
budgetUsdc <= policy.maxTotalUsdc &&
|
|
190
|
+
ratePerSecondUsdc <= policy.maxRatePerSecondUsdc &&
|
|
191
|
+
durationSeconds <= policy.maxDurationSeconds,
|
|
192
|
+
},
|
|
148
193
|
to: preview.to,
|
|
149
194
|
data: preview.data,
|
|
150
195
|
chainId: preview.chainId,
|
|
@@ -180,10 +225,28 @@ export const openCommand: CommandSpec = {
|
|
|
180
225
|
["duration", formatDuration(durationSeconds)],
|
|
181
226
|
[
|
|
182
227
|
"mandate",
|
|
183
|
-
mandate
|
|
184
|
-
? `
|
|
185
|
-
:
|
|
228
|
+
mandate !== null
|
|
229
|
+
? `self-signed, caps from ${mandateCaps.source}`
|
|
230
|
+
: policy !== null
|
|
231
|
+
? "not signed — this wallet's ceiling is enforced by its broker, which is stronger"
|
|
232
|
+
: `not signed (no wallet); caps would come from ${mandateCaps.source}`,
|
|
186
233
|
],
|
|
234
|
+
...(policy === null
|
|
235
|
+
? []
|
|
236
|
+
: ([
|
|
237
|
+
[
|
|
238
|
+
"spend policy",
|
|
239
|
+
`${formatUsdc(policy.maxTotalUsdc)} total, ${policy.maxRatePerSecondUsdc} atomic/second, ${formatDuration(policy.maxDurationSeconds)} max — enforced by ${policy.brokerUrl}`,
|
|
240
|
+
],
|
|
241
|
+
[
|
|
242
|
+
"fits policy",
|
|
243
|
+
budgetUsdc <= policy.maxTotalUsdc &&
|
|
244
|
+
ratePerSecondUsdc <= policy.maxRatePerSecondUsdc &&
|
|
245
|
+
durationSeconds <= policy.maxDurationSeconds
|
|
246
|
+
? "yes"
|
|
247
|
+
: "NO — a real open would be refused before broadcasting",
|
|
248
|
+
],
|
|
249
|
+
] as [string, string][])),
|
|
187
250
|
["escrow", preview.to],
|
|
188
251
|
[
|
|
189
252
|
"usdc balance",
|
|
@@ -219,7 +282,7 @@ export const openCommand: CommandSpec = {
|
|
|
219
282
|
);
|
|
220
283
|
// Unreachable without a wallet: `resolveCliWallet` above throws when the
|
|
221
284
|
// command is not a dry run. The narrowing keeps that a type-level fact.
|
|
222
|
-
if (account === null
|
|
285
|
+
if (account === null) {
|
|
223
286
|
throw configError(
|
|
224
287
|
"this command signs a transaction and needs a wallet, and none was resolved",
|
|
225
288
|
);
|
|
@@ -233,7 +296,10 @@ export const openCommand: CommandSpec = {
|
|
|
233
296
|
budgetUsdc,
|
|
234
297
|
ratePerSecondUsdc,
|
|
235
298
|
maxDurationSeconds: durationSeconds,
|
|
236
|
-
|
|
299
|
+
// `null` for a policy-enforced wallet: the ceiling was checked against
|
|
300
|
+
// the broker's own policy above, and the broker checks it again on the
|
|
301
|
+
// transaction it signs.
|
|
302
|
+
signedMandate: mandate === null ? null : mandate.signed,
|
|
237
303
|
spentSoFarUsdc: 0n,
|
|
238
304
|
nowSeconds: deps.nowSeconds(),
|
|
239
305
|
});
|
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
} from "../escape-hatch.js";
|
|
11
11
|
import { signRecoveryMandate } from "../mandate.js";
|
|
12
12
|
import { assertCanPayGas } from "../preflight.js";
|
|
13
|
-
import {
|
|
13
|
+
import { writeClientOptions } from "../deps.js";
|
|
14
|
+
import { isPolicyEnforced, resolveCliWallet } from "../wallet.js";
|
|
14
15
|
import type { CommandContext, CommandSpec } from "../context.js";
|
|
15
16
|
import type { CliReadClient } from "../deps.js";
|
|
16
17
|
|
|
@@ -80,7 +81,11 @@ export const reclaimUnverifiedCommand: CommandSpec = {
|
|
|
80
81
|
);
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
const
|
|
84
|
+
const wallet = await resolveCliWallet(env, {
|
|
85
|
+
note: out.note,
|
|
86
|
+
rpcUrl: config.rpcUrl,
|
|
87
|
+
});
|
|
88
|
+
const { account } = wallet;
|
|
84
89
|
const read = deps.createReadClient(config);
|
|
85
90
|
await assertTestnetRpc(config, () => read.getChainId());
|
|
86
91
|
await assertCanPayGas(read, account.address);
|
|
@@ -120,10 +125,17 @@ export const reclaimUnverifiedCommand: CommandSpec = {
|
|
|
120
125
|
);
|
|
121
126
|
out.note("");
|
|
122
127
|
|
|
123
|
-
const write = deps.createWriteClient(
|
|
128
|
+
const write = deps.createWriteClient(
|
|
129
|
+
config,
|
|
130
|
+
account,
|
|
131
|
+
writeClientOptions(wallet),
|
|
132
|
+
);
|
|
124
133
|
const result = await write.reclaimUnverifiedStream({
|
|
125
134
|
streamId,
|
|
126
|
-
|
|
135
|
+
// `null` for a policy-enforced wallet — see `../spend-policy.ts`.
|
|
136
|
+
signedMandate: isPolicyEnforced(wallet)
|
|
137
|
+
? null
|
|
138
|
+
: await signRecoveryMandate(account, config.chainId),
|
|
127
139
|
spentSoFarUsdc: 0n,
|
|
128
140
|
nowSeconds: deps.nowSeconds(),
|
|
129
141
|
});
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
} from "../escape-hatch.js";
|
|
12
12
|
import { signRecoveryMandate } from "../mandate.js";
|
|
13
13
|
import { assertCanPayGas } from "../preflight.js";
|
|
14
|
-
import {
|
|
14
|
+
import { writeClientOptions } from "../deps.js";
|
|
15
|
+
import { isPolicyEnforced, resolveCliWallet } from "../wallet.js";
|
|
15
16
|
import type { CommandContext, CommandSpec } from "../context.js";
|
|
16
17
|
import type { CliReadClient, CliWriteClient } from "../deps.js";
|
|
17
18
|
import type {
|
|
@@ -48,12 +49,28 @@ export const reclaimCommand: CommandSpec = {
|
|
|
48
49
|
if (attempts < 1) throw usageError("--attempts must be at least 1");
|
|
49
50
|
if (interval < 1) throw usageError("--interval must be at least 1 second");
|
|
50
51
|
|
|
51
|
-
const
|
|
52
|
+
const wallet = await resolveCliWallet(env, {
|
|
53
|
+
note: out.note,
|
|
54
|
+
rpcUrl: config.rpcUrl,
|
|
55
|
+
});
|
|
56
|
+
const { account } = wallet;
|
|
52
57
|
const read = deps.createReadClient(config);
|
|
53
58
|
await assertTestnetRpc(config, () => read.getChainId());
|
|
54
59
|
await assertCanPayGas(read, account.address);
|
|
55
60
|
|
|
56
|
-
const write = deps.createWriteClient(
|
|
61
|
+
const write = deps.createWriteClient(
|
|
62
|
+
config,
|
|
63
|
+
account,
|
|
64
|
+
writeClientOptions(wallet),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// A policy-enforced wallet (Privy) signs no mandate of its own: these are
|
|
68
|
+
// buyer-recovery actions, gated by the escrow on the buyer address and by
|
|
69
|
+
// the broker on `stream.buyer == wallet`. See `../spend-policy.ts`.
|
|
70
|
+
const recoveryMandate = async () =>
|
|
71
|
+
isPolicyEnforced(wallet)
|
|
72
|
+
? null
|
|
73
|
+
: await signRecoveryMandate(account, config.chainId);
|
|
57
74
|
|
|
58
75
|
// `--close-first` is TWO transactions, and the second one only simulates
|
|
59
76
|
// successfully against post-close state. Sequencing them inside a single SDK
|
|
@@ -75,7 +92,7 @@ export const reclaimCommand: CommandSpec = {
|
|
|
75
92
|
} else {
|
|
76
93
|
closeResult = await closeBeforeReclaim(write, {
|
|
77
94
|
streamId,
|
|
78
|
-
signedMandate: await
|
|
95
|
+
signedMandate: await recoveryMandate(),
|
|
79
96
|
spentSoFarUsdc: 0n,
|
|
80
97
|
nowSeconds: deps.nowSeconds(),
|
|
81
98
|
});
|
|
@@ -97,7 +114,7 @@ export const reclaimCommand: CommandSpec = {
|
|
|
97
114
|
write,
|
|
98
115
|
async () => ({
|
|
99
116
|
streamId,
|
|
100
|
-
signedMandate: await
|
|
117
|
+
signedMandate: await recoveryMandate(),
|
|
101
118
|
spentSoFarUsdc: 0n,
|
|
102
119
|
nowSeconds: deps.nowSeconds(),
|
|
103
120
|
}),
|
|
@@ -234,7 +234,7 @@ async function resolveScanAddress(
|
|
|
234
234
|
if (wallet === null) {
|
|
235
235
|
throw configError(
|
|
236
236
|
"metrik streams needs a wallet to know whose streams to list",
|
|
237
|
-
`pass --address 0x… to list any address's streams without one, or set ${PRIVATE_KEY_ENV} (or the CDP variables).`,
|
|
237
|
+
`pass --address 0x… to list any address's streams without one, or set ${PRIVATE_KEY_ENV} (or the CDP or Privy variables).`,
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
240
|
return wallet.account.address;
|