@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,381 @@
|
|
|
1
|
+
import type { CheckpointProof, StreamV2 } from "@absol-labs/sdk";
|
|
2
|
+
|
|
3
|
+
import { formatDuration, formatUsdc } from "./amounts.js";
|
|
4
|
+
import {
|
|
5
|
+
attesterDisclosure,
|
|
6
|
+
operatorAttestsOwnDelivery,
|
|
7
|
+
readAttesterSet,
|
|
8
|
+
type AttesterSet,
|
|
9
|
+
} from "./attesters.js";
|
|
10
|
+
import { describeError } from "./errors.js";
|
|
11
|
+
import {
|
|
12
|
+
describeEscapeHatch,
|
|
13
|
+
escapeHatchWindow,
|
|
14
|
+
formatTimestamp,
|
|
15
|
+
readChainNow,
|
|
16
|
+
type EscapeHatchWindow,
|
|
17
|
+
} from "./escape-hatch.js";
|
|
18
|
+
import type { CliReadClient } from "./deps.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether the accrual figures on this screen are FINAL.
|
|
22
|
+
*
|
|
23
|
+
* `reclaimableFor` is answered against a checkpoint's cumulative, and the
|
|
24
|
+
* escrow's own `reclaimWithProof` refuses until a checkpoint's `checkedAt`
|
|
25
|
+
* reaches the stream's stop time (`SettlementWindowOpen`). Between closing a
|
|
26
|
+
* stream and the first checkpoint that covers the close, the latest checkpoint
|
|
27
|
+
* therefore describes a SHORTER stream than the one that exists — so the
|
|
28
|
+
* reclaimable figure computed from it is larger than the amount that can
|
|
29
|
+
* actually be reclaimed, and shrinks when the covering checkpoint lands.
|
|
30
|
+
*
|
|
31
|
+
* A live acceptance run read `reclaimable: 1400` immediately after `close`, `0`
|
|
32
|
+
* two minutes later, and got `NothingToReclaim()` from the reclaim in between.
|
|
33
|
+
* Nobody reads "buyer reclaimable 0.0014 USDC" as "a provisional figure that
|
|
34
|
+
* may fall to zero" — they read it as money they have. So while this is
|
|
35
|
+
* pending, the CLI prints no settled-looking number at all.
|
|
36
|
+
*/
|
|
37
|
+
export interface SettlementCoverage {
|
|
38
|
+
/**
|
|
39
|
+
* The instant accrual stops: `closedAt`, or `expiresAt` once it has passed.
|
|
40
|
+
* `null` while the stream is still running, where there is nothing to cover.
|
|
41
|
+
*/
|
|
42
|
+
readonly stopAt: number | null;
|
|
43
|
+
/** How far the oracle has published. `null` when it could not be read. */
|
|
44
|
+
readonly coveredUpTo: number | null;
|
|
45
|
+
/** True when a stop time exists and no known checkpoint reaches it. */
|
|
46
|
+
readonly pending: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Coverage from the three facts that decide it. Pure, so the rule is testable
|
|
51
|
+
* without an RPC.
|
|
52
|
+
*
|
|
53
|
+
* Unknown coverage counts as PENDING, not as covered: the buyer-favouring error
|
|
54
|
+
* is to withhold a number we cannot stand behind, never to print one we cannot.
|
|
55
|
+
*/
|
|
56
|
+
export function settlementCoverage(
|
|
57
|
+
stream: Pick<StreamV2, "closedAt" | "expiresAt">,
|
|
58
|
+
latestCheckpointCheckedAt: number | null,
|
|
59
|
+
nowSeconds: number,
|
|
60
|
+
): SettlementCoverage {
|
|
61
|
+
const stopAt =
|
|
62
|
+
stream.closedAt > 0
|
|
63
|
+
? stream.closedAt
|
|
64
|
+
: nowSeconds >= stream.expiresAt
|
|
65
|
+
? stream.expiresAt
|
|
66
|
+
: null;
|
|
67
|
+
const coveredUpTo =
|
|
68
|
+
latestCheckpointCheckedAt !== null && latestCheckpointCheckedAt > 0
|
|
69
|
+
? latestCheckpointCheckedAt
|
|
70
|
+
: null;
|
|
71
|
+
return {
|
|
72
|
+
stopAt,
|
|
73
|
+
coveredUpTo,
|
|
74
|
+
pending: stopAt !== null && (coveredUpTo === null || coveredUpTo < stopAt),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A stream as the CLI reports it.
|
|
80
|
+
*
|
|
81
|
+
* The accrual figures come from the ORACLE's checkpoint proof, not from the
|
|
82
|
+
* stream tuple. `StreamEscrowV2.getStream` reports `claimedCumulative = 0` and
|
|
83
|
+
* `settledCumulative = 0` for a stream's whole active life — `settledCumulative`
|
|
84
|
+
* is written by `reclaim` — so a status built from stream fields would print
|
|
85
|
+
* "earned: 0" for a stream that had been delivering for an hour.
|
|
86
|
+
*/
|
|
87
|
+
export interface StreamReport {
|
|
88
|
+
readonly streamId: `0x${string}`;
|
|
89
|
+
readonly stream: StreamV2;
|
|
90
|
+
/** `null` until the oracle has published a checkpoint covering this stream. */
|
|
91
|
+
readonly proof: CheckpointProof | null;
|
|
92
|
+
/** Why there is no proof yet, when applicable. */
|
|
93
|
+
readonly proofUnavailable: string | null;
|
|
94
|
+
readonly claimable: bigint | null;
|
|
95
|
+
readonly reclaimable: bigint | null;
|
|
96
|
+
readonly protocolFeeBps: number | null;
|
|
97
|
+
/**
|
|
98
|
+
* When the buyer's no-proof escape hatch opens, and whether it already has.
|
|
99
|
+
* `null` only when the escrow would not answer.
|
|
100
|
+
*
|
|
101
|
+
* Without this a buyer had no command that could tell them "the proof path is
|
|
102
|
+
* blocked, but the no-proof path opens at T+grace" — the two facts they need
|
|
103
|
+
* together when the oracle has stopped answering.
|
|
104
|
+
*/
|
|
105
|
+
readonly escapeHatch: EscapeHatchWindow | null;
|
|
106
|
+
/**
|
|
107
|
+
* How far the oracle has published (`latestCheckpointCheckedAt`). A stale
|
|
108
|
+
* value beside a blocked reclaim IS the explanation for the blocked reclaim.
|
|
109
|
+
*/
|
|
110
|
+
readonly latestCheckpointCheckedAt: number | null;
|
|
111
|
+
readonly nowSeconds: number;
|
|
112
|
+
/** `local` when the chain's clock could not be read and the caller's was used. */
|
|
113
|
+
readonly clockSource: "chain" | "local";
|
|
114
|
+
/** Whether `claimable` / `reclaimable` are final. See {@link SettlementCoverage}. */
|
|
115
|
+
readonly settlement: SettlementCoverage;
|
|
116
|
+
/**
|
|
117
|
+
* The escrow's oracle signer set, and therefore whether this stream's
|
|
118
|
+
* operator is also one of the parties attesting to its own delivery.
|
|
119
|
+
* `signers: null` means the set could not be read — which is UNKNOWN, not
|
|
120
|
+
* "no overlap".
|
|
121
|
+
*/
|
|
122
|
+
readonly attesters: AttesterSet;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function readStreamReport(
|
|
126
|
+
read: CliReadClient,
|
|
127
|
+
streamId: `0x${string}`,
|
|
128
|
+
nowSeconds: number,
|
|
129
|
+
): Promise<StreamReport> {
|
|
130
|
+
const stream = await read.getStreamV2(streamId);
|
|
131
|
+
|
|
132
|
+
// Timing is answered against the chain, because every deadline on this screen
|
|
133
|
+
// — expiry, and the escape hatch in particular — is compared against
|
|
134
|
+
// `block.timestamp` by the escrow, not against this machine's clock.
|
|
135
|
+
const now = await readChainNow(read, nowSeconds);
|
|
136
|
+
|
|
137
|
+
let proof: CheckpointProof | null = null;
|
|
138
|
+
let proofUnavailable: string | null = null;
|
|
139
|
+
try {
|
|
140
|
+
proof = await read.checkpointProof(streamId);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
proofUnavailable = describeError(error);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let claimable: bigint | null = null;
|
|
146
|
+
let reclaimable: bigint | null = null;
|
|
147
|
+
if (proof !== null) {
|
|
148
|
+
[claimable, reclaimable] = await Promise.all([
|
|
149
|
+
read.claimableForV2(streamId, proof.cumulativeAmount),
|
|
150
|
+
read.reclaimableForV2(streamId, proof.cumulativeAmount),
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let protocolFeeBps: number | null = null;
|
|
155
|
+
try {
|
|
156
|
+
protocolFeeBps = await read.protocolFeeBps();
|
|
157
|
+
} catch {
|
|
158
|
+
protocolFeeBps = null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let escapeHatch: EscapeHatchWindow | null = null;
|
|
162
|
+
try {
|
|
163
|
+
escapeHatch = escapeHatchWindow(
|
|
164
|
+
stream,
|
|
165
|
+
await read.escapeGraceSeconds(),
|
|
166
|
+
now.seconds,
|
|
167
|
+
);
|
|
168
|
+
} catch {
|
|
169
|
+
escapeHatch = null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let latestCheckpointCheckedAt: number | null = null;
|
|
173
|
+
try {
|
|
174
|
+
latestCheckpointCheckedAt = await read.latestCheckpointCheckedAt();
|
|
175
|
+
} catch {
|
|
176
|
+
latestCheckpointCheckedAt = null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const settlement = settlementCoverage(
|
|
180
|
+
stream,
|
|
181
|
+
latestCheckpointCheckedAt,
|
|
182
|
+
now.seconds,
|
|
183
|
+
);
|
|
184
|
+
const attesters = await readAttesterSet(read);
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
streamId,
|
|
188
|
+
stream,
|
|
189
|
+
proof,
|
|
190
|
+
proofUnavailable,
|
|
191
|
+
claimable,
|
|
192
|
+
reclaimable,
|
|
193
|
+
protocolFeeBps,
|
|
194
|
+
escapeHatch,
|
|
195
|
+
latestCheckpointCheckedAt,
|
|
196
|
+
nowSeconds: now.seconds,
|
|
197
|
+
clockSource: now.source,
|
|
198
|
+
settlement,
|
|
199
|
+
attesters,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function streamReportJson(report: StreamReport): unknown {
|
|
204
|
+
const { stream, proof } = report;
|
|
205
|
+
return {
|
|
206
|
+
streamId: report.streamId,
|
|
207
|
+
// StreamEscrowV2 has exactly two states. There is no "paused".
|
|
208
|
+
status: stream.status,
|
|
209
|
+
reclaimed: stream.reclaimed,
|
|
210
|
+
buyer: stream.buyer,
|
|
211
|
+
operator: stream.operator,
|
|
212
|
+
serviceRef: stream.serviceRef,
|
|
213
|
+
deposit: stream.deposit,
|
|
214
|
+
ratePerSecond: stream.ratePerSecond,
|
|
215
|
+
claimedCumulative: stream.claimedCumulative,
|
|
216
|
+
settledCumulative: stream.settledCumulative,
|
|
217
|
+
feesPaid: stream.feesPaid,
|
|
218
|
+
openedAt: stream.openedAt,
|
|
219
|
+
expiresAt: stream.expiresAt,
|
|
220
|
+
closedAt: stream.closedAt,
|
|
221
|
+
protocolFeeBps: report.protocolFeeBps,
|
|
222
|
+
verifiedAccrual:
|
|
223
|
+
proof === null
|
|
224
|
+
? null
|
|
225
|
+
: {
|
|
226
|
+
cumulativeAmount: proof.cumulativeAmount,
|
|
227
|
+
checkpointId: proof.checkpointId,
|
|
228
|
+
checkedAt: proof.checkedAt,
|
|
229
|
+
root: proof.root,
|
|
230
|
+
},
|
|
231
|
+
verifiedAccrualUnavailable: report.proofUnavailable,
|
|
232
|
+
// `null` while settlement is pending. A consumer that reads this field gets
|
|
233
|
+
// either an amount that can actually be obtained or nothing — never a
|
|
234
|
+
// settled-looking number derived from a checkpoint that predates the
|
|
235
|
+
// stream's stop time. The superseded figure is still available, explicitly
|
|
236
|
+
// labelled, under `settlement.provisionalReclaimable`.
|
|
237
|
+
claimable: report.claimable,
|
|
238
|
+
reclaimable: report.settlement.pending ? null : report.reclaimable,
|
|
239
|
+
settlement: {
|
|
240
|
+
pending: report.settlement.pending,
|
|
241
|
+
stopAt: report.settlement.stopAt,
|
|
242
|
+
coveredUpTo: report.settlement.coveredUpTo,
|
|
243
|
+
provisionalReclaimable: report.settlement.pending
|
|
244
|
+
? report.reclaimable
|
|
245
|
+
: null,
|
|
246
|
+
provisionalClaimable: report.settlement.pending ? report.claimable : null,
|
|
247
|
+
},
|
|
248
|
+
// Who attests. `operatorIsOracleSigner: null` means the signer set could
|
|
249
|
+
// not be read — UNKNOWN, never "no overlap".
|
|
250
|
+
oracleSigners: report.attesters.signers,
|
|
251
|
+
oracleSignersUnavailable: report.attesters.unavailable,
|
|
252
|
+
operatorIsOracleSigner: operatorAttestsOwnDelivery(
|
|
253
|
+
report.attesters,
|
|
254
|
+
stream.operator,
|
|
255
|
+
),
|
|
256
|
+
latestCheckpointCheckedAt: report.latestCheckpointCheckedAt,
|
|
257
|
+
escapeHatch:
|
|
258
|
+
report.escapeHatch === null
|
|
259
|
+
? null
|
|
260
|
+
: {
|
|
261
|
+
escapeGraceSeconds: report.escapeHatch.graceSeconds,
|
|
262
|
+
readyAt: report.escapeHatch.readyAt,
|
|
263
|
+
ready: report.escapeHatch.ready,
|
|
264
|
+
secondsUntilReady: report.escapeHatch.secondsRemaining,
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function streamReportRows(
|
|
270
|
+
report: StreamReport,
|
|
271
|
+
): (readonly [string, string])[] {
|
|
272
|
+
const { stream, proof } = report;
|
|
273
|
+
const rows: (readonly [string, string])[] = [
|
|
274
|
+
[
|
|
275
|
+
"status",
|
|
276
|
+
stream.reclaimed ? `${stream.status} (reclaimed)` : stream.status,
|
|
277
|
+
],
|
|
278
|
+
["buyer", stream.buyer],
|
|
279
|
+
["operator", stream.operator],
|
|
280
|
+
["serviceRef", stream.serviceRef],
|
|
281
|
+
["deposit", formatUsdc(stream.deposit)],
|
|
282
|
+
["rate", `${formatUsdc(stream.ratePerSecond)} / second`],
|
|
283
|
+
["opened", formatTimestamp(stream.openedAt)],
|
|
284
|
+
["expires", describeDeadline(stream.expiresAt, report.nowSeconds)],
|
|
285
|
+
];
|
|
286
|
+
if (stream.closedAt > 0) {
|
|
287
|
+
rows.push(["closed", formatTimestamp(stream.closedAt)]);
|
|
288
|
+
}
|
|
289
|
+
rows.push([
|
|
290
|
+
"verified accrual",
|
|
291
|
+
proof === null
|
|
292
|
+
? `not checkpointed yet (${report.proofUnavailable ?? "no proof"})`
|
|
293
|
+
: `${formatUsdc(proof.cumulativeAmount)} @ checkpoint ${proof.checkpointId} (${formatTimestamp(Number(proof.checkedAt))})`,
|
|
294
|
+
]);
|
|
295
|
+
// `claim` accepts ANY retained root, so the operator's balance is obtainable
|
|
296
|
+
// now — it is merely not FINAL until the covering checkpoint lands, and it
|
|
297
|
+
// can only grow. `reclaim` requires the LATEST root to reach the stop time,
|
|
298
|
+
// so the buyer's figure is not obtainable at all while that is pending: it is
|
|
299
|
+
// withheld rather than printed as money the buyer does not have.
|
|
300
|
+
rows.push([
|
|
301
|
+
"operator claimable",
|
|
302
|
+
report.claimable === null
|
|
303
|
+
? "-"
|
|
304
|
+
: report.settlement.pending
|
|
305
|
+
? `${formatUsdc(report.claimable)} so far — not final until the checkpoint covering the stop time lands`
|
|
306
|
+
: formatUsdc(report.claimable),
|
|
307
|
+
]);
|
|
308
|
+
rows.push([
|
|
309
|
+
"buyer reclaimable",
|
|
310
|
+
report.settlement.pending
|
|
311
|
+
? describePendingSettlement(report.settlement)
|
|
312
|
+
: report.reclaimable === null
|
|
313
|
+
? "-"
|
|
314
|
+
: formatUsdc(report.reclaimable),
|
|
315
|
+
]);
|
|
316
|
+
rows.push(["fees paid", formatUsdc(stream.feesPaid)]);
|
|
317
|
+
rows.push([
|
|
318
|
+
"oracle checkpoint",
|
|
319
|
+
report.latestCheckpointCheckedAt === null
|
|
320
|
+
? "-"
|
|
321
|
+
: report.latestCheckpointCheckedAt <= 0
|
|
322
|
+
? "none posted yet"
|
|
323
|
+
: `latest covers up to ${formatTimestamp(report.latestCheckpointCheckedAt)}`,
|
|
324
|
+
]);
|
|
325
|
+
// The no-proof path. Printed for every stream, not only blocked ones: a buyer
|
|
326
|
+
// should learn that this exists before they need it.
|
|
327
|
+
rows.push([
|
|
328
|
+
"escape hatch",
|
|
329
|
+
report.escapeHatch === null
|
|
330
|
+
? "-"
|
|
331
|
+
: `${describeEscapeHatch(report.escapeHatch)} — no-proof reclaim, forfeits the operator's unclaimed earnings`,
|
|
332
|
+
]);
|
|
333
|
+
// The same row, read from the seller's side. `reclaimUnverified` pays the
|
|
334
|
+
// buyer the WHOLE remaining deposit with no proof, so an operator who has
|
|
335
|
+
// earned but not claimed loses it the moment that window opens. The window is
|
|
336
|
+
// `expiresAt + escapeGraceSeconds` and the grace is immutable on the live
|
|
337
|
+
// escrow, so the only thing an operator can do about it is claim in time —
|
|
338
|
+
// which they cannot do if nothing tells them the deadline exists.
|
|
339
|
+
if (
|
|
340
|
+
report.escapeHatch !== null &&
|
|
341
|
+
report.claimable !== null &&
|
|
342
|
+
report.claimable > 0n
|
|
343
|
+
) {
|
|
344
|
+
rows.push([
|
|
345
|
+
"operator deadline",
|
|
346
|
+
report.escapeHatch.ready
|
|
347
|
+
? `${formatUsdc(report.claimable)} is unclaimed and the buyer's no-proof reclaim is ALREADY open — it would take this amount from the operator`
|
|
348
|
+
: `${formatUsdc(report.claimable)} is unclaimed; \`metrik claim\` it before ${formatTimestamp(report.escapeHatch.readyAt)}, after which a buyer's no-proof reclaim can take it`,
|
|
349
|
+
]);
|
|
350
|
+
}
|
|
351
|
+
if (report.protocolFeeBps !== null) {
|
|
352
|
+
rows.push([
|
|
353
|
+
"protocol fee",
|
|
354
|
+
`${report.protocolFeeBps} bps (read from the escrow)`,
|
|
355
|
+
]);
|
|
356
|
+
}
|
|
357
|
+
return rows;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* What is being waited for, in the place a number used to be. Names the two
|
|
362
|
+
* timestamps whose ordering decides it, so the reader can see the wait end.
|
|
363
|
+
*/
|
|
364
|
+
function describePendingSettlement(settlement: SettlementCoverage): string {
|
|
365
|
+
const stopAt =
|
|
366
|
+
settlement.stopAt === null ? "unknown" : formatTimestamp(settlement.stopAt);
|
|
367
|
+
const covered =
|
|
368
|
+
settlement.coveredUpTo === null
|
|
369
|
+
? "unknown (the escrow would not answer)"
|
|
370
|
+
: formatTimestamp(settlement.coveredUpTo);
|
|
371
|
+
return `pending final checkpoint — the stream stopped at ${stopAt}, the oracle has published only to ${covered}. Reclaim reverts until a checkpoint reaches the stop time (~15-22s apart).`;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function describeDeadline(expiresAt: number, nowSeconds: number): string {
|
|
375
|
+
const remaining = expiresAt - nowSeconds;
|
|
376
|
+
const suffix =
|
|
377
|
+
remaining > 0
|
|
378
|
+
? `in ${formatDuration(remaining)}`
|
|
379
|
+
: `${formatDuration(-remaining)} ago`;
|
|
380
|
+
return `${formatTimestamp(expiresAt)} — ${suffix}`;
|
|
381
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CliError } from "./errors.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* How long any single READ is allowed to take before the CLI gives up on it.
|
|
5
|
+
*
|
|
6
|
+
* A live acceptance run found `status` calls that ran past two minutes with no
|
|
7
|
+
* output, and a `close` that took 8s once and 142s the next time. An unbounded
|
|
8
|
+
* wait is indistinguishable from a wedged command, and the only honest way to
|
|
9
|
+
* tell them apart is to stop waiting and say so.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately generous: Base Sepolia's public endpoint is load-balanced and
|
|
12
|
+
* occasionally slow, and a timeout that fires on a merely-slow node would turn
|
|
13
|
+
* a working command into a spurious failure.
|
|
14
|
+
*/
|
|
15
|
+
export const READ_TIMEOUT_MS = 20_000;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Per-RPC-call budget handed to viem's HTTP transport, with a bounded retry
|
|
19
|
+
* count so one slow endpoint cannot multiply into minutes of silence. viem
|
|
20
|
+
* retries with exponential backoff, so `retryCount` is a multiplier on this.
|
|
21
|
+
*/
|
|
22
|
+
export const RPC_TIMEOUT_MS = 15_000;
|
|
23
|
+
export const RPC_RETRY_COUNT = 2;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Bound a read.
|
|
27
|
+
*
|
|
28
|
+
* ONLY safe for reads. A broadcast must never be abandoned on a timer: the
|
|
29
|
+
* transaction may already be in the mempool, and a CLI that walked away from it
|
|
30
|
+
* would report a failure for something that in fact settled. Write paths keep
|
|
31
|
+
* waiting, and the heartbeat (`./progress.ts`) is what makes that wait legible.
|
|
32
|
+
*/
|
|
33
|
+
export async function withReadTimeout<T>(
|
|
34
|
+
operation: () => Promise<T>,
|
|
35
|
+
label: string,
|
|
36
|
+
timeoutMs: number = READ_TIMEOUT_MS,
|
|
37
|
+
): Promise<T> {
|
|
38
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
39
|
+
const pending = operation();
|
|
40
|
+
// Attached before the race so an eventual rejection of the abandoned promise
|
|
41
|
+
// is never an unhandled rejection that takes the process down.
|
|
42
|
+
pending.catch(() => {});
|
|
43
|
+
try {
|
|
44
|
+
return await Promise.race([
|
|
45
|
+
pending,
|
|
46
|
+
new Promise<never>((_resolve, reject) => {
|
|
47
|
+
timer = setTimeout(() => {
|
|
48
|
+
reject(
|
|
49
|
+
new CliError(
|
|
50
|
+
`${label} did not answer within ${Math.round(timeoutMs / 1000)}s`,
|
|
51
|
+
{
|
|
52
|
+
exitCode: 4,
|
|
53
|
+
hint: "the endpoint is unreachable or very slow — retry, or point --rpc-url / --oracle-url at a different one.",
|
|
54
|
+
},
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
}, timeoutMs);
|
|
58
|
+
timer.unref?.();
|
|
59
|
+
}),
|
|
60
|
+
]);
|
|
61
|
+
} finally {
|
|
62
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The published version of this package, read from its own manifest.
|
|
5
|
+
*
|
|
6
|
+
* Kept in its own module because both the runner (`--version`) and the MCP
|
|
7
|
+
* config printer need it, and the printer is a command the runner imports — so
|
|
8
|
+
* reading it from `run.ts` would be a cycle.
|
|
9
|
+
*
|
|
10
|
+
* Falls back rather than throwing: a CLI that cannot state its version should
|
|
11
|
+
* still run.
|
|
12
|
+
*/
|
|
13
|
+
export function packageVersion(): string {
|
|
14
|
+
try {
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const manifest = require("../../package.json") as { version?: string };
|
|
17
|
+
return manifest.version ?? "0.0.0";
|
|
18
|
+
} catch {
|
|
19
|
+
return "0.0.0";
|
|
20
|
+
}
|
|
21
|
+
}
|