@absol-labs/agent 0.9.0 → 0.10.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 +77 -3
- package/dist/cli/amounts.d.ts +26 -0
- package/dist/cli/amounts.d.ts.map +1 -0
- package/dist/cli/amounts.js +71 -0
- package/dist/cli/amounts.js.map +1 -0
- package/dist/cli/args.d.ts +32 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +119 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/attesters.d.ts +51 -0
- package/dist/cli/attesters.d.ts.map +1 -0
- package/dist/cli/attesters.js +58 -0
- package/dist/cli/attesters.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +5 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/claim.d.ts +13 -0
- package/dist/cli/commands/claim.d.ts.map +1 -0
- package/dist/cli/commands/claim.js +86 -0
- package/dist/cli/commands/claim.js.map +1 -0
- package/dist/cli/commands/close.d.ts +3 -0
- package/dist/cli/commands/close.d.ts.map +1 -0
- package/dist/cli/commands/close.js +43 -0
- package/dist/cli/commands/close.js.map +1 -0
- package/dist/cli/commands/discover.d.ts +3 -0
- package/dist/cli/commands/discover.d.ts.map +1 -0
- package/dist/cli/commands/discover.js +183 -0
- package/dist/cli/commands/discover.js.map +1 -0
- package/dist/cli/commands/invoke.d.ts +3 -0
- package/dist/cli/commands/invoke.d.ts.map +1 -0
- package/dist/cli/commands/invoke.js +141 -0
- package/dist/cli/commands/invoke.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +3 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +113 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/open.d.ts +3 -0
- package/dist/cli/commands/open.d.ts.map +1 -0
- package/dist/cli/commands/open.js +279 -0
- package/dist/cli/commands/open.js.map +1 -0
- package/dist/cli/commands/reclaim-unverified.d.ts +3 -0
- package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -0
- package/dist/cli/commands/reclaim-unverified.js +164 -0
- package/dist/cli/commands/reclaim-unverified.js.map +1 -0
- package/dist/cli/commands/reclaim.d.ts +33 -0
- package/dist/cli/commands/reclaim.d.ts.map +1 -0
- package/dist/cli/commands/reclaim.js +297 -0
- package/dist/cli/commands/reclaim.js.map +1 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +43 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/streams.d.ts +3 -0
- package/dist/cli/commands/streams.d.ts.map +1 -0
- package/dist/cli/commands/streams.js +257 -0
- package/dist/cli/commands/streams.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +3 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +55 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/config.d.ts +61 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +140 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/context.d.ts +29 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/context.js +2 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/deps.d.ts +145 -0
- package/dist/cli/deps.d.ts.map +1 -0
- package/dist/cli/deps.js +227 -0
- package/dist/cli/deps.js.map +1 -0
- package/dist/cli/descriptor.d.ts +61 -0
- package/dist/cli/descriptor.d.ts.map +1 -0
- package/dist/cli/descriptor.js +78 -0
- package/dist/cli/descriptor.js.map +1 -0
- package/dist/cli/errors.d.ts +48 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/errors.js +129 -0
- package/dist/cli/errors.js.map +1 -0
- package/dist/cli/escape-hatch.d.ts +50 -0
- package/dist/cli/escape-hatch.d.ts.map +1 -0
- package/dist/cli/escape-hatch.js +45 -0
- package/dist/cli/escape-hatch.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/listings.d.ts +15 -0
- package/dist/cli/listings.d.ts.map +1 -0
- package/dist/cli/listings.js +56 -0
- package/dist/cli/listings.js.map +1 -0
- package/dist/cli/mandate.d.ts +66 -0
- package/dist/cli/mandate.d.ts.map +1 -0
- package/dist/cli/mandate.js +61 -0
- package/dist/cli/mandate.js.map +1 -0
- package/dist/cli/output.d.ts +33 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +34 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/preflight.d.ts +38 -0
- package/dist/cli/preflight.d.ts.map +1 -0
- package/dist/cli/preflight.js +72 -0
- package/dist/cli/preflight.js.map +1 -0
- package/dist/cli/progress.d.ts +36 -0
- package/dist/cli/progress.d.ts.map +1 -0
- package/dist/cli/progress.js +40 -0
- package/dist/cli/progress.js.map +1 -0
- package/dist/cli/run.d.ts +25 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +173 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/scan-range.d.ts +55 -0
- package/dist/cli/scan-range.d.ts.map +1 -0
- package/dist/cli/scan-range.js +57 -0
- package/dist/cli/scan-range.js.map +1 -0
- package/dist/cli/status.d.ts +90 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +252 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/timeout.d.ts +30 -0
- package/dist/cli/timeout.d.ts.map +1 -0
- package/dist/cli/timeout.js +55 -0
- package/dist/cli/timeout.js.map +1 -0
- package/dist/cli/version.d.ts +12 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/cli/version.js +22 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/cli/wallet.d.ts +95 -0
- package/dist/cli/wallet.d.ts.map +1 -0
- package/dist/cli/wallet.js +169 -0
- package/dist/cli/wallet.js.map +1 -0
- package/dist/frameworks/agentkit.d.ts.map +1 -1
- package/dist/frameworks/agentkit.js +8 -4
- package/dist/frameworks/agentkit.js.map +1 -1
- package/dist/frameworks/crewai.d.ts +10 -0
- package/dist/frameworks/crewai.d.ts.map +1 -1
- package/dist/frameworks/crewai.js +12 -2
- package/dist/frameworks/crewai.js.map +1 -1
- package/dist/frameworks/eliza.d.ts.map +1 -1
- package/dist/frameworks/eliza.js +14 -3
- package/dist/frameworks/eliza.js.map +1 -1
- package/dist/frameworks/langchain.d.ts.map +1 -1
- package/dist/frameworks/langchain.js +14 -3
- package/dist/frameworks/langchain.js.map +1 -1
- package/dist/gateway/server-entry.d.ts +1 -0
- package/dist/gateway/server-entry.js +1 -0
- package/dist/gateway/server-entry.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/http-server.d.ts +1 -0
- package/dist/mcp/http-server.js +1 -0
- package/dist/mcp/http-server.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +46 -6
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/stdio.d.ts +1 -0
- package/dist/mcp/stdio.js +1 -0
- package/dist/mcp/stdio.js.map +1 -1
- package/dist/sdk/client.d.ts +44 -3
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/client.js +38 -3
- package/dist/sdk/client.js.map +1 -1
- package/docs/agent-layer.md +2 -1
- package/docs/cli.md +569 -0
- package/docs/crewai.md +6 -2
- package/docs/eliza.md +6 -1
- package/docs/mcp-hosted.md +3 -2
- package/docs/quickstart.md +18 -5
- package/package.json +17 -5
- package/src/cli/amounts.ts +102 -0
- package/src/cli/args.ts +150 -0
- package/src/cli/attesters.ts +86 -0
- package/src/cli/bin.ts +5 -0
- package/src/cli/commands/claim.ts +100 -0
- package/src/cli/commands/close.ts +53 -0
- package/src/cli/commands/discover.ts +228 -0
- package/src/cli/commands/invoke.ts +177 -0
- package/src/cli/commands/mcp.ts +135 -0
- package/src/cli/commands/open.ts +331 -0
- package/src/cli/commands/reclaim-unverified.ts +206 -0
- package/src/cli/commands/reclaim.ts +360 -0
- package/src/cli/commands/status.ts +56 -0
- package/src/cli/commands/streams.ts +344 -0
- package/src/cli/commands/watch.ts +70 -0
- package/src/cli/config.ts +208 -0
- package/src/cli/context.ts +30 -0
- package/src/cli/deps.ts +437 -0
- package/src/cli/descriptor.ts +133 -0
- package/src/cli/errors.ts +162 -0
- package/src/cli/escape-hatch.ts +92 -0
- package/src/cli/index.ts +27 -0
- package/src/cli/listings.ts +77 -0
- package/src/cli/mandate.ts +126 -0
- package/src/cli/output.ts +63 -0
- package/src/cli/preflight.ts +88 -0
- package/src/cli/progress.ts +75 -0
- package/src/cli/run.ts +209 -0
- package/src/cli/scan-range.ts +76 -0
- package/src/cli/status.ts +381 -0
- package/src/cli/timeout.ts +64 -0
- package/src/cli/version.ts +21 -0
- package/src/cli/wallet.ts +241 -0
- package/src/frameworks/agentkit.ts +8 -3
- package/src/frameworks/crewai.ts +13 -2
- package/src/frameworks/eliza.ts +14 -3
- package/src/frameworks/langchain.ts +14 -3
- package/src/gateway/server-entry.ts +1 -0
- package/src/index.ts +2 -0
- package/src/mcp/http-server.ts +1 -0
- package/src/mcp/server.ts +51 -6
- package/src/mcp/stdio.ts +1 -0
- package/src/sdk/client.ts +71 -4
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
2
|
+
import type { Account, LocalAccount } from "viem";
|
|
3
|
+
|
|
4
|
+
import { resolveAgentWallet } from "../wallet/provider.js";
|
|
5
|
+
import { configError } from "./errors.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The ONLY place the CLI resolves a signing wallet.
|
|
9
|
+
*
|
|
10
|
+
* There is deliberately NO `--private-key` flag, and no flag for any other
|
|
11
|
+
* credential. A secret passed on the command line lands in shell history, in
|
|
12
|
+
* `ps` output, and in any CI log that echoes the command; an environment
|
|
13
|
+
* variable does none of those. Secrets are also never echoed back: commands
|
|
14
|
+
* print the derived ADDRESS only.
|
|
15
|
+
*
|
|
16
|
+
* ## Resolution order (first match wins, checked in this order)
|
|
17
|
+
*
|
|
18
|
+
* 1. `METRIK_AGENT_PRIVATE_KEY` — a raw Base Sepolia test key.
|
|
19
|
+
* 2. The `CDP_*` / `METRIK_AGENT_CDP_*` quartet — a Coinbase CDP Server Wallet
|
|
20
|
+
* v2 account, resolved through this package's own `resolveAgentWallet()`.
|
|
21
|
+
*
|
|
22
|
+
* The order matches `parseAgentWalletEnv()` (the MCP server's resolution), so
|
|
23
|
+
* one environment configures every surface the same way. It is checked in a
|
|
24
|
+
* FIXED order rather than "whichever is configured", because an environment
|
|
25
|
+
* carrying both is ambiguous about which address funds the stream — and that
|
|
26
|
+
* ambiguity is a fund-moving one. When both are present the private key wins
|
|
27
|
+
* and `resolveCliWallet` says so on stderr rather than silently picking.
|
|
28
|
+
*
|
|
29
|
+
* ## Why CDP is wired and Privy is not
|
|
30
|
+
*
|
|
31
|
+
* CDP is the only wallet path in this package with live CI evidence
|
|
32
|
+
* (`scripts/e2e-cdp.ts` drives the full journey against the real Base Sepolia
|
|
33
|
+
* escrow), and its account signs LOCALLY — `toAccount(cdpServerAccount)` is a
|
|
34
|
+
* viem local account, so it drops straight into the CLI's existing
|
|
35
|
+
* `http(rpcUrl)` transport with no other change.
|
|
36
|
+
*
|
|
37
|
+
* Privy does not. Both Privy adapters in `src/wallet/` produce a JSON-RPC
|
|
38
|
+
* account with no `signTransaction`: they sign and broadcast by sending
|
|
39
|
+
* `eth_sendTransaction` / `eth_signTypedData_v4` to a provider, so they need the
|
|
40
|
+
* wallet client's TRANSPORT to be that provider rather than a plain RPC. Wiring
|
|
41
|
+
* that into the CLI is not a matter of reading three more variables — it
|
|
42
|
+
* changes the transport for every fund-moving command, and `metrik invoke`
|
|
43
|
+
* additionally requires a local signer (`InvokeOptions.buyer: LocalAccount`).
|
|
44
|
+
* `assertLocalSigner` below therefore REFUSES a non-local account with that
|
|
45
|
+
* fact, instead of accepting one and failing later inside viem. See
|
|
46
|
+
* `docs/cli.md` § Wallet.
|
|
47
|
+
*/
|
|
48
|
+
export const PRIVATE_KEY_ENV = "METRIK_AGENT_PRIVATE_KEY";
|
|
49
|
+
|
|
50
|
+
/** Every environment variable the CDP path reads. Named in errors, never printed. */
|
|
51
|
+
export const CDP_ENV_VARS = [
|
|
52
|
+
"CDP_API_KEY_ID",
|
|
53
|
+
"CDP_API_KEY_SECRET",
|
|
54
|
+
"CDP_WALLET_SECRET",
|
|
55
|
+
"METRIK_AGENT_CDP_OWNER_NAME",
|
|
56
|
+
] as const;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* What the key is for, in the words of the command that needs it. `invoke`
|
|
60
|
+
* signs an EIP-712 invocation capability and broadcasts NOTHING; telling its
|
|
61
|
+
* caller "this command signs a transaction" was simply false.
|
|
62
|
+
*/
|
|
63
|
+
export type KeyPurpose = "transaction" | "capability";
|
|
64
|
+
|
|
65
|
+
const PURPOSE_TEXT: Record<KeyPurpose, string> = {
|
|
66
|
+
transaction: "signs a transaction",
|
|
67
|
+
capability:
|
|
68
|
+
"signs an off-chain EIP-712 invocation capability (no transaction is broadcast)",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** Which environment configured the signer, for output and for error text. */
|
|
72
|
+
export type CliWalletSource = "private-key" | "cdp";
|
|
73
|
+
|
|
74
|
+
export interface CliWallet {
|
|
75
|
+
readonly source: CliWalletSource;
|
|
76
|
+
/**
|
|
77
|
+
* A LOCAL signer. Every CLI path either broadcasts through `http(rpcUrl)`
|
|
78
|
+
* (which cannot sign) or signs EIP-712 off-chain, so a signer that delegates
|
|
79
|
+
* to a remote provider is refused at resolution rather than mid-command.
|
|
80
|
+
*/
|
|
81
|
+
readonly account: LocalAccount;
|
|
82
|
+
/** One line naming the signer's origin. Safe to print: no secret in it. */
|
|
83
|
+
readonly description: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const WALLET_HINT =
|
|
87
|
+
`export ${PRIVATE_KEY_ENV}=0x… (a funded Base Sepolia test key), or configure a Coinbase CDP wallet with ` +
|
|
88
|
+
`${CDP_ENV_VARS.join(", ")}. Both are read from the environment only — never pass a credential as a command-line flag.`;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Resolves the signing wallet, or throws an actionable configuration error.
|
|
92
|
+
*
|
|
93
|
+
* `note` receives one line when the environment is ambiguous (both wallet
|
|
94
|
+
* sources configured). It is optional so `resolveCliWallet` stays usable from a
|
|
95
|
+
* context with no printer; commands always pass `out.note`.
|
|
96
|
+
*/
|
|
97
|
+
export async function resolveCliWallet(
|
|
98
|
+
env: NodeJS.ProcessEnv,
|
|
99
|
+
options: {
|
|
100
|
+
readonly purpose?: KeyPurpose;
|
|
101
|
+
readonly note?: (line: string) => void;
|
|
102
|
+
} = {},
|
|
103
|
+
): Promise<CliWallet> {
|
|
104
|
+
const wallet = await optionalCliWallet(env, options);
|
|
105
|
+
if (wallet === null) {
|
|
106
|
+
throw configError(
|
|
107
|
+
`this command ${PURPOSE_TEXT[options.purpose ?? "transaction"]} and needs a wallet: neither ${PRIVATE_KEY_ENV} nor the CDP variables are set`,
|
|
108
|
+
WALLET_HINT,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return wallet;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The same resolution, but "nothing configured" is not an error. Used by
|
|
116
|
+
* look-before-you-leap paths (`open --dry-run`, `streams --address`) that
|
|
117
|
+
* broadcast nothing and must therefore work for someone who does not yet have a
|
|
118
|
+
* funded wallet — which is exactly who a dry run is for.
|
|
119
|
+
*
|
|
120
|
+
* A wallet that is PARTIALLY configured is still an error here: "you set three
|
|
121
|
+
* of the four CDP variables" and "you configured no wallet" are different
|
|
122
|
+
* facts, and rendering the first as the second sends the reader looking in the
|
|
123
|
+
* wrong place.
|
|
124
|
+
*/
|
|
125
|
+
export async function optionalCliWallet(
|
|
126
|
+
env: NodeJS.ProcessEnv,
|
|
127
|
+
options: { readonly note?: (line: string) => void } = {},
|
|
128
|
+
): Promise<CliWallet | null> {
|
|
129
|
+
const privateKeyAccount = optionalCliAccount(env);
|
|
130
|
+
const cdpConfigured = CDP_ENV_VARS.some((name) => isSet(env[name]));
|
|
131
|
+
|
|
132
|
+
if (privateKeyAccount !== null) {
|
|
133
|
+
if (cdpConfigured) {
|
|
134
|
+
options.note?.(
|
|
135
|
+
`warning: both ${PRIVATE_KEY_ENV} and CDP wallet variables are set; using ${PRIVATE_KEY_ENV} (${privateKeyAccount.address}). Unset it to sign with the CDP wallet.`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
source: "private-key",
|
|
140
|
+
account: privateKeyAccount,
|
|
141
|
+
description: `${privateKeyAccount.address} (from ${PRIVATE_KEY_ENV})`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!cdpConfigured) return null;
|
|
146
|
+
|
|
147
|
+
const missing = CDP_ENV_VARS.filter((name) => !isSet(env[name]));
|
|
148
|
+
if (missing.length > 0) {
|
|
149
|
+
throw configError(
|
|
150
|
+
`the CDP wallet is partially configured: ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} not set`,
|
|
151
|
+
`a CDP wallet needs all of ${CDP_ENV_VARS.join(", ")}. Set the missing one${missing.length === 1 ? "" : "s"}, or unset the others and use ${PRIVATE_KEY_ENV} instead.`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// A CDP SMART account is an ERC-4337 contract account at a DIFFERENT address
|
|
156
|
+
// from its owner EOA, and it is reached through a bundler — which this CLI
|
|
157
|
+
// does not route to: every command broadcasts through `http(--rpc-url)`. So a
|
|
158
|
+
// smart-account environment would silently sign and spend from the OWNER,
|
|
159
|
+
// while the user funded the smart account. Refuse instead.
|
|
160
|
+
if (
|
|
161
|
+
isSet(env.METRIK_AGENT_CDP_SMART_ACCOUNT_NAME) ||
|
|
162
|
+
isTruthy(env.METRIK_AGENT_CDP_CREATE_SMART_ACCOUNT)
|
|
163
|
+
) {
|
|
164
|
+
throw configError(
|
|
165
|
+
"the CLI cannot sign from a CDP smart account: METRIK_AGENT_CDP_CREATE_SMART_ACCOUNT / METRIK_AGENT_CDP_SMART_ACCOUNT_NAME is set",
|
|
166
|
+
"a CDP smart account is an ERC-4337 account at a different address from its owner, reached through a bundler; this CLI broadcasts through --rpc-url and would spend from the OWNER address instead. Unset those two variables to use the owner account directly, or use the SDK's account-abstraction path.",
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const ownerName = env.METRIK_AGENT_CDP_OWNER_NAME as string;
|
|
171
|
+
let account: Account;
|
|
172
|
+
try {
|
|
173
|
+
const resolved = await resolveAgentWallet({
|
|
174
|
+
cdp: {
|
|
175
|
+
apiKeyId: env.CDP_API_KEY_ID as string,
|
|
176
|
+
apiKeySecret: env.CDP_API_KEY_SECRET as string,
|
|
177
|
+
walletSecret: env.CDP_WALLET_SECRET as string,
|
|
178
|
+
ownerName,
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
account = resolved.account;
|
|
182
|
+
} catch (error) {
|
|
183
|
+
// Reaching CDP is a network call against an authenticated API, so this is
|
|
184
|
+
// where a wrong key, a revoked key or an outage surfaces. Say which wallet
|
|
185
|
+
// failed; the underlying message carries the rest.
|
|
186
|
+
throw configError(
|
|
187
|
+
`could not resolve the CDP wallet "${ownerName}"`,
|
|
188
|
+
`check ${CDP_ENV_VARS.join(", ")}, and that '@coinbase/cdp-sdk' (an optional peer dependency) is installed. Underlying error: ${error instanceof Error ? error.message : String(error)}`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
source: "cdp",
|
|
194
|
+
account: assertLocalSigner(account, "cdp"),
|
|
195
|
+
description: `${account.address} (CDP account "${ownerName}")`,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The raw `METRIK_AGENT_PRIVATE_KEY` read, kept separate so the wallet-source
|
|
201
|
+
* precedence above reads as one list rather than as nested conditionals.
|
|
202
|
+
*/
|
|
203
|
+
export function optionalCliAccount(
|
|
204
|
+
env: NodeJS.ProcessEnv,
|
|
205
|
+
): LocalAccount | null {
|
|
206
|
+
const raw = env[PRIVATE_KEY_ENV];
|
|
207
|
+
if (raw === undefined || raw.length === 0) return null;
|
|
208
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(raw)) {
|
|
209
|
+
throw configError(
|
|
210
|
+
`${PRIVATE_KEY_ENV} must be a 0x-prefixed 32-byte hex private key`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return privateKeyToAccount(raw as `0x${string}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Every CLI signing path needs a LOCAL signer: fund-moving commands broadcast
|
|
218
|
+
* through `http(--rpc-url)`, which cannot sign, and `invoke` needs a
|
|
219
|
+
* `LocalAccount` to sign the EIP-712 capability. A JSON-RPC account (Privy's
|
|
220
|
+
* adapters) would type-check as an `Account` and then fail somewhere inside
|
|
221
|
+
* viem with a message about a missing method, so it is rejected here with the
|
|
222
|
+
* actual reason.
|
|
223
|
+
*/
|
|
224
|
+
function assertLocalSigner(
|
|
225
|
+
account: Account,
|
|
226
|
+
source: CliWalletSource,
|
|
227
|
+
): LocalAccount {
|
|
228
|
+
if (account.type === "local") return account;
|
|
229
|
+
throw configError(
|
|
230
|
+
`the ${source} wallet returned a ${account.type} account, which cannot sign locally`,
|
|
231
|
+
"the CLI broadcasts through --rpc-url and signs EIP-712 capabilities in-process, so it needs a local signer.",
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function isSet(value: string | undefined): boolean {
|
|
236
|
+
return value !== undefined && value.length > 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function isTruthy(value: string | undefined): boolean {
|
|
240
|
+
return value === "true" || value === "1";
|
|
241
|
+
}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../discovery/registry.js";
|
|
22
22
|
import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
|
|
23
23
|
import {
|
|
24
|
+
isCheckpointStream,
|
|
24
25
|
MandateDeniedError,
|
|
25
26
|
VerifiedStreamAgentClient,
|
|
26
27
|
type AgentSdkClientFactory,
|
|
@@ -400,9 +401,13 @@ export class MetrikActionProvider extends ActionProvider<WalletProvider> {
|
|
|
400
401
|
|
|
401
402
|
return (
|
|
402
403
|
`Stream ${streamId} is ${stream.status}${paused ? " (payment halted)" : ""}. ` +
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
404
|
+
(isCheckpointStream(stream)
|
|
405
|
+
? `Verified entitlement: ${formatUsdc(stream.settledCumulative)} USDC of ` +
|
|
406
|
+
`${formatUsdc(stream.deposit)} deposited, of which ` +
|
|
407
|
+
`${formatUsdc(stream.claimedCumulative)} USDC has been claimed. `
|
|
408
|
+
: `Verified accrued: ${formatUsdc(stream.accrued)} USDC of ` +
|
|
409
|
+
`${formatUsdc(stream.deposit)} deposited. `) +
|
|
410
|
+
`Claimable by seller: ${formatUsdc(claimable)} USDC. Reclaimable by buyer: ${formatUsdc(reclaimable)} USDC.`
|
|
406
411
|
);
|
|
407
412
|
}
|
|
408
413
|
|
package/src/frameworks/crewai.ts
CHANGED
|
@@ -23,13 +23,24 @@ export interface CrewAiVerifiedStreamMcpConfigOptions {
|
|
|
23
23
|
readonly cacheToolsList?: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The default launcher: the `metrik-mcp` bin this package ships.
|
|
28
|
+
*
|
|
29
|
+
* It used to be `pnpm mcp:stdio`, which only resolves INSIDE this repository —
|
|
30
|
+
* a CrewAI user who installed `@absol-labs/agent` from npm got a config that
|
|
31
|
+
* could never start. `metrik-mcp` is on `PATH` wherever the package is
|
|
32
|
+
* installed (npm/pnpm link it into `node_modules/.bin`), and `npx` resolves it
|
|
33
|
+
* with no install at all.
|
|
34
|
+
*/
|
|
35
|
+
export const CREWAI_DEFAULT_MCP_COMMAND = "metrik-mcp";
|
|
36
|
+
|
|
26
37
|
export function createCrewAiVerifiedStreamMcpConfig(
|
|
27
38
|
options: CrewAiVerifiedStreamMcpConfigOptions = {},
|
|
28
39
|
): CrewAiVerifiedStreamMcpConfig {
|
|
29
40
|
return {
|
|
30
41
|
transport: "stdio",
|
|
31
|
-
command: options.command ??
|
|
32
|
-
args: options.args ?? [
|
|
42
|
+
command: options.command ?? CREWAI_DEFAULT_MCP_COMMAND,
|
|
43
|
+
args: options.args ?? [],
|
|
33
44
|
env: options.env ?? {},
|
|
34
45
|
allowedToolNames:
|
|
35
46
|
options.allowedToolNames ?? CREWAI_VERIFIED_STREAM_TOOL_NAMES,
|
package/src/frameworks/eliza.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "../discovery/registry.js";
|
|
16
16
|
import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
|
|
17
17
|
import {
|
|
18
|
+
isCheckpointStream,
|
|
18
19
|
type MandateAuthorizedStreamActionInput,
|
|
19
20
|
type ReclaimAuthorizedStreamInput,
|
|
20
21
|
type ReclaimVerifiedStreamResult,
|
|
@@ -733,9 +734,19 @@ function serializeStreamStatus(
|
|
|
733
734
|
...result.stream,
|
|
734
735
|
deposit: result.stream.deposit.toString(),
|
|
735
736
|
ratePerSecond: result.stream.ratePerSecond.toString(),
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
737
|
+
// The two escrow generations report entitlement differently; emit the
|
|
738
|
+
// vocabulary of whichever one answered rather than inventing a shared one.
|
|
739
|
+
...(isCheckpointStream(result.stream)
|
|
740
|
+
? {
|
|
741
|
+
settledCumulative: result.stream.settledCumulative.toString(),
|
|
742
|
+
claimedCumulative: result.stream.claimedCumulative.toString(),
|
|
743
|
+
feesPaid: result.stream.feesPaid.toString(),
|
|
744
|
+
}
|
|
745
|
+
: {
|
|
746
|
+
accrued: result.stream.accrued.toString(),
|
|
747
|
+
claimed: result.stream.claimed.toString(),
|
|
748
|
+
lastSequence: result.stream.lastSequence.toString(),
|
|
749
|
+
}),
|
|
739
750
|
},
|
|
740
751
|
claimable: result.claimable.toString(),
|
|
741
752
|
reclaimable: result.reclaimable.toString(),
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "../discovery/registry.js";
|
|
10
10
|
import { checkMandate, type SignedSpendMandate } from "../mandates/mandate.js";
|
|
11
11
|
import {
|
|
12
|
+
isCheckpointStream,
|
|
12
13
|
type MandateAuthorizedStreamActionInput,
|
|
13
14
|
type ReclaimAuthorizedStreamInput,
|
|
14
15
|
type ReclaimVerifiedStreamResult,
|
|
@@ -332,9 +333,19 @@ function serializeStreamStatus(
|
|
|
332
333
|
...result.stream,
|
|
333
334
|
deposit: result.stream.deposit.toString(),
|
|
334
335
|
ratePerSecond: result.stream.ratePerSecond.toString(),
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
336
|
+
// The two escrow generations report entitlement differently; emit the
|
|
337
|
+
// vocabulary of whichever one answered rather than inventing a shared one.
|
|
338
|
+
...(isCheckpointStream(result.stream)
|
|
339
|
+
? {
|
|
340
|
+
settledCumulative: result.stream.settledCumulative.toString(),
|
|
341
|
+
claimedCumulative: result.stream.claimedCumulative.toString(),
|
|
342
|
+
feesPaid: result.stream.feesPaid.toString(),
|
|
343
|
+
}
|
|
344
|
+
: {
|
|
345
|
+
accrued: result.stream.accrued.toString(),
|
|
346
|
+
claimed: result.stream.claimed.toString(),
|
|
347
|
+
lastSequence: result.stream.lastSequence.toString(),
|
|
348
|
+
}),
|
|
338
349
|
},
|
|
339
350
|
claimable: result.claimable.toString(),
|
|
340
351
|
reclaimable: result.reclaimable.toString(),
|
package/src/index.ts
CHANGED
|
@@ -104,6 +104,8 @@ export {
|
|
|
104
104
|
type ReclaimAuthorizedStreamInput,
|
|
105
105
|
type ReclaimVerifiedStreamOptions,
|
|
106
106
|
type ReclaimVerifiedStreamResult,
|
|
107
|
+
isCheckpointStream,
|
|
108
|
+
type StreamStatusStream,
|
|
107
109
|
type StreamStatusView,
|
|
108
110
|
type VerifiedStreamAgentOpener,
|
|
109
111
|
} from "./sdk/client.js";
|
package/src/mcp/http-server.ts
CHANGED
package/src/mcp/server.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
ServerMandateConfigError,
|
|
21
21
|
} from "../mandates/env.js";
|
|
22
22
|
import {
|
|
23
|
+
isCheckpointStream,
|
|
23
24
|
VerifiedStreamAgentClient,
|
|
24
25
|
type ReclaimAuthorizedStreamInput,
|
|
25
26
|
type ReclaimVerifiedStreamResult,
|
|
@@ -798,10 +799,34 @@ export function createVerifiedStreamMcpServer(
|
|
|
798
799
|
};
|
|
799
800
|
}
|
|
800
801
|
|
|
802
|
+
/** One readable line per invalid or missing environment variable. */
|
|
803
|
+
function describeEnvIssues(error: z.ZodError): string {
|
|
804
|
+
const seen = new Set<string>();
|
|
805
|
+
for (const issue of error.issues) {
|
|
806
|
+
const name = issue.path.join(".");
|
|
807
|
+
seen.add(
|
|
808
|
+
issue.code === "invalid_type" && issue.received === "undefined"
|
|
809
|
+
? `${name} is not set`
|
|
810
|
+
: `${name} is invalid (${issue.message})`,
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
return [...seen].join("; ");
|
|
814
|
+
}
|
|
815
|
+
|
|
801
816
|
export function parseMetrikAgentEnv(
|
|
802
817
|
env: NodeJS.ProcessEnv = process.env,
|
|
803
818
|
): ParsedMetrikAgentEnv {
|
|
804
|
-
const
|
|
819
|
+
const result = metrikAgentEnvSchema.safeParse(env);
|
|
820
|
+
if (!result.success) {
|
|
821
|
+
// `.parse()` threw a ZodError whose message is a JSON array of issue
|
|
822
|
+
// objects. `metrik-mcp` is started by an MCP host and fails closed on stdio,
|
|
823
|
+
// so that array was the ONLY thing an operator ever saw — a correct failure
|
|
824
|
+
// rendered as noise. Name the variables instead.
|
|
825
|
+
throw new Error(
|
|
826
|
+
`metrik-mcp is not configured: ${describeEnvIssues(result.error)}. Set METRIK_AGENT_RPC_URL (an http(s) URL), METRIK_AGENT_ESCROW and METRIK_AGENT_USDC (EVM addresses), plus a wallet (METRIK_AGENT_PRIVATE_KEY, or the CDP/Privy variables). METRIK_AGENT_CHAIN_ID defaults to Base Sepolia (${baseSepolia.id}).`,
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
const parsed = result.data;
|
|
805
830
|
const chainId = Number(parsed.METRIK_AGENT_CHAIN_ID ?? baseSepolia.id);
|
|
806
831
|
if (!Number.isInteger(chainId) || chainId <= 0) {
|
|
807
832
|
throw new Error("METRIK_AGENT_CHAIN_ID must be a positive integer");
|
|
@@ -859,6 +884,11 @@ export async function createVerifiedStreamMcpServerOptionsFromEnv(
|
|
|
859
884
|
chain: parsed.chain,
|
|
860
885
|
transport: http(parsed.rpcUrl),
|
|
861
886
|
escrow: parsed.escrow,
|
|
887
|
+
// Declare the escrow as V2 as well. Without this the SDK treats the
|
|
888
|
+
// client as V1 and routes claimable()/reclaimable() to the legacy ABI,
|
|
889
|
+
// which reverts against StreamEscrowV2 — the checkpoint escrow replaced
|
|
890
|
+
// those one-argument views with proof-carrying ones.
|
|
891
|
+
escrowV2: parsed.escrow,
|
|
862
892
|
usdc: parsed.usdc,
|
|
863
893
|
settlementTarget: {
|
|
864
894
|
chainId: parsed.chain.id,
|
|
@@ -943,15 +973,30 @@ function serializeStreamStatus(
|
|
|
943
973
|
operator: status.stream.operator,
|
|
944
974
|
serviceRef: status.stream.serviceRef,
|
|
945
975
|
deposit: status.stream.deposit.toString(),
|
|
976
|
+
// Informational on a checkpoint escrow: it stores the rate but computes
|
|
977
|
+
// no accrual. Emit the vocabulary of whichever escrow generation answered
|
|
978
|
+
// instead of flattening two incompatible tuples into one shape.
|
|
946
979
|
ratePerSecond: status.stream.ratePerSecond.toString(),
|
|
947
|
-
accrued: status.stream.accrued.toString(),
|
|
948
|
-
claimed: status.stream.claimed.toString(),
|
|
949
980
|
openedAt: status.stream.openedAt,
|
|
950
981
|
expiresAt: status.stream.expiresAt,
|
|
951
|
-
lastVerifiedAt: status.stream.lastVerifiedAt,
|
|
952
|
-
lastSequence: status.stream.lastSequence.toString(),
|
|
953
|
-
consecutiveFailures: status.stream.consecutiveFailures,
|
|
954
982
|
status: status.stream.status,
|
|
983
|
+
...(isCheckpointStream(status.stream)
|
|
984
|
+
? {
|
|
985
|
+
settlement: "checkpoint" as const,
|
|
986
|
+
settledCumulative: status.stream.settledCumulative.toString(),
|
|
987
|
+
claimedCumulative: status.stream.claimedCumulative.toString(),
|
|
988
|
+
feesPaid: status.stream.feesPaid.toString(),
|
|
989
|
+
closedAt: status.stream.closedAt,
|
|
990
|
+
reclaimed: status.stream.reclaimed,
|
|
991
|
+
}
|
|
992
|
+
: {
|
|
993
|
+
settlement: "per-attestation" as const,
|
|
994
|
+
accrued: status.stream.accrued.toString(),
|
|
995
|
+
claimed: status.stream.claimed.toString(),
|
|
996
|
+
lastVerifiedAt: status.stream.lastVerifiedAt,
|
|
997
|
+
lastSequence: status.stream.lastSequence.toString(),
|
|
998
|
+
consecutiveFailures: status.stream.consecutiveFailures,
|
|
999
|
+
}),
|
|
955
1000
|
},
|
|
956
1001
|
claimable: status.claimable.toString(),
|
|
957
1002
|
reclaimable: status.reclaimable.toString(),
|
package/src/mcp/stdio.ts
CHANGED
package/src/sdk/client.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type HireComputeResult,
|
|
4
4
|
type StreamProofClientConfig,
|
|
5
5
|
type StreamProofStream,
|
|
6
|
+
type StreamV2,
|
|
6
7
|
} from "@absol-labs/sdk";
|
|
7
8
|
|
|
8
9
|
import {
|
|
@@ -25,8 +26,23 @@ export interface OpenVerifiedStreamInput {
|
|
|
25
26
|
readonly revokedMandateIds?: Iterable<`0x${string}`>;
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* A stream as read from the configured escrow. Checkpoint escrows (V2) and the
|
|
31
|
+
* legacy per-attestation escrow (V1) expose incompatible tuples, so the view
|
|
32
|
+
* carries whichever one the client is pointed at. Narrow with
|
|
33
|
+
* {@link isCheckpointStream} before touching version-specific fields.
|
|
34
|
+
*/
|
|
35
|
+
export type StreamStatusStream = StreamProofStream | StreamV2;
|
|
36
|
+
|
|
37
|
+
/** True when the stream came from a checkpoint-settled (V2) escrow. */
|
|
38
|
+
export function isCheckpointStream(
|
|
39
|
+
stream: StreamStatusStream,
|
|
40
|
+
): stream is StreamV2 {
|
|
41
|
+
return "settledCumulative" in stream;
|
|
42
|
+
}
|
|
43
|
+
|
|
28
44
|
export interface StreamStatusView {
|
|
29
|
-
readonly stream:
|
|
45
|
+
readonly stream: StreamStatusStream;
|
|
30
46
|
readonly claimable: bigint;
|
|
31
47
|
readonly reclaimable: bigint;
|
|
32
48
|
}
|
|
@@ -63,12 +79,26 @@ export interface AgentSdkClient {
|
|
|
63
79
|
readonly ratePerSecond: bigint;
|
|
64
80
|
readonly maxDurationSeconds: number;
|
|
65
81
|
}): Promise<HireComputeResult>;
|
|
82
|
+
// Both decoders. The SDK deliberately makes `getStream` throw once a V2
|
|
83
|
+
// escrow is configured (the tuples are incompatible), so the caller must pick
|
|
84
|
+
// based on configuration rather than probing.
|
|
66
85
|
getStream(streamId: `0x${string}`): Promise<StreamProofStream>;
|
|
86
|
+
getStreamV2(streamId: `0x${string}`): Promise<StreamV2>;
|
|
67
87
|
claimable(streamId: `0x${string}`): Promise<bigint>;
|
|
68
88
|
reclaimable(streamId: `0x${string}`): Promise<bigint>;
|
|
69
89
|
claim(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
|
|
70
90
|
close(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
|
|
71
91
|
reclaim(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
|
|
92
|
+
/**
|
|
93
|
+
* The BUYER's no-proof escape hatch. Every other buyer path to the money
|
|
94
|
+
* (`reclaim` -> `reclaimWithProof`) needs a Merkle proof fetched from the
|
|
95
|
+
* oracle, so it is only as available as the oracle is. `reclaimUnverified`
|
|
96
|
+
* takes no proof, no quorum and no guardian — it is the path that exists
|
|
97
|
+
* precisely for the case where that dependency has failed.
|
|
98
|
+
*/
|
|
99
|
+
reclaimUnverified(
|
|
100
|
+
streamId: `0x${string}`,
|
|
101
|
+
): Promise<StreamProofTransactionResult>;
|
|
72
102
|
}
|
|
73
103
|
|
|
74
104
|
export type AgentSdkClientFactory = (
|
|
@@ -93,6 +123,12 @@ export class MandateDeniedError extends Error {
|
|
|
93
123
|
|
|
94
124
|
export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
95
125
|
private readonly sdkClient: AgentSdkClient;
|
|
126
|
+
/**
|
|
127
|
+
* Whether the configured escrow is checkpoint-settled. Set once from config:
|
|
128
|
+
* the SDK throws rather than guessing if the wrong decoder is used, so this
|
|
129
|
+
* has to be decided here instead of discovered by trial.
|
|
130
|
+
*/
|
|
131
|
+
private readonly checkpointSettled: boolean;
|
|
96
132
|
|
|
97
133
|
constructor(
|
|
98
134
|
config: StreamProofClientConfig,
|
|
@@ -101,6 +137,15 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
|
101
137
|
this.sdkClient = (options.createSdkClient ?? defaultSdkClientFactory)(
|
|
102
138
|
config,
|
|
103
139
|
);
|
|
140
|
+
this.checkpointSettled = config.escrowV2 !== undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private async readStream(
|
|
144
|
+
streamId: `0x${string}`,
|
|
145
|
+
): Promise<StreamStatusStream> {
|
|
146
|
+
return this.checkpointSettled
|
|
147
|
+
? await this.sdkClient.getStreamV2(streamId)
|
|
148
|
+
: await this.sdkClient.getStream(streamId);
|
|
104
149
|
}
|
|
105
150
|
|
|
106
151
|
async openVerifiedStream(
|
|
@@ -132,7 +177,7 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
|
132
177
|
|
|
133
178
|
async getStreamStatus(streamId: `0x${string}`): Promise<StreamStatusView> {
|
|
134
179
|
const [stream, claimable, reclaimable] = await Promise.all([
|
|
135
|
-
this.
|
|
180
|
+
this.readStream(streamId),
|
|
136
181
|
this.sdkClient.claimable(streamId),
|
|
137
182
|
this.sdkClient.reclaimable(streamId),
|
|
138
183
|
]);
|
|
@@ -166,7 +211,29 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
|
166
211
|
}
|
|
167
212
|
|
|
168
213
|
/**
|
|
169
|
-
*
|
|
214
|
+
* Buyer-only escape hatch: reclaim WITHOUT a checkpoint proof, available only
|
|
215
|
+
* once `block.timestamp >= expiresAt + escapeGraceSeconds` on the escrow.
|
|
216
|
+
*
|
|
217
|
+
* Authorized exactly like `close` / `reclaim` — on the mandate owner's
|
|
218
|
+
* signature alone (see {@link authorizeBuyerRecovery}) — because it only
|
|
219
|
+
* returns the buyer's own unspent escrow. It is a SEPARATE method rather than
|
|
220
|
+
* an option on {@link reclaimStream} because it is not an alternative route to
|
|
221
|
+
* the same outcome: `reclaimUnverified` pays the buyer
|
|
222
|
+
* `deposit - claimedCumulative` and permanently forfeits every operator
|
|
223
|
+
* voucher that was earned but not yet redeemed on-chain.
|
|
224
|
+
*
|
|
225
|
+
* Takes no `closeFirst`: the escrow gates this path on `expiresAt`, never on
|
|
226
|
+
* `closedAt`, so closing first would buy nothing but a second gas fee.
|
|
227
|
+
*/
|
|
228
|
+
async reclaimUnverifiedStream(
|
|
229
|
+
input: MandateAuthorizedStreamActionInput,
|
|
230
|
+
): Promise<StreamProofTransactionResult> {
|
|
231
|
+
await this.authorizeBuyerRecovery(input);
|
|
232
|
+
return await this.sdkClient.reclaimUnverified(input.streamId);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Authorizes a BUYER-RECOVERY action (close / reclaim / reclaimUnverified). These only return
|
|
170
237
|
* unspent funds to the buyer or stop payment, so they are authorized on
|
|
171
238
|
* mandate-owner signature ALONE — never gated by expiry, revocation, or caps,
|
|
172
239
|
* which would otherwise let a lapsed mandate strand the buyer's own funds.
|
|
@@ -183,7 +250,7 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
|
183
250
|
private async authorizeExistingStreamAction(
|
|
184
251
|
input: MandateAuthorizedStreamActionInput,
|
|
185
252
|
): Promise<void> {
|
|
186
|
-
const stream = await this.
|
|
253
|
+
const stream = await this.readStream(input.streamId);
|
|
187
254
|
const decision = await checkMandate(
|
|
188
255
|
input.signedMandate,
|
|
189
256
|
{
|