@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
package/docs/cli.md
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# The `metrik` CLI
|
|
2
|
+
|
|
3
|
+
`metrik` runs the whole Metrik loop from a terminal: find a verified service, escrow
|
|
4
|
+
USDC into a metered stream, call the service, watch what it actually earned, stop the
|
|
5
|
+
clock, and take back what was never delivered.
|
|
6
|
+
|
|
7
|
+
**What you are buying: TIME, not calls.** A stream rents an endpoint that the oracle keeps
|
|
8
|
+
probing, and the meter runs on availability that VERIFIED — not on the requests you send.
|
|
9
|
+
One request and a thousand requests cost the same second, and the clock keeps running
|
|
10
|
+
while you send none. A reviewer who made a single `invoke` call and paid for 302 seconds
|
|
11
|
+
was billed exactly as designed. `metrik close <streamId>` stops the clock; nothing else
|
|
12
|
+
does, and nothing pushes money back on its own.
|
|
13
|
+
|
|
14
|
+
It is the **first surface that completes that loop**. The MCP tool set can discover,
|
|
15
|
+
hire, check status and reclaim — but it cannot invoke the paid service and it cannot
|
|
16
|
+
close a stream, so a reviewer or a new integrator had no single place to see the product
|
|
17
|
+
end to end.
|
|
18
|
+
|
|
19
|
+
The CLI is a thin shell over this package's existing exports (`discoverServices`,
|
|
20
|
+
`VerifiedStreamAgentClient`, `invoke`, `MetrikClient`). It re-implements no protocol
|
|
21
|
+
logic and defines no stream type of its own.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx --package @absol-labs/agent metrik --help # no install
|
|
27
|
+
pnpm add -g @absol-labs/agent && metrik --help # installed
|
|
28
|
+
pnpm cli --help # inside this repository
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The package publishes four executables. Each is declared in `bin`, shipped in the
|
|
32
|
+
tarball, and — the part that matters — proved to resolve from a clean consumer's
|
|
33
|
+
`node_modules/.bin` by `pnpm test:package`:
|
|
34
|
+
|
|
35
|
+
| Bin | What it starts |
|
|
36
|
+
| ----------------- | ----------------------------------------------------------------- |
|
|
37
|
+
| `metrik` | this CLI |
|
|
38
|
+
| `metrik-mcp` | the stdio MCP server (`src/mcp/stdio.ts`) |
|
|
39
|
+
| `metrik-mcp-http` | the hosted multi-tenant MCP server (`src/mcp/http-server.ts`) |
|
|
40
|
+
| `metrik-gateway` | the reference caller-auth gateway (`src/gateway/server-entry.ts`) |
|
|
41
|
+
|
|
42
|
+
The last two were documented as `node dist/…/x.js` against a checkout, which a
|
|
43
|
+
consumer who installed the package could not run at all. `metrik mcp
|
|
44
|
+
--print-config` prints the MCP client block that starts `metrik-mcp`.
|
|
45
|
+
|
|
46
|
+
## Testnet only
|
|
47
|
+
|
|
48
|
+
The CLI signs with a raw private key and moves USDC, so the chain is an **allowlist**,
|
|
49
|
+
not a default: only Base Sepolia (`84532`) is accepted. There is deliberately **no
|
|
50
|
+
override flag** — an escape hatch that re-enabled real funds would defeat the guarantee,
|
|
51
|
+
and there is no mainnet deployment for it to reach anyway.
|
|
52
|
+
|
|
53
|
+
The rule is enforced twice:
|
|
54
|
+
|
|
55
|
+
1. against the chain id you declare (`--chain-id`, or `METRIK_AGENT_CHAIN_ID`), and
|
|
56
|
+
2. against the chain id the RPC endpoint **actually reports**, re-read before any
|
|
57
|
+
command that signs. A declared chain id is just a number someone typed; the endpoint
|
|
58
|
+
is the thing that settles. This is what stops
|
|
59
|
+
`--chain-id 84532 --rpc-url <a mainnet RPC>`.
|
|
60
|
+
|
|
61
|
+
## Wallet
|
|
62
|
+
|
|
63
|
+
Two wallet sources, resolved in a **fixed order** — first match wins:
|
|
64
|
+
|
|
65
|
+
| Order | Variables | Wallet |
|
|
66
|
+
| ----- | ------------------------------------------------------------------------------------------ | --------------------------------------- |
|
|
67
|
+
| 1 | `METRIK_AGENT_PRIVATE_KEY` | a raw Base Sepolia test key |
|
|
68
|
+
| 2 | `CDP_API_KEY_ID`, `CDP_API_KEY_SECRET`, `CDP_WALLET_SECRET`, `METRIK_AGENT_CDP_OWNER_NAME` | a Coinbase CDP Server Wallet v2 account |
|
|
69
|
+
|
|
70
|
+
This is the same order `parseAgentWalletEnv()` uses, so one environment configures the
|
|
71
|
+
CLI, the MCP server and the framework adapters identically. The order is **fixed**
|
|
72
|
+
rather than "whichever is configured": an environment carrying both is ambiguous about
|
|
73
|
+
which address funds the stream, and that ambiguity is a fund-moving one. When both are
|
|
74
|
+
present the private key wins and the CLI says so on stderr rather than choosing quietly.
|
|
75
|
+
|
|
76
|
+
Three misconfigurations are refused up front, at exit `3`, instead of failing somewhere
|
|
77
|
+
deeper:
|
|
78
|
+
|
|
79
|
+
- **A half-configured CDP wallet** ("you set three of the four variables") is reported as
|
|
80
|
+
exactly that. Rendering it as "no wallet is configured" would send the reader looking
|
|
81
|
+
in the wrong place.
|
|
82
|
+
- **A CDP _smart_ account** (`METRIK_AGENT_CDP_CREATE_SMART_ACCOUNT` /
|
|
83
|
+
`METRIK_AGENT_CDP_SMART_ACCOUNT_NAME`) is refused. It is an ERC-4337 account at a
|
|
84
|
+
different address from its owner, reached through a bundler; this CLI broadcasts
|
|
85
|
+
through `--rpc-url`, so it would sign and spend from the **owner** while you funded the
|
|
86
|
+
smart account.
|
|
87
|
+
- **A non-local signer** is refused with the reason, rather than accepted and failed
|
|
88
|
+
later inside viem.
|
|
89
|
+
|
|
90
|
+
There is no `--private-key` flag, and there will not be one — nor a flag for any other
|
|
91
|
+
credential: a secret on the command line lands in shell history, in `ps` output, and in
|
|
92
|
+
any CI log that echoes the command. Commands print the derived **address**; the
|
|
93
|
+
credential is never echoed, including by `metrik mcp --print-config`.
|
|
94
|
+
|
|
95
|
+
**Privy is not wired into the CLI, and this is not an oversight.** Both Privy adapters in
|
|
96
|
+
`src/wallet/` produce a JSON-RPC account with no `signTransaction`: they sign and
|
|
97
|
+
broadcast by sending `eth_sendTransaction` / `eth_signTypedData_v4` to a provider, so
|
|
98
|
+
they need the wallet client's **transport** to be that provider rather than a plain RPC.
|
|
99
|
+
That is not three more environment variables — it changes the transport for every
|
|
100
|
+
fund-moving command, and `metrik invoke` additionally requires a local signer
|
|
101
|
+
(`InvokeOptions.buyer: LocalAccount`). CDP has neither problem (its account signs
|
|
102
|
+
locally) and is the only wallet path in this package with live CI evidence, which is why
|
|
103
|
+
it is the one that is wired.
|
|
104
|
+
|
|
105
|
+
`invoke` needs the key too, even though it broadcasts nothing: it signs an **off-chain
|
|
106
|
+
EIP-712 invocation capability** as the stream's buyer. `open --dry-run` is the one path
|
|
107
|
+
that needs no key at all — it builds and prints the intended call and says which parts
|
|
108
|
+
(allowance, simulation, mandate signature) it could not produce without one.
|
|
109
|
+
|
|
110
|
+
## Commands
|
|
111
|
+
|
|
112
|
+
| Command | Needs a wallet? | Broadcasts a tx? | What it does |
|
|
113
|
+
| -------------------- | ------------------------------ | ---------------- | ------------------------------------------------ |
|
|
114
|
+
| `discover` | no | no | Lists verified registry listings |
|
|
115
|
+
| `open` | **yes** (not with `--dry-run`) | **yes** | Escrows USDC into a metered stream |
|
|
116
|
+
| `status` | no | no | One stream's on-chain state + verified accrual |
|
|
117
|
+
| `streams` | no (with `--address`) | no | Lists a wallet's streams from the escrow's logs |
|
|
118
|
+
| `watch` | no | no | Polls that until the stream closes |
|
|
119
|
+
| `invoke` | **yes** (off-chain capability) | no | Calls the paid service, authorized by the stream |
|
|
120
|
+
| `close` | **yes** | **yes** | Stops the clock |
|
|
121
|
+
| `reclaim` | **yes** | **yes** | Takes back what was never earned |
|
|
122
|
+
| `reclaim-unverified` | **yes** | **yes** | Buyer escape hatch: reclaim with no proof |
|
|
123
|
+
| `claim` | **yes** (the operator's) | **yes** | Operator: redeem what verified |
|
|
124
|
+
| `mcp` | no | no | Prints the MCP client config for `metrik-mcp` |
|
|
125
|
+
|
|
126
|
+
### `metrik discover`
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
metrik discover # everything the registry serves
|
|
130
|
+
metrik discover --min-tier 2 --limit 5 # verified-only, five strongest
|
|
131
|
+
metrik discover --operator 0x… # one operator's listings
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`--category` filters the registry's unsigned `category` column. The public discovery
|
|
135
|
+
endpoint serves **signed records only** and carries no category column, so that filter
|
|
136
|
+
matches nothing there — it is for an authenticated PostgREST read.
|
|
137
|
+
|
|
138
|
+
Each row prints what the operator's **signed descriptor** already carries: the callable
|
|
139
|
+
endpoints (method + path + description), one worked example call, and the accepted
|
|
140
|
+
`ratePerSecond` band in USDC **base units** — which is what `--rate … --units atomic`
|
|
141
|
+
takes. Nothing unsigned is presented as fact.
|
|
142
|
+
|
|
143
|
+
Listings are ordered **strongest declared verification first** (re-execution, then schema,
|
|
144
|
+
then SLA, then canaries), ties broken on `serviceRef`, so the order is reproducible and
|
|
145
|
+
`--limit 1` returns the best-attested listing rather than whatever the registry happened to
|
|
146
|
+
serve first.
|
|
147
|
+
|
|
148
|
+
Every row is schema-validated, its `serviceRef` re-derived from the signed record and the
|
|
149
|
+
operator signature recovered — a compromised registry can neither invent an operator to
|
|
150
|
+
pay nor redirect an invocation URL. Rows that fail are dropped and reported on stderr.
|
|
151
|
+
|
|
152
|
+
An unreachable registry is an **error**, never an empty list. "The marketplace is empty"
|
|
153
|
+
and "the registry was down" are different facts and are never rendered the same way.
|
|
154
|
+
|
|
155
|
+
**Who attests is disclosed.** `discover` reads `getOracleSigners()` off the escrow and, when
|
|
156
|
+
a listing's operator is ALSO one of those signers, says so on stderr and sets
|
|
157
|
+
`operatorIsOracleSigner: true` in `--json`. On the current Base Sepolia deployment that
|
|
158
|
+
overlap is real: the escrow has three oracle signers and one of them operates the live
|
|
159
|
+
listings, so the party being paid is among the parties attesting that delivery happened.
|
|
160
|
+
Verification there is not independent of the seller, and a tool that prints "tier 2" and
|
|
161
|
+
"cryptographically verified" without saying so is letting a buyer assume otherwise. The
|
|
162
|
+
CLI does not refuse such a listing and does not score it — it states the fact.
|
|
163
|
+
|
|
164
|
+
If the signer set cannot be read, `operatorIsOracleSigner` is `null` and the warning says
|
|
165
|
+
**UNKNOWN**. "We checked and there is no overlap" and "we could not check" are different
|
|
166
|
+
facts; an escrow outage never renders as a clean bill of health.
|
|
167
|
+
|
|
168
|
+
**SLA text is the operator's own claim.** Anything under `sla` comes from the operator's
|
|
169
|
+
signed descriptor, and a signature proves only that the operator SAID it. Metrik verifies
|
|
170
|
+
delivery, not promises made about it — one live listing claims that two consecutive failed
|
|
171
|
+
intervals auto-pause the stream, and StreamEscrowV2 has no paused state at all, so that
|
|
172
|
+
claim cannot be true. Every rendering of it is labelled unverified, in human output and in
|
|
173
|
+
`--json` (`slaClaimVerified: false`).
|
|
174
|
+
|
|
175
|
+
### `metrik open`
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
metrik open --service <serviceRef> --rate 0.000001 --budget 0.27 --duration 1h
|
|
179
|
+
metrik open --service <serviceRef> --rate 1 --budget 270000 --duration 3600 --units atomic
|
|
180
|
+
metrik open ... --dry-run
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The operator is taken from the **verified listing** for that `serviceRef` (override with
|
|
184
|
+
`--operator`, which then skips the registry lookup). `--dry-run` builds and simulates the
|
|
185
|
+
transaction and broadcasts nothing — it prints the escrow calldata, the bounded USDC
|
|
186
|
+
approval, and the `streamId` a real hire would produce.
|
|
187
|
+
|
|
188
|
+
**`--dry-run` needs no wallet.** It is the look-before-you-leap path, and the person who
|
|
189
|
+
most needs it is the one who has not funded a key yet. Without `METRIK_AGENT_PRIVATE_KEY`
|
|
190
|
+
it prints the intended call and reports `buyer: null`, `mandateSigned: false`, and the
|
|
191
|
+
SDK's own `no account configured — allowance and simulation skipped`, rather than
|
|
192
|
+
inventing an address or a signature. With a key, the preview is a real simulation from a
|
|
193
|
+
real address.
|
|
194
|
+
|
|
195
|
+
**Two pre-flights, both before anything is signed.** Gas and the deposit are separate
|
|
196
|
+
balances on separate tokens, so passing one says nothing about the other:
|
|
197
|
+
|
|
198
|
+
1. Before a real open, close or reclaim, the CLI reads the wallet's **native** balance and
|
|
199
|
+
refuses at exit `3` if it is zero, naming the Base Sepolia faucet — instead of letting
|
|
200
|
+
the node answer with `gas required exceeds allowance (0)`, which reads like a USDC
|
|
201
|
+
approval problem and is not one.
|
|
202
|
+
2. Before a real open, it reads the wallet's **USDC** balance and refuses at exit `3` if it
|
|
203
|
+
is below `--budget`, naming Circle's faucet. Without it the open path sends the USDC
|
|
204
|
+
approval first — which succeeds, because approving an amount you do not hold is legal —
|
|
205
|
+
and then reverts inside `transferFrom`, having already spent gas to learn something
|
|
206
|
+
that was one `balanceOf` away.
|
|
207
|
+
|
|
208
|
+
The second check is deliberately **not** an allowance check: the CLI's own open path grants
|
|
209
|
+
the bounded approval it needs (`approveTxHash` in the result), so a missing allowance is a
|
|
210
|
+
step of the command, not a precondition for it. Gating on it would refuse a hire that was
|
|
211
|
+
about to work. Balance is the fact the CLI cannot fix for you.
|
|
212
|
+
|
|
213
|
+
`--dry-run` reports the USDC balance instead of enforcing it — it broadcasts nothing, so an
|
|
214
|
+
underfunded wallet is information there, not a refusal.
|
|
215
|
+
|
|
216
|
+
**A dry run does not predict failure on a first hire.** The simulation runs against current
|
|
217
|
+
chain state, which does not include the bounded USDC approval a real open sends FIRST, so
|
|
218
|
+
`openStream` reverts on the allowance every time that approval is not already in place.
|
|
219
|
+
That is the command's own ordering, not a fault, and reporting it as `simulationError` made
|
|
220
|
+
the one command meant to build confidence forecast failure on first use. It is now reported
|
|
221
|
+
as `simulationPendingApproval: true`, with no `simulationError` — a script branching on
|
|
222
|
+
that field no longer sees the expected path as a failure. An allowance-shaped revert when
|
|
223
|
+
the allowance IS already sufficient, and every other revert, are still surfaced as errors.
|
|
224
|
+
|
|
225
|
+
**Amounts.** `--budget` and `--rate` are decimal USDC by default; `--units atomic`
|
|
226
|
+
switches both to raw 6-decimal base units (what the escrow, the oracle and every
|
|
227
|
+
`METRIK_E2E_*` fixture use). The unit is explicit rather than inferred from magnitude,
|
|
228
|
+
because guessing would be a silent 1,000,000x error on a fund-moving command.
|
|
229
|
+
|
|
230
|
+
**Spend mandates.** Every fund-moving path in this package goes through `checkMandate()`,
|
|
231
|
+
and the CLI is not exempt. The person typing the command is the mandate owner: the
|
|
232
|
+
mandate is self-signed from the same wallet that funds the stream, exactly as the MCP
|
|
233
|
+
server does. If `METRIK_AGENT_MANDATE_*` caps are set, they bind and an oversized hire is
|
|
234
|
+
denied; with no caps configured, the mandate is sized to exactly this hire and scoped to
|
|
235
|
+
exactly this operator. Neither path is an unbounded mandate.
|
|
236
|
+
|
|
237
|
+
### `metrik status` / `metrik watch`
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
metrik status <streamId>
|
|
241
|
+
metrik watch <streamId> --interval 15 --max-polls 40
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`--max-polls` has **no default**: `watch` runs until the stream closes or you interrupt
|
|
245
|
+
it. Both commands apply the same Base Sepolia allowlist as the signing paths — the V1 and
|
|
246
|
+
V2 stream tuples diverge from index 5 onward, so a wrong-chain read does not revert, it
|
|
247
|
+
silently decodes something else into fields with the right names.
|
|
248
|
+
|
|
249
|
+
Accrual is read from the **oracle's checkpoint proof**, not from the stream tuple.
|
|
250
|
+
`StreamEscrowV2.getStream` reports `claimedCumulative = 0` and `settledCumulative = 0`
|
|
251
|
+
for a stream's entire active life (`settledCumulative` is written by `reclaim`), so a
|
|
252
|
+
status built from stream fields would print "earned: 0" for a stream that had been
|
|
253
|
+
delivering for an hour.
|
|
254
|
+
|
|
255
|
+
Before the oracle's first covering checkpoint there is simply no proof, and the CLI says
|
|
256
|
+
so rather than printing a zero that looks like a verdict. The protocol fee is read live
|
|
257
|
+
off the escrow (`protocolFeeBps`), never hardcoded.
|
|
258
|
+
|
|
259
|
+
**No settled figure is printed while the final checkpoint is missing.** Once a stream stops
|
|
260
|
+
— closed, or past `expiresAt` — its accrual is final only after a checkpoint whose
|
|
261
|
+
`checkedAt` reaches that stop time. Until then the latest checkpoint describes a SHORTER
|
|
262
|
+
stream than the one that exists, so a reclaimable computed from it is too large and falls
|
|
263
|
+
when the covering checkpoint lands. A live run read `reclaimable: 1400` straight after
|
|
264
|
+
`close`, `0` two minutes later, and got `NothingToReclaim()` from the reclaim in between.
|
|
265
|
+
|
|
266
|
+
So while that is pending, `buyer reclaimable` reads `pending final checkpoint` with the two
|
|
267
|
+
timestamps that decide it, and `--json` sets `reclaimable: null` plus an explicit
|
|
268
|
+
`settlement` object:
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"reclaimable": null,
|
|
273
|
+
"settlement": {
|
|
274
|
+
"pending": true,
|
|
275
|
+
"stopAt": 1789671000,
|
|
276
|
+
"coveredUpTo": 1789670980,
|
|
277
|
+
"provisionalReclaimable": "1400"
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The superseded number is kept under a name that cannot be mistaken for money. If the
|
|
283
|
+
escrow will not say how far the oracle has published, that counts as pending — withholding
|
|
284
|
+
a figure we cannot stand behind is the buyer-favouring error.
|
|
285
|
+
|
|
286
|
+
`operator claimable` is treated differently on purpose: `claimWithProof` accepts any
|
|
287
|
+
retained root, so the operator can act on it now and it can only grow. It is shown, marked
|
|
288
|
+
`not final until the checkpoint covering the stop time lands`, rather than withheld.
|
|
289
|
+
|
|
290
|
+
**The seller's side of the escape hatch.** When a stream has unclaimed earnings, `status`
|
|
291
|
+
also prints an `operator deadline` row: `reclaimUnverified` pays the buyer the whole
|
|
292
|
+
remaining deposit with no proof from `expiresAt + escapeGraceSeconds`, and on the live
|
|
293
|
+
escrow that grace is 900 seconds and immutable. An operator who has earned but not claimed
|
|
294
|
+
loses it at that moment, and the only defence is to `metrik claim` before it — which
|
|
295
|
+
requires knowing the deadline exists.
|
|
296
|
+
|
|
297
|
+
StreamEscrowV2 has exactly two states, `active` and `closed`. There is no paused state
|
|
298
|
+
and the CLI never renders one.
|
|
299
|
+
|
|
300
|
+
Two rows exist so that a buyer can diagnose a stuck reclaim from `status` alone. **oracle
|
|
301
|
+
checkpoint** is how far the oracle has actually published; a value well behind the
|
|
302
|
+
stream's stop time IS the reason a `reclaim` is blocked. **escape hatch** is when the
|
|
303
|
+
no-proof path opens. Both are printed for every stream, not only broken ones — a buyer
|
|
304
|
+
should learn the escape hatch exists before the day they need it.
|
|
305
|
+
|
|
306
|
+
Every deadline on that screen is resolved against the **chain's** clock, not this
|
|
307
|
+
machine's, because the escrow compares against `block.timestamp`. If the chain will not
|
|
308
|
+
answer, the local clock is used and `clockSource` says so rather than hiding it.
|
|
309
|
+
|
|
310
|
+
### `metrik streams`
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
metrik streams # the configured wallet's streams
|
|
314
|
+
metrik streams --address 0x… --role buyer # anyone's, as buyer only
|
|
315
|
+
metrik streams --limit 5 --json
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Answers "which streams are mine?" from the escrow's own `StreamOpened` logs. `buyer` and
|
|
319
|
+
`operator` are **indexed** on that event, so `--role` becomes an `eth_getLogs` topic
|
|
320
|
+
filter the node evaluates: the response carries only that wallet's streams no matter how
|
|
321
|
+
wide the window is.
|
|
322
|
+
|
|
323
|
+
It signs nothing and broadcasts nothing, so `--address` is enough — no wallet needed.
|
|
324
|
+
With no `--address`, the configured wallet is the subject.
|
|
325
|
+
|
|
326
|
+
**The scan is anchored at the escrow's deploy block**, not at a fixed lookback. A
|
|
327
|
+
lookback cannot describe coverage: a wallet whose stream predates the window is shown an
|
|
328
|
+
empty list, indistinguishable from a wallet that never opened one, and telling someone
|
|
329
|
+
their escrowed funds do not exist is the worst failure a payments tool has. The contract
|
|
330
|
+
has no history before its deploy block, so whether the scan reached it is a fact the
|
|
331
|
+
command can state. Pass `--from-block` for a non-default `--escrow`; without one, a
|
|
332
|
+
different escrow is refused rather than scanned from a block that means nothing for it.
|
|
333
|
+
|
|
334
|
+
**An empty result is only reported as "no streams" when it can be trusted.** Three things
|
|
335
|
+
can make a scan partial, and each is named on stderr in both output modes, with
|
|
336
|
+
`degraded: true` and a `degradedReasons` array in `--json`:
|
|
337
|
+
|
|
338
|
+
- the window budget could not reach the anchor (4,000,000 blocks — the live escrow's whole
|
|
339
|
+
history is ~1.06M, so the default scan is complete);
|
|
340
|
+
- a log query failed three times (one transient 429 is retried, not counted);
|
|
341
|
+
- the RPC has **pruned its log index**. A pruning node answers a range it no longer
|
|
342
|
+
retains with a _successful empty result_, so the CLI probes the deploy block first —
|
|
343
|
+
measured on Base Sepolia, that block carries 6 escrow logs, and an endpoint that returns
|
|
344
|
+
none for it has pruned the history and its empty windows cannot be believed.
|
|
345
|
+
`https://base-sepolia-rpc.publicnode.com` fails this probe today; `https://sepolia.base.org`
|
|
346
|
+
passes it.
|
|
347
|
+
|
|
348
|
+
The `--json` output carries `logRetentionProven` (`true` / `false` / `null` when no canary
|
|
349
|
+
was possible). A row whose `getStream` read failed is kept and marked `status: "unread"` —
|
|
350
|
+
never dropped, because a stream that vanishes from this list is exactly what the command
|
|
351
|
+
exists to prevent.
|
|
352
|
+
|
|
353
|
+
`originalDepositUsdc` comes from the **event**, not the stream: StreamEscrowV2 mutates
|
|
354
|
+
`Stream.deposit` on reclaim, so the log is the only surviving record of what was escrowed.
|
|
355
|
+
Earned-so-far is not listed here — that is a per-stream call to the oracle, and
|
|
356
|
+
`metrik status <streamId>` is the command that makes it.
|
|
357
|
+
|
|
358
|
+
### `metrik mcp --print-config`
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
metrik mcp # same thing: a bare invocation prints it
|
|
362
|
+
metrik mcp --print-config # the block to paste into an MCP client
|
|
363
|
+
metrik mcp --print-config --name metrik-b # a second server key, for a second wallet
|
|
364
|
+
metrik mcp --print-config --json | jq .
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Prints the `mcpServers` block for Claude Desktop (or any MCP client) that starts this
|
|
368
|
+
package's stdio MCP server. A **bare `metrik mcp` prints it too** — it used to exit `2`
|
|
369
|
+
demanding `--print-config` while the help said the command prints a config — and states on
|
|
370
|
+
stderr that it printed a config rather than starting anything. The server itself is the
|
|
371
|
+
separate `metrik-mcp` executable, which the host launches. It describes an **installed** package, not this repository:
|
|
372
|
+
the command is `npx -y --package @absol-labs/agent@<this version> metrik-mcp`, pinned to
|
|
373
|
+
the version that printed it — `latest` would drift under an MCP host that signs spend
|
|
374
|
+
mandates.
|
|
375
|
+
|
|
376
|
+
The block carries every variable the server fails closed without: `METRIK_AGENT_RPC_URL`,
|
|
377
|
+
`METRIK_AGENT_ESCROW` and `METRIK_AGENT_USDC` (which have **no** defaults there, unlike in
|
|
378
|
+
this CLI) filled from the settlement config the CLI is already pointed at, plus the four
|
|
379
|
+
`METRIK_AGENT_MANDATE_*` caps without which the server refuses to sign a mandate at all.
|
|
380
|
+
A printed config that omitted them would be a config that fails on first launch, which is
|
|
381
|
+
the failure this command exists to prevent.
|
|
382
|
+
|
|
383
|
+
Configured mandate ceilings are carried through verbatim — they are policy, not secrets,
|
|
384
|
+
and re-typing four caps from memory is where a wrong ceiling comes from. **Credentials are
|
|
385
|
+
always placeholders.** This command never reads a key out of your environment and prints
|
|
386
|
+
it back; the wallet block is simply shaped like the environment it ran in (the CDP
|
|
387
|
+
variables if those are what you have set, `METRIK_AGENT_PRIVATE_KEY` otherwise).
|
|
388
|
+
|
|
389
|
+
### `metrik invoke`
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
metrik invoke <streamId> --method POST --path /transform --body '{"text":"hi"}'
|
|
393
|
+
metrik invoke <streamId> --path /v1/status --header "accept: application/json"
|
|
394
|
+
metrik invoke <streamId> --path /health --ttl 120
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
`--path` defaults to `/`, `--method` to `GET`, and `--ttl` (the capability's lifetime in
|
|
398
|
+
seconds) to `60`. `--header` may be repeated.
|
|
399
|
+
|
|
400
|
+
Resolves the invocation origin from the operator's **signed** listing, signs an EIP-712
|
|
401
|
+
`InvocationCapability` scoped to exactly this method and path, and sends it in the
|
|
402
|
+
`x-metrik-capability` header. No out-of-band credential is involved: the stream is the
|
|
403
|
+
credential.
|
|
404
|
+
|
|
405
|
+
`--base-url` is an escape hatch for a local gateway. It bypasses verified listing
|
|
406
|
+
routing, so the CLI warns when it is used.
|
|
407
|
+
|
|
408
|
+
A non-2xx response is an error exit, so a script can branch on it. This command signs a
|
|
409
|
+
capability but broadcasts no transaction — it still requires `METRIK_AGENT_PRIVATE_KEY`,
|
|
410
|
+
because the capability is signed by the stream's buyer.
|
|
411
|
+
|
|
412
|
+
**Not shipped: a `prove` command.** `invokeWithT2DeliveryProof` throws for gated
|
|
413
|
+
listings ("consumer-attested T2 invocation is not supported for gated listings"), and the
|
|
414
|
+
live listings are gated — so a `metrik prove` would be a command that fails for almost
|
|
415
|
+
everyone who ran it. It is left out until that constraint lifts.
|
|
416
|
+
|
|
417
|
+
### `metrik close` and `metrik reclaim`
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
metrik close <streamId>
|
|
421
|
+
metrik reclaim <streamId> --attempts 12 --interval 10
|
|
422
|
+
metrik reclaim <streamId> --close-first
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Nothing pushes money back. `close` stops the clock; `reclaim` is the transaction that
|
|
426
|
+
returns the unspent budget, and the buyer sends it. `close` now points at
|
|
427
|
+
`--close-first` for next time, rather than leaving the one-shot form visible only in
|
|
428
|
+
`metrik reclaim --help`.
|
|
429
|
+
|
|
430
|
+
**`reclaim` waits, by design.** `reclaimWithProof` reverts with `SettlementWindowOpen`
|
|
431
|
+
until a checkpoint whose `checkedAt` is at or after the stream's stop time has posted.
|
|
432
|
+
That is the normal ordering of a checkpoint rail, not a fault — in practice the first
|
|
433
|
+
attempt fails and one roughly 30 seconds later succeeds. The CLI retries **that one
|
|
434
|
+
condition** on a bounded schedule and narrates the wait on stderr; every other failure is
|
|
435
|
+
surfaced immediately and unretried.
|
|
436
|
+
|
|
437
|
+
If it is still blocked when the budget runs out, the message does **not** just say "retry
|
|
438
|
+
later" — every attempt this command makes needs a proof from the oracle, so when the
|
|
439
|
+
oracle is gone, retrying forever never pays. The give-up message names
|
|
440
|
+
`reclaim-unverified`, the path that needs no proof, and says whether its window is open
|
|
441
|
+
yet and what taking it would cost.
|
|
442
|
+
|
|
443
|
+
**`--close-first` is two transactions, sequenced by the CLI.** The reclaim only simulates
|
|
444
|
+
against post-close state, so the close is sent, then confirmed through the same read path
|
|
445
|
+
the reclaim will use, and only then is the reclaim attempted. (`sepolia.base.org` is
|
|
446
|
+
load-balanced: the close receipt can come back from one node while the next simulation
|
|
447
|
+
lands on another that has not imported the block.) If the stream is **already** closed,
|
|
448
|
+
the close is skipped and the command goes straight to reclaim — so re-running it is always
|
|
449
|
+
safe and never dead-ends on `StreamClosedAlready()`.
|
|
450
|
+
|
|
451
|
+
`reclaim` on a stream that is still running reports that in words, with the command that
|
|
452
|
+
fixes it, rather than surfacing the `StreamNotReclaimable()` selector.
|
|
453
|
+
|
|
454
|
+
Close and reclaim are buyer-recovery actions: `VerifiedStreamAgentClient` authorizes them
|
|
455
|
+
on the mandate owner's signature alone, never on caps or expiry, so a lapsed mandate can
|
|
456
|
+
never strand a buyer's own money.
|
|
457
|
+
|
|
458
|
+
### `metrik claim`
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
metrik claim <streamId>
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
The seller's side of the loop, and the only redemption surface in this package — the MCP
|
|
465
|
+
server has no claim tool either. `status` has always printed an `operator claimable` row;
|
|
466
|
+
until now nothing in the CLI could act on it.
|
|
467
|
+
|
|
468
|
+
`claimWithProof` is operator-gated on-chain, so the command reads the stream first and
|
|
469
|
+
refuses a wallet that is not the operator **before** it broadcasts. An address mismatch is
|
|
470
|
+
knowable with one read; letting it become a raw `NotOperator()` revert would charge gas to
|
|
471
|
+
learn a fact that was already free. Same for a missing checkpoint: accrual lives in the
|
|
472
|
+
oracle's proof, never in the stream tuple, so no proof means no claim is possible yet and
|
|
473
|
+
the command says that instead of paying for `NothingToClaim()`.
|
|
474
|
+
|
|
475
|
+
### `metrik reclaim-unverified`
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
metrik reclaim-unverified <streamId> --forfeit-unclaimed
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**The escape hatch, for when the oracle cannot give you a proof.** Every other route this
|
|
482
|
+
CLI has to a buyer's money goes through `reclaimWithProof`, so the whole ordinary path is
|
|
483
|
+
only as available as the oracle is. `StreamEscrowV2.reclaimUnverified` is the one that is
|
|
484
|
+
not: buyer-only, no proof, no signer quorum, no guardian.
|
|
485
|
+
|
|
486
|
+
**What it costs.** The escrow sets the operator's entitlement to what they had _already
|
|
487
|
+
claimed on-chain_, and their later claims then revert `NothingToClaim()` forever. So every
|
|
488
|
+
voucher the operator verifiably earned but had not yet redeemed is **permanently forfeit,
|
|
489
|
+
and paid to the buyer**. Nothing recovers it afterwards. That is why this is a separate
|
|
490
|
+
command rather than a `metrik reclaim --unverified` flag one token away from the safe path,
|
|
491
|
+
and why `--forfeit-unclaimed` has no default and no `--no-` form: the only way to reach the
|
|
492
|
+
transaction is to type a phrase that says what it does. Where `metrik reclaim` works, use
|
|
493
|
+
that instead — it settles the operator honestly and still pays you the rest.
|
|
494
|
+
|
|
495
|
+
**When it opens.** The escrow gates it on
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
block.timestamp >= expiresAt + escapeGraceSeconds
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
— on **`expiresAt`, never on `closedAt`**. Closing a stream early does **not** bring the
|
|
502
|
+
hatch forward: close a 24-hour stream after ten minutes and the hatch still opens roughly
|
|
503
|
+
24 hours out, not `escapeGraceSeconds` later. Before then there is genuinely no no-proof
|
|
504
|
+
route, and no CLI change can invent one. Readiness is fully computable before any gas is
|
|
505
|
+
spent (`escapeGraceSeconds` is a public immutable), so the command refuses early rather
|
|
506
|
+
than broadcasting into a guaranteed `EscapeHatchNotReady()`.
|
|
507
|
+
|
|
508
|
+
Before broadcasting it prints what the operator stands to lose, read live. On this path
|
|
509
|
+
that figure is usually **unknown** — the reason to be here at all is that the oracle is not
|
|
510
|
+
answering, and the operator's redeemable balance is derived from the oracle's proof. It is
|
|
511
|
+
reported as unknown, never as zero.
|
|
512
|
+
|
|
513
|
+
> **Deployment note.** `escapeGraceSeconds` is **900 s** on the live Base Sepolia escrow,
|
|
514
|
+
> while the contract's own comment says it should be "typically >= a day" — the point being
|
|
515
|
+
> to give an operator time to redeem before forfeiture. Both values are immutable, so this
|
|
516
|
+
> cannot change without a redeploy. The forfeiture above therefore bites far sooner than
|
|
517
|
+
> the contract author intended.
|
|
518
|
+
|
|
519
|
+
## Output
|
|
520
|
+
|
|
521
|
+
Human-readable by default. `--json` puts one JSON document on stdout; warnings, progress
|
|
522
|
+
and errors always go to **stderr**, so the stream stays parseable. `metrik watch --json`
|
|
523
|
+
emits **JSON Lines** — one compact object per poll, each on its own line, so every line
|
|
524
|
+
parses standalone:
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
metrik watch <streamId> --json | while read -r line; do
|
|
528
|
+
echo "$line" | jq -r '.verifiedAccrual.cumulativeAmount // "not checkpointed yet"'
|
|
529
|
+
done
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
Exit codes: `0` success, `2` usage error, `3` refused configuration (wrong chain, no
|
|
533
|
+
wallet, mandate denied), `4` the operation failed.
|
|
534
|
+
|
|
535
|
+
**Nothing waits silently.** Every RPC call carries a 15s budget with two bounded retries,
|
|
536
|
+
and the oracle fetch — an HTTP call the RPC transport does not cover — has its own 20s
|
|
537
|
+
budget that names the endpoint when it expires. Any command still working after 8 seconds
|
|
538
|
+
starts a heartbeat on stderr with the elapsed time, repeating every 10s, because with no
|
|
539
|
+
output at all a slow endpoint and a wedged process are indistinguishable. The heartbeat is
|
|
540
|
+
stderr in both modes, so `--json` stdout stays a single parseable document.
|
|
541
|
+
|
|
542
|
+
Read paths time out; **broadcasts do not.** A transaction that has been sent may already be
|
|
543
|
+
in the mempool, and a CLI that walked away from it on a timer would report a failure for
|
|
544
|
+
something that in fact settled. Write paths keep waiting, and the heartbeat is what makes
|
|
545
|
+
that wait legible.
|
|
546
|
+
|
|
547
|
+
## Settlement configuration
|
|
548
|
+
|
|
549
|
+
Everything defaults to Metrik's live Base Sepolia deployment, and every value is
|
|
550
|
+
overridable per command or through the environment:
|
|
551
|
+
|
|
552
|
+
| Flag | Env | Default |
|
|
553
|
+
| ---------------- | --------------------------- | ------------------------------------- |
|
|
554
|
+
| `--chain-id` | `METRIK_AGENT_CHAIN_ID` | `84532` |
|
|
555
|
+
| `--rpc-url` | `METRIK_AGENT_RPC_URL` | Base Sepolia public RPC |
|
|
556
|
+
| `--escrow` | `METRIK_AGENT_ESCROW` | `BASE_SEPOLIA_ESCROW_V2` |
|
|
557
|
+
| `--usdc` | `METRIK_AGENT_USDC` | `BASE_SEPOLIA_USDC` |
|
|
558
|
+
| `--oracle-url` | `METRIK_AGENT_ORACLE_URL` | `https://oracle.metrik.live` |
|
|
559
|
+
| `--registry-url` | `METRIK_AGENT_REGISTRY_URL` | the oracle's public listings endpoint |
|
|
560
|
+
|
|
561
|
+
## Embedding it
|
|
562
|
+
|
|
563
|
+
The runner is exported, with every side-effecting dependency injected:
|
|
564
|
+
|
|
565
|
+
```ts
|
|
566
|
+
import { runCli } from "@absol-labs/agent/cli";
|
|
567
|
+
|
|
568
|
+
const exitCode = await runCli({ argv: ["status", streamId, "--json"] });
|
|
569
|
+
```
|
package/docs/crewai.md
CHANGED
|
@@ -33,9 +33,13 @@ from crewai import Agent, Crew, Task
|
|
|
33
33
|
from crewai.mcp import MCPServerStdio
|
|
34
34
|
from crewai.mcp.filters import create_static_tool_filter
|
|
35
35
|
|
|
36
|
+
# `metrik-mcp` is the executable this package publishes. It resolves anywhere
|
|
37
|
+
# `@absol-labs/agent` is installed (npm/pnpm link it into node_modules/.bin) and
|
|
38
|
+
# via `npx --package @absol-labs/agent metrik-mcp`. The old default was
|
|
39
|
+
# `pnpm mcp:stdio`, which only ever resolved inside the agent repository.
|
|
36
40
|
metrik_mcp = MCPServerStdio(
|
|
37
|
-
command="
|
|
38
|
-
args=[
|
|
41
|
+
command="metrik-mcp",
|
|
42
|
+
args=[],
|
|
39
43
|
env={
|
|
40
44
|
"METRIK_AGENT_RPC_URL": "https://base-sepolia.example.invalid",
|
|
41
45
|
"METRIK_AGENT_ESCROW": "0x0000000000000000000000000000000000000000",
|
package/docs/eliza.md
CHANGED
|
@@ -50,6 +50,7 @@ runnable-shaped example lives in
|
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
52
|
import { http } from "viem";
|
|
53
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
53
54
|
import { baseSepolia } from "viem/chains";
|
|
54
55
|
import { createMetrikElizaPlugin } from "@absol-labs/agent/eliza";
|
|
55
56
|
import { createWalletBackedAgentClient } from "@absol-labs/agent";
|
|
@@ -65,7 +66,11 @@ const { agentClient } = await createWalletBackedAgentClient(
|
|
|
65
66
|
escrow: process.env.METRIK_AGENT_ESCROW! as `0x${string}`,
|
|
66
67
|
},
|
|
67
68
|
},
|
|
68
|
-
{
|
|
69
|
+
{
|
|
70
|
+
injectedAccount: privateKeyToAccount(
|
|
71
|
+
process.env.METRIK_AGENT_PRIVATE_KEY as `0x${string}`,
|
|
72
|
+
),
|
|
73
|
+
},
|
|
69
74
|
);
|
|
70
75
|
|
|
71
76
|
const metrikPlugin = createMetrikElizaPlugin({ agentClient });
|
package/docs/mcp-hosted.md
CHANGED
|
@@ -4,8 +4,9 @@ The agent lane ships two MCP transports over the **same** tool set
|
|
|
4
4
|
(`hire_verified_service`, `check_stream_status`, `reclaim_unspent`, `list_streams`,
|
|
5
5
|
and the optional `prove_https_response`):
|
|
6
6
|
|
|
7
|
-
- **stdio** (`src/mcp/stdio.ts
|
|
8
|
-
|
|
7
|
+
- **stdio** (`src/mcp/stdio.ts`) — local dev, one process per caller. Started with
|
|
8
|
+
the published `metrik-mcp` executable (`npx --package @absol-labs/agent metrik-mcp`),
|
|
9
|
+
or `pnpm mcp:stdio` inside this repository.
|
|
9
10
|
- **Streamable HTTP** (`src/mcp/http.ts` + `src/mcp/http-server.ts`,
|
|
10
11
|
`pnpm mcp:http`) — a hosted, **multi-tenant**, per-caller-authenticated
|
|
11
12
|
endpoint suitable for deployment. This document covers the HTTP transport.
|
package/docs/quickstart.md
CHANGED
|
@@ -34,8 +34,8 @@ pnpm add @absol-labs/agent
|
|
|
34
34
|
`@absol-labs/agent` and its `@absol-labs/*` dependencies are published to the **public
|
|
35
35
|
npm registry** — no `.npmrc`, scope registry, or auth token is required to install.
|
|
36
36
|
|
|
37
|
-
**Runtime:** Node **20.
|
|
38
|
-
|
|
37
|
+
**Runtime:** Node **20.19+, 22 LTS or 24** (the package pins `engines.node` to
|
|
38
|
+
`>=20.19 <25`). The Coinbase AgentKit action reads decorator metadata via
|
|
39
39
|
`reflect-metadata`, which only exists in the **built** output (`tsc` emits decorator
|
|
40
40
|
metadata; `tsx`/esbuild does not) — so `import "reflect-metadata"` **before** importing
|
|
41
41
|
the package, and for the AgentKit path import from the built package, not raw `.ts`.
|
|
@@ -286,11 +286,23 @@ funds exactly.
|
|
|
286
286
|
|
|
287
287
|
---
|
|
288
288
|
|
|
289
|
-
## 8.
|
|
289
|
+
## 8. Five ways to integrate
|
|
290
290
|
|
|
291
|
-
Pick the surface that matches your stack — all
|
|
291
|
+
Pick the surface that matches your stack — all five sit on the **same** mandate-gated
|
|
292
292
|
client and business logic.
|
|
293
293
|
|
|
294
|
+
**(a0) The `metrik` CLI** — no code at all, and the only surface that runs the whole
|
|
295
|
+
loop (`discover -> open -> invoke -> watch -> close -> reclaim`):
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
npx --package @absol-labs/agent metrik discover --min-tier 2
|
|
299
|
+
npx --package @absol-labs/agent metrik open --service <serviceRef> \
|
|
300
|
+
--rate 0.000001 --budget 0.27 --duration 1h --dry-run
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Testnet-only, key read from `METRIK_AGENT_PRIVATE_KEY`, `--json` for scripting. Full
|
|
304
|
+
reference: [`docs/cli.md`](./cli.md).
|
|
305
|
+
|
|
294
306
|
**(a) SDK client directly** — the path used above:
|
|
295
307
|
|
|
296
308
|
```ts
|
|
@@ -319,7 +331,8 @@ const agentKit = await createMetrikAgentKit({
|
|
|
319
331
|
**(c) MCP** — local stdio for dev, hosted HTTP for deployment:
|
|
320
332
|
|
|
321
333
|
```bash
|
|
322
|
-
|
|
334
|
+
metrik-mcp # local: one process per caller — the published executable
|
|
335
|
+
pnpm mcp:stdio # same server, from inside this repository (src/mcp/stdio.ts)
|
|
323
336
|
pnpm mcp:http # hosted: multi-tenant Streamable HTTP, per-caller bearer auth
|
|
324
337
|
```
|
|
325
338
|
|