@absol-labs/agent 0.9.1 → 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/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/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/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 +20 -1
- 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 +25 -1
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/client.js +20 -1
- 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/crewai.ts +13 -2
- package/src/gateway/server-entry.ts +1 -0
- package/src/mcp/http-server.ts +1 -0
- package/src/mcp/server.ts +25 -1
- package/src/mcp/stdio.ts +1 -0
- package/src/sdk/client.ts +33 -1
|
@@ -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
|
+
}
|
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/mcp/http-server.ts
CHANGED
package/src/mcp/server.ts
CHANGED
|
@@ -799,10 +799,34 @@ export function createVerifiedStreamMcpServer(
|
|
|
799
799
|
};
|
|
800
800
|
}
|
|
801
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
|
+
|
|
802
816
|
export function parseMetrikAgentEnv(
|
|
803
817
|
env: NodeJS.ProcessEnv = process.env,
|
|
804
818
|
): ParsedMetrikAgentEnv {
|
|
805
|
-
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;
|
|
806
830
|
const chainId = Number(parsed.METRIK_AGENT_CHAIN_ID ?? baseSepolia.id);
|
|
807
831
|
if (!Number.isInteger(chainId) || chainId <= 0) {
|
|
808
832
|
throw new Error("METRIK_AGENT_CHAIN_ID must be a positive integer");
|
package/src/mcp/stdio.ts
CHANGED
package/src/sdk/client.ts
CHANGED
|
@@ -89,6 +89,16 @@ export interface AgentSdkClient {
|
|
|
89
89
|
claim(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
|
|
90
90
|
close(streamId: `0x${string}`): Promise<StreamProofTransactionResult>;
|
|
91
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>;
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
export type AgentSdkClientFactory = (
|
|
@@ -201,7 +211,29 @@ export class VerifiedStreamAgentClient implements VerifiedStreamAgentOpener {
|
|
|
201
211
|
}
|
|
202
212
|
|
|
203
213
|
/**
|
|
204
|
-
*
|
|
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
|
|
205
237
|
* unspent funds to the buyer or stop payment, so they are authorized on
|
|
206
238
|
* mandate-owner signature ALONE — never gated by expiry, revocation, or caps,
|
|
207
239
|
* which would otherwise let a lapsed mandate strand the buyer's own funds.
|