@skeinlabs/sdk 0.0.0-stage → 0.2.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.
@@ -0,0 +1,358 @@
1
+ // ../telegraph/src/config.ts
2
+ var TESTNET = {
3
+ nodeUrl: "https://devnode.telegraphprotocol.com",
4
+ caip2: "eip155:11155111",
5
+ chainId: 11155111,
6
+ diamond: "0x3624EB012B5E998BB57B6bE6C9Cfc9c4bc5eb99b",
7
+ usdc: "0xcc194e1a843B658526D0D5C431f670028582Ae17",
8
+ rpcUrl: "https://ethereum-sepolia-rpc.publicnode.com"
9
+ };
10
+ function networkFromEnv(env = process.env) {
11
+ const chainId = env.TELEGRAPH_CHAIN_ID ? Number(env.TELEGRAPH_CHAIN_ID) : TESTNET.chainId;
12
+ return {
13
+ nodeUrl: env.TELEGRAPH_NODE_URL || TESTNET.nodeUrl,
14
+ caip2: env.TELEGRAPH_CAIP2 || `eip155:${chainId}`,
15
+ chainId,
16
+ diamond: env.TELEGRAPH_DIAMOND || TESTNET.diamond,
17
+ usdc: env.TELEGRAPH_USDC || TESTNET.usdc,
18
+ rpcUrl: env.RPC_URL || TESTNET.rpcUrl
19
+ };
20
+ }
21
+ var SEASON_START = Date.parse("2026-11-01T00:00:00Z");
22
+
23
+ // ../telegraph/src/escrow.ts
24
+ var WINDOW_BEFORE = 5;
25
+ var WINDOW_AFTER = 100;
26
+ function b64decode(s) {
27
+ return Buffer.from(s, "base64").toString("utf8");
28
+ }
29
+ function b64encode(s) {
30
+ return Buffer.from(s, "utf8").toString("base64");
31
+ }
32
+ function readChallenge(headerValue, body) {
33
+ const candidates = [];
34
+ if (headerValue) {
35
+ try {
36
+ candidates.push(JSON.parse(b64decode(headerValue)));
37
+ } catch {
38
+ }
39
+ }
40
+ candidates.push(body);
41
+ for (const c of candidates) {
42
+ const accepts = c?.accepts;
43
+ if (!Array.isArray(accepts)) continue;
44
+ const hit = accepts.find(
45
+ (a) => a?.scheme === "escrow" && typeof a?.extra?.message === "string" && typeof a?.payTo === "string"
46
+ );
47
+ if (hit) {
48
+ const assets = accepts.map((a) => a?.asset).filter((x) => typeof x === "string");
49
+ return { escrow: hit, assets };
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ var TEMPLATE_HEADER = "Telegraph Escrow Payment";
55
+ var TEMPLATE_KEYS = ["Wallet", "Pay To", "Network", "Amount", "Resource", "Nonce", "Valid After", "Valid Before"];
56
+ var PLACEHOLDERS = {
57
+ Wallet: "{from}",
58
+ Nonce: "{nonce}",
59
+ "Valid After": "{validAfter}",
60
+ "Valid Before": "{validBefore}"
61
+ };
62
+ function parseTemplate(template) {
63
+ const lines = template.split("\n");
64
+ if (lines[0] !== TEMPLATE_HEADER || lines[1] !== "" || lines.length !== 2 + TEMPLATE_KEYS.length) {
65
+ throw new ChallengeMismatchError("escrow template does not have the documented layout");
66
+ }
67
+ const values = {};
68
+ TEMPLATE_KEYS.forEach((key, i) => {
69
+ const line = lines[2 + i];
70
+ const prefix = `${key}: `;
71
+ if (!line.startsWith(prefix)) throw new ChallengeMismatchError(`escrow template line ${3 + i} should be "${key}: \u2026"`);
72
+ values[key] = line.slice(prefix.length);
73
+ });
74
+ for (const [key, ph] of Object.entries(PLACEHOLDERS)) {
75
+ if (values[key] !== ph) throw new ChallengeMismatchError(`escrow template ${key} must be ${ph}`);
76
+ }
77
+ const fixed = TEMPLATE_KEYS.filter((k) => !(k in PLACEHOLDERS)).map((k) => values[k]);
78
+ if (fixed.some((v) => /[{}]/.test(v))) throw new ChallengeMismatchError("escrow template has an unexpected placeholder");
79
+ const amount = /^(\d{1,12}) uUSDC$/.exec(values.Amount);
80
+ if (!amount) throw new ChallengeMismatchError('escrow template Amount is not "N uUSDC"');
81
+ if (!/^\/v1\/ask(\/[\w.-]+)?$/.test(values.Resource)) throw new ChallengeMismatchError(`escrow template Resource ${values.Resource} is not an ask`);
82
+ return { payTo: values["Pay To"], network: values.Network, amountUusdc: amount[1], resource: values.Resource };
83
+ }
84
+ function fillTemplate(template, auth) {
85
+ return template.split("{from}").join(auth.from).split("{nonce}").join(auth.nonce).split("{validAfter}").join(auth.validAfter).split("{validBefore}").join(auth.validBefore);
86
+ }
87
+ function buildAuthorization(args) {
88
+ return {
89
+ // The node lowercases the wallet before checking the signature.
90
+ from: args.from.toLowerCase(),
91
+ to: args.to,
92
+ value: args.value,
93
+ validAfter: String(args.nowSec - WINDOW_BEFORE),
94
+ validBefore: String(args.nowSec + WINDOW_AFTER),
95
+ nonce: args.nonce
96
+ };
97
+ }
98
+ var ChallengeMismatchError = class extends Error {
99
+ code = "CHALLENGE_MISMATCH";
100
+ };
101
+ async function signEscrowPayment(args) {
102
+ const { accept, expected } = args;
103
+ const t = parseTemplate(accept.extra.message);
104
+ const same = (a, b) => a.toLowerCase() === b.toLowerCase();
105
+ if (accept.network !== expected.caip2 || t.network !== expected.caip2) {
106
+ throw new ChallengeMismatchError(`challenge network ${accept.network} / template ${t.network}, expected ${expected.caip2}`);
107
+ }
108
+ if (!same(accept.payTo, expected.diamond) || !same(t.payTo, expected.diamond)) {
109
+ throw new ChallengeMismatchError(`challenge pays ${accept.payTo} / template ${t.payTo}, expected the Diamond ${expected.diamond}`);
110
+ }
111
+ if (accept.amount !== void 0 && accept.amount !== t.amountUusdc) {
112
+ throw new ChallengeMismatchError(`challenge amount ${accept.amount} disagrees with the template's ${t.amountUusdc}`);
113
+ }
114
+ if (accept.extra.resource !== void 0 && accept.extra.resource !== t.resource) {
115
+ throw new ChallengeMismatchError(`challenge resource ${accept.extra.resource} disagrees with the template's ${t.resource}`);
116
+ }
117
+ if (expected.resource !== void 0 && t.resource !== expected.resource) {
118
+ throw new ChallengeMismatchError(`template resource ${t.resource}, expected ${expected.resource}`);
119
+ }
120
+ if (expected.usdc) {
121
+ const wrong = (args.assets ?? []).find((a) => !same(a, expected.usdc));
122
+ if (wrong) throw new ChallengeMismatchError(`challenge names asset ${wrong}, expected USDC ${expected.usdc}`);
123
+ }
124
+ const amountUusdc = Number(t.amountUusdc);
125
+ if (!(Number.isSafeInteger(amountUusdc) && amountUusdc <= expected.maxUusdc)) {
126
+ throw new ChallengeMismatchError(`challenge asks ${t.amountUusdc} uUSDC, cap ${expected.maxUusdc}`);
127
+ }
128
+ const authorization = buildAuthorization({
129
+ from: args.signer.address,
130
+ to: expected.diamond,
131
+ value: t.amountUusdc,
132
+ nowSec: args.nowSec,
133
+ nonce: args.nonce
134
+ });
135
+ const signature = await args.signer.signMessage(fillTemplate(accept.extra.message, authorization));
136
+ const header = b64encode(
137
+ JSON.stringify({ x402Version: 2, scheme: "escrow", network: accept.network, payload: { signature, authorization } })
138
+ );
139
+ return { header, authorization, amountUusdc };
140
+ }
141
+ function decodeReceipt(headerValue) {
142
+ if (!headerValue) return null;
143
+ try {
144
+ const r = JSON.parse(b64decode(headerValue));
145
+ return typeof r?.receipt_hash === "string" ? r : null;
146
+ } catch {
147
+ return null;
148
+ }
149
+ }
150
+ function receiptProblems(r, signed) {
151
+ if (!r) return ["no receipt"];
152
+ const out = [];
153
+ if (!/^0x[0-9a-fA-F]{64}$/.test(r.receipt_hash)) out.push("receipt_hash is not 32 bytes of hex");
154
+ if (r.rail !== "escrow") out.push(`receipt rail ${r.rail}, expected escrow`);
155
+ if (typeof r.payer !== "string" || r.payer.toLowerCase() !== signed.payer.toLowerCase()) out.push(`receipt payer ${r.payer}, expected ${signed.payer}`);
156
+ if (String(r.amount_uusdc) !== String(signed.amountUusdc)) out.push(`receipt amount ${r.amount_uusdc}, signed ${signed.amountUusdc}`);
157
+ if (signed.minerId && r.miner !== void 0 && String(r.miner) !== signed.minerId) out.push(`receipt miner ${r.miner}, pinned ${signed.minerId}`);
158
+ return out;
159
+ }
160
+
161
+ // ../telegraph/src/client.ts
162
+ import { privateKeyToAccount } from "viem/accounts";
163
+ var PreSeasonError = class extends Error {
164
+ code = "PRE_SEASON";
165
+ };
166
+ function viemSigner(privateKey) {
167
+ const account = privateKeyToAccount(privateKey);
168
+ return { address: account.address, signMessage: (message) => account.signMessage({ message }) };
169
+ }
170
+ var RETRYABLE = /* @__PURE__ */ new Set(["timeout", "network", "server_error", "refused"]);
171
+ var TelegraphClient = class {
172
+ constructor(opts) {
173
+ this.opts = opts;
174
+ this.fetch = opts.fetch ?? globalThis.fetch;
175
+ this.now = opts.now ?? Date.now;
176
+ this.nonce = opts.nonce ?? (() => crypto.randomUUID());
177
+ }
178
+ fetch;
179
+ now;
180
+ nonce;
181
+ url(path) {
182
+ return `${this.opts.network.nodeUrl.replace(/\/$/, "")}${path}`;
183
+ }
184
+ async getJson(path) {
185
+ const r = await this.send(path, { method: "GET" }, 15e3);
186
+ if (typeof r === "string") throw new Error(`GET ${path}: ${r}`);
187
+ if (r.status < 200 || r.status >= 300) throw new Error(`GET ${path}: HTTP ${r.status}`);
188
+ return JSON.parse(r.text);
189
+ }
190
+ /**
191
+ * One request with a referenced timer (unlike AbortSignal.timeout(), it keeps
192
+ * a worker or CLI alive while an upstream call hangs) that also covers
193
+ * reading the body, so a slow body cannot hang a verdict.
194
+ */
195
+ async send(path, init, timeoutMs) {
196
+ const ctl = new AbortController();
197
+ const timer = setTimeout(() => ctl.abort(new DOMException("upstream timed out", "TimeoutError")), timeoutMs);
198
+ try {
199
+ const res = await this.fetch(this.url(path), { ...init, signal: ctl.signal });
200
+ const text = (await res.text()).slice(0, 1e6);
201
+ return { status: res.status, headers: res.headers, text };
202
+ } catch (e) {
203
+ const err = e;
204
+ return err.name === "TimeoutError" || err.name === "AbortError" ? "timeout" : "network";
205
+ } finally {
206
+ clearTimeout(timer);
207
+ }
208
+ }
209
+ listMiners(q = {}) {
210
+ const p = new URLSearchParams();
211
+ if (q.intent) p.set("intent", q.intent);
212
+ if (q.status) p.set("status", q.status);
213
+ if (q.limit) p.set("limit", String(q.limit));
214
+ return this.getJson(`/api/miners${p.size ? `?${p}` : ""}`);
215
+ }
216
+ getIntents() {
217
+ return this.getJson("/engine/v1/intents");
218
+ }
219
+ getIntent(intent) {
220
+ return this.getJson(`/engine/v1/intents/${encodeURIComponent(intent)}`);
221
+ }
222
+ getSignal(signalHash) {
223
+ return this.getJson(`/engine/v1/signal/${encodeURIComponent(signalHash)}`);
224
+ }
225
+ /**
226
+ * Looks a paid answer's signal_hash up on the node, the network's own record
227
+ * of it. "verified": the node has the signal (and, when it says which miner,
228
+ * that miner is the one paid); "missing": the node has no such signal;
229
+ * "mismatch": it does, from another miner; "unchecked": the node did not answer.
230
+ */
231
+ async verifySignal(signalHash, expect = {}) {
232
+ if (!signalHash || !/^0x[0-9a-fA-F]{64}$/.test(signalHash)) return "missing";
233
+ const r = await this.send(`/engine/v1/signal/${encodeURIComponent(signalHash)}`, { method: "GET" }, 1e4);
234
+ if (typeof r === "string" || r.status >= 500) return "unchecked";
235
+ if (r.status === 404) return "missing";
236
+ if (r.status < 200 || r.status >= 300) return "unchecked";
237
+ const body = parseJson(r.text);
238
+ const miner = body?.miner_id ?? body?.minerId;
239
+ if (expect.minerId && miner !== void 0 && miner !== null && String(miner) !== expect.minerId) return "mismatch";
240
+ return "verified";
241
+ }
242
+ /**
243
+ * Buys one answer on the escrow rail. Failures before payment (no answer to
244
+ * the unpaid probe, a 5xx) and an explicit 402 refusal of the payment are
245
+ * retried once with a fresh challenge and nonce: nothing was billed. A
246
+ * failure after X-PAYMENT was sent is never retried, because the node may
247
+ * have billed it; it comes back with paidUusdc set and billing "unknown".
248
+ * maxUusdc lowers the client-wide cap for this one call.
249
+ */
250
+ async ask(req, opts) {
251
+ const started = this.now();
252
+ let last = null;
253
+ for (let attempt = 1; attempt <= 2; attempt++) {
254
+ const out = await this.attempt(req, opts, started, attempt);
255
+ if (out.ok || out.billing === "unknown" || !RETRYABLE.has(out.kind)) return out;
256
+ last = out;
257
+ }
258
+ return last;
259
+ }
260
+ async attempt(req, opts, started, attempts) {
261
+ const path = opts.minerId ? `/engine/v1/ask/${encodeURIComponent(opts.minerId)}` : "/engine/v1/ask";
262
+ const body = JSON.stringify(req);
263
+ const fail = (kind, error, httpStatus, paidUusdc = 0) => ({
264
+ ok: false,
265
+ kind,
266
+ error,
267
+ durationMs: this.now() - started,
268
+ attempts,
269
+ paidUusdc,
270
+ billing: paidUusdc > 0 ? "unknown" : "none",
271
+ ...httpStatus !== void 0 ? { httpStatus } : {}
272
+ });
273
+ const post = (headers) => this.send(path, { method: "POST", headers: { "content-type": "application/json", ...headers }, body }, opts.timeoutMs);
274
+ const challenge = await post({});
275
+ if (typeof challenge === "string") return fail(challenge, `unpaid request: ${challenge}`);
276
+ if (challenge.status !== 402) {
277
+ const msg = challenge.text.slice(0, 2e3);
278
+ let err;
279
+ try {
280
+ err = JSON.parse(msg).error;
281
+ } catch {
282
+ }
283
+ const kind = challenge.status < 300 || err === "no_compatible_miner" || err === "invalid_params" || err === "no_input_schema" || err === "intent_required" ? "incompatible" : challenge.status >= 500 ? "server_error" : "client_error";
284
+ return fail(kind, `expected 402, got ${challenge.status}: ${msg}`, challenge.status);
285
+ }
286
+ const parsed = readChallenge(challenge.headers.get("payment-required"), parseJson(challenge.text));
287
+ if (!parsed) return fail("mismatch", "the node offered no escrow scheme", 402);
288
+ if (!this.opts.allowPreSeason && this.now() < SEASON_START) {
289
+ return fail("preseason", "paid calls are refused before the season opens (Nov 1 UTC)");
290
+ }
291
+ if (!this.opts.signer) return fail("mismatch", "no signer configured");
292
+ const maxUusdc = Math.min(this.opts.maxUusdcPerCall, opts.maxUusdc ?? Infinity);
293
+ let signed;
294
+ try {
295
+ signed = await signEscrowPayment({
296
+ accept: parsed.escrow,
297
+ assets: parsed.assets,
298
+ signer: this.opts.signer,
299
+ expected: {
300
+ caip2: this.opts.network.caip2,
301
+ diamond: this.opts.network.diamond,
302
+ usdc: this.opts.network.usdc,
303
+ maxUusdc,
304
+ // A pinned ask must be for that miner's resource, not another's.
305
+ ...opts.minerId ? { resource: `/v1/ask/${opts.minerId}` } : {}
306
+ },
307
+ nowSec: Math.floor(this.now() / 1e3),
308
+ nonce: this.nonce()
309
+ });
310
+ } catch (e) {
311
+ if (e instanceof ChallengeMismatchError) return fail("mismatch", e.message, 402);
312
+ throw e;
313
+ }
314
+ const paid = await post({ "X-PAYMENT": signed.header });
315
+ const sent = signed.amountUusdc;
316
+ if (typeof paid === "string") return fail(paid, `paid request: ${paid}`, void 0, sent);
317
+ if (paid.status === 402) {
318
+ const reason = parseJson(paid.text);
319
+ return fail("refused", `payment refused: ${reason?.reason ?? reason?.error ?? "no reason given"}`, 402);
320
+ }
321
+ if (paid.status < 200 || paid.status >= 300) {
322
+ return fail(paid.status >= 500 ? "server_error" : "client_error", `HTTP ${paid.status}: ${paid.text.slice(0, 2e3)}`, paid.status, sent);
323
+ }
324
+ const response = parseJson(paid.text);
325
+ if (!response || typeof response !== "object") return fail("server_error", "200 with a body that is not JSON", paid.status, sent);
326
+ const receipt = decodeReceipt(paid.headers.get("x-payment-response"));
327
+ const problems = receiptProblems(receipt, { payer: signed.authorization.from, amountUusdc: sent, ...opts.minerId ? { minerId: opts.minerId } : {} });
328
+ if (opts.minerId && response.miner_id !== void 0 && String(response.miner_id) !== opts.minerId) {
329
+ problems.push(`answered by miner ${response.miner_id}, pinned ${opts.minerId}`);
330
+ }
331
+ return {
332
+ ok: true,
333
+ response,
334
+ receipt,
335
+ receiptProblems: problems,
336
+ httpStatus: paid.status,
337
+ amountUusdc: sent,
338
+ durationMs: this.now() - started,
339
+ attempts
340
+ };
341
+ }
342
+ };
343
+ function parseJson(text) {
344
+ try {
345
+ return JSON.parse(text);
346
+ } catch {
347
+ return null;
348
+ }
349
+ }
350
+ export {
351
+ ChallengeMismatchError,
352
+ PreSeasonError,
353
+ TESTNET,
354
+ TelegraphClient,
355
+ networkFromEnv,
356
+ viemSigner
357
+ };
358
+ //# sourceMappingURL=telegraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../telegraph/src/config.ts","../../telegraph/src/escrow.ts","../../telegraph/src/client.ts"],"sourcesContent":["/**\n * Network settings. Defaults are the testnet values from the docs' Addresses\n * page (read 2026-10-30); every one can be overridden from the environment,\n * and the purchaser refuses a 402 challenge that disagrees with them.\n */\nexport interface NetworkConfig {\n nodeUrl: string;\n /** CAIP-2 id of the chain the escrow lives on, e.g. eip155:11155111. */\n caip2: string;\n chainId: number;\n diamond: string;\n usdc: string;\n rpcUrl: string;\n}\n\nexport const TESTNET: Readonly<NetworkConfig> = {\n nodeUrl: \"https://devnode.telegraphprotocol.com\",\n caip2: \"eip155:11155111\",\n chainId: 11155111,\n diamond: \"0x3624EB012B5E998BB57B6bE6C9Cfc9c4bc5eb99b\",\n usdc: \"0xcc194e1a843B658526D0D5C431f670028582Ae17\",\n rpcUrl: \"https://ethereum-sepolia-rpc.publicnode.com\",\n};\n\nexport function networkFromEnv(env: Record<string, string | undefined> = process.env): NetworkConfig {\n const chainId = env.TELEGRAPH_CHAIN_ID ? Number(env.TELEGRAPH_CHAIN_ID) : TESTNET.chainId;\n return {\n nodeUrl: env.TELEGRAPH_NODE_URL || TESTNET.nodeUrl,\n caip2: env.TELEGRAPH_CAIP2 || `eip155:${chainId}`,\n chainId,\n diamond: env.TELEGRAPH_DIAMOND || TESTNET.diamond,\n usdc: env.TELEGRAPH_USDC || TESTNET.usdc,\n rpcUrl: env.RPC_URL || TESTNET.rpcUrl,\n };\n}\n\n/**\n * The event period starts on Nov 1 (UTC). Paid calls before it would be\n * presented as event activity, which the rules forbid, so the client refuses\n * them unless explicitly told it is a pre-season test.\n */\nexport const SEASON_START = Date.parse(\"2026-11-01T00:00:00Z\");\n","/**\n * The escrow payment rail, as documented on \"Pay from Escrow\" (2026-10-30):\n * read the 402's escrow entry, fill its message template, sign it with\n * EIP-191 personal_sign, and resend with an X-PAYMENT header.\n */\n\nexport interface EscrowAccept {\n scheme: \"escrow\";\n network: string;\n payTo: string;\n price?: string;\n amount?: string;\n extra: { message: string; resource?: string; signWith?: string };\n}\n\nexport interface Authorization {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n}\n\n/** The standard Telegraph receipt (X-PAYMENT-RESPONSE, or X-TELEGRAPH-RECEIPT on x402). */\nexport interface Receipt {\n receipt_hash: string;\n rail: string;\n payer: string;\n miner: string;\n intent?: string;\n amount_uusdc: string;\n epoch_id?: number;\n settlement?: string;\n chain?: string;\n timestamp: number;\n tx_hash?: string;\n}\n\nexport interface MessageSigner {\n address: string;\n signMessage(message: string): Promise<string>;\n}\n\n/** The validity window may be at most 2 minutes; stay well inside it. */\nexport const MAX_WINDOW_SEC = 120;\nconst WINDOW_BEFORE = 5;\nconst WINDOW_AFTER = 100;\n\nfunction b64decode(s: string): string {\n return Buffer.from(s, \"base64\").toString(\"utf8\");\n}\n\nfunction b64encode(s: string): string {\n return Buffer.from(s, \"utf8\").toString(\"base64\");\n}\n\n/** The parts of a 402 that matter: the escrow entry, plus any asset the other entries declare. */\nexport interface Challenge {\n escrow: EscrowAccept;\n /** Assets named by the challenge's other entries (e.g. the x402 \"exact\" one). */\n assets: string[];\n}\n\n/**\n * Reads a 402. The full challenge is in the PAYMENT-REQUIRED header (base64\n * JSON); the body carries a summary that, for escrow, still includes the\n * message template. Header first, then body.\n */\nexport function readChallenge(headerValue: string | null, body: unknown): Challenge | null {\n const candidates: unknown[] = [];\n if (headerValue) {\n try {\n candidates.push(JSON.parse(b64decode(headerValue)));\n } catch {\n // fall through to the body\n }\n }\n candidates.push(body);\n for (const c of candidates) {\n const accepts = (c as { accepts?: unknown })?.accepts;\n if (!Array.isArray(accepts)) continue;\n const hit = accepts.find(\n (a) => a?.scheme === \"escrow\" && typeof a?.extra?.message === \"string\" && typeof a?.payTo === \"string\",\n );\n if (hit) {\n const assets = accepts.map((a) => a?.asset).filter((x): x is string => typeof x === \"string\");\n return { escrow: hit as EscrowAccept, assets };\n }\n }\n return null;\n}\n\nexport function findEscrowAccept(headerValue: string | null, body: unknown): EscrowAccept | null {\n return readChallenge(headerValue, body)?.escrow ?? null;\n}\n\n/** The template's fixed lines, in order, and the placeholder each signed field must carry. */\nconst TEMPLATE_HEADER = \"Telegraph Escrow Payment\";\nconst TEMPLATE_KEYS = [\"Wallet\", \"Pay To\", \"Network\", \"Amount\", \"Resource\", \"Nonce\", \"Valid After\", \"Valid Before\"] as const;\nconst PLACEHOLDERS: Partial<Record<(typeof TEMPLATE_KEYS)[number], string>> = {\n Wallet: \"{from}\",\n Nonce: \"{nonce}\",\n \"Valid After\": \"{validAfter}\",\n \"Valid Before\": \"{validBefore}\",\n};\n\nexport interface ParsedTemplate {\n payTo: string;\n network: string;\n amountUusdc: string;\n resource: string;\n}\n\n/**\n * Parses an escrow message template strictly: the header, a blank line, then\n * exactly the eight documented \"Key: value\" lines, each once and in order.\n * The four signer-filled fields must be bare placeholders, and no other\n * placeholder may appear. Anything else is refused, because the signature\n * covers the whole text, not just the fields Skein checked.\n */\nexport function parseTemplate(template: string): ParsedTemplate {\n const lines = template.split(\"\\n\");\n if (lines[0] !== TEMPLATE_HEADER || lines[1] !== \"\" || lines.length !== 2 + TEMPLATE_KEYS.length) {\n throw new ChallengeMismatchError(\"escrow template does not have the documented layout\");\n }\n const values: Record<string, string> = {};\n TEMPLATE_KEYS.forEach((key, i) => {\n const line = lines[2 + i]!;\n const prefix = `${key}: `;\n if (!line.startsWith(prefix)) throw new ChallengeMismatchError(`escrow template line ${3 + i} should be \"${key}: …\"`);\n values[key] = line.slice(prefix.length);\n });\n for (const [key, ph] of Object.entries(PLACEHOLDERS)) {\n if (values[key] !== ph) throw new ChallengeMismatchError(`escrow template ${key} must be ${ph}`);\n }\n const fixed = TEMPLATE_KEYS.filter((k) => !(k in PLACEHOLDERS)).map((k) => values[k]!);\n if (fixed.some((v) => /[{}]/.test(v))) throw new ChallengeMismatchError(\"escrow template has an unexpected placeholder\");\n const amount = /^(\\d{1,12}) uUSDC$/.exec(values.Amount!);\n if (!amount) throw new ChallengeMismatchError(\"escrow template Amount is not \\\"N uUSDC\\\"\");\n if (!/^\\/v1\\/ask(\\/[\\w.-]+)?$/.test(values.Resource!)) throw new ChallengeMismatchError(`escrow template Resource ${values.Resource} is not an ask`);\n return { payTo: values[\"Pay To\"]!, network: values.Network!, amountUusdc: amount[1]!, resource: values.Resource! };\n}\n\n/** The amount to authorise, read from a strictly parsed template. */\nexport function amountFromTemplate(template: string): string | null {\n try {\n return parseTemplate(template).amountUusdc;\n } catch {\n return null;\n }\n}\n\n/** Fills the four placeholders; parseTemplate has already proved each occurs exactly once. */\nexport function fillTemplate(template: string, auth: Authorization): string {\n return template\n .split(\"{from}\").join(auth.from)\n .split(\"{nonce}\").join(auth.nonce)\n .split(\"{validAfter}\").join(auth.validAfter)\n .split(\"{validBefore}\").join(auth.validBefore);\n}\n\nexport function buildAuthorization(args: {\n from: string;\n to: string;\n value: string;\n nowSec: number;\n nonce: string;\n}): Authorization {\n return {\n // The node lowercases the wallet before checking the signature.\n from: args.from.toLowerCase(),\n to: args.to,\n value: args.value,\n validAfter: String(args.nowSec - WINDOW_BEFORE),\n validBefore: String(args.nowSec + WINDOW_AFTER),\n nonce: args.nonce,\n };\n}\n\nexport class ChallengeMismatchError extends Error {\n readonly code = \"CHALLENGE_MISMATCH\";\n}\n\n/**\n * Signs one escrow payment. Every field the signature covers is checked\n * first: the template's Pay To, Network, Amount and Resource must agree with\n * the configuration, with the challenge's own JSON fields, and with the cap.\n * A node that asks Skein to pay somewhere, someone or something unexpected is\n * refused rather than paid.\n */\nexport async function signEscrowPayment(args: {\n accept: EscrowAccept;\n signer: MessageSigner;\n expected: { caip2: string; diamond: string; maxUusdc: number; usdc?: string; resource?: string };\n /** Assets the challenge's other entries declare; each must be the configured USDC. */\n assets?: readonly string[];\n nowSec: number;\n nonce: string;\n}): Promise<{ header: string; authorization: Authorization; amountUusdc: number }> {\n const { accept, expected } = args;\n const t = parseTemplate(accept.extra.message);\n const same = (a: string, b: string) => a.toLowerCase() === b.toLowerCase();\n if (accept.network !== expected.caip2 || t.network !== expected.caip2) {\n throw new ChallengeMismatchError(`challenge network ${accept.network} / template ${t.network}, expected ${expected.caip2}`);\n }\n if (!same(accept.payTo, expected.diamond) || !same(t.payTo, expected.diamond)) {\n throw new ChallengeMismatchError(`challenge pays ${accept.payTo} / template ${t.payTo}, expected the Diamond ${expected.diamond}`);\n }\n if (accept.amount !== undefined && accept.amount !== t.amountUusdc) {\n throw new ChallengeMismatchError(`challenge amount ${accept.amount} disagrees with the template's ${t.amountUusdc}`);\n }\n if (accept.extra.resource !== undefined && accept.extra.resource !== t.resource) {\n throw new ChallengeMismatchError(`challenge resource ${accept.extra.resource} disagrees with the template's ${t.resource}`);\n }\n if (expected.resource !== undefined && t.resource !== expected.resource) {\n throw new ChallengeMismatchError(`template resource ${t.resource}, expected ${expected.resource}`);\n }\n if (expected.usdc) {\n const wrong = (args.assets ?? []).find((a) => !same(a, expected.usdc!));\n if (wrong) throw new ChallengeMismatchError(`challenge names asset ${wrong}, expected USDC ${expected.usdc}`);\n }\n const amountUusdc = Number(t.amountUusdc);\n if (!(Number.isSafeInteger(amountUusdc) && amountUusdc <= expected.maxUusdc)) {\n throw new ChallengeMismatchError(`challenge asks ${t.amountUusdc} uUSDC, cap ${expected.maxUusdc}`);\n }\n const authorization = buildAuthorization({\n from: args.signer.address,\n to: expected.diamond,\n value: t.amountUusdc,\n nowSec: args.nowSec,\n nonce: args.nonce,\n });\n const signature = await args.signer.signMessage(fillTemplate(accept.extra.message, authorization));\n const header = b64encode(\n JSON.stringify({ x402Version: 2, scheme: \"escrow\", network: accept.network, payload: { signature, authorization } }),\n );\n return { header, authorization, amountUusdc };\n}\n\nexport function decodeReceipt(headerValue: string | null): Receipt | null {\n if (!headerValue) return null;\n try {\n const r = JSON.parse(b64decode(headerValue)) as Receipt;\n return typeof r?.receipt_hash === \"string\" ? r : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Checks a receipt against the payment that was signed. A receipt for another\n * payer, amount, rail or miner is not evidence of this purchase.\n */\nexport function receiptProblems(r: Receipt | null, signed: { payer: string; amountUusdc: number; minerId?: string }): string[] {\n if (!r) return [\"no receipt\"];\n const out: string[] = [];\n if (!/^0x[0-9a-fA-F]{64}$/.test(r.receipt_hash)) out.push(\"receipt_hash is not 32 bytes of hex\");\n if (r.rail !== \"escrow\") out.push(`receipt rail ${r.rail}, expected escrow`);\n if (typeof r.payer !== \"string\" || r.payer.toLowerCase() !== signed.payer.toLowerCase()) out.push(`receipt payer ${r.payer}, expected ${signed.payer}`);\n if (String(r.amount_uusdc) !== String(signed.amountUusdc)) out.push(`receipt amount ${r.amount_uusdc}, signed ${signed.amountUusdc}`);\n if (signed.minerId && r.miner !== undefined && String(r.miner) !== signed.minerId) out.push(`receipt miner ${r.miner}, pinned ${signed.minerId}`);\n return out;\n}\n","import { privateKeyToAccount } from \"viem/accounts\";\nimport { SEASON_START, type NetworkConfig } from \"./config\";\nimport {\n ChallengeMismatchError,\n decodeReceipt,\n readChallenge,\n receiptProblems,\n signEscrowPayment,\n type MessageSigner,\n type Receipt,\n} from \"./escrow\";\n\n/** POST /engine/v1/ask[/{minerId}] bodies (docs: \"Make a Request\"). */\nexport type AskRequest =\n | { intent: string; params?: Record<string, unknown>; query?: string; context?: Record<string, unknown> }\n | { method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\"; endpoint: string; payload: Record<string, unknown> };\n\nexport interface AskResponse {\n miner_id: string;\n miner_name?: string;\n endpoint?: string;\n result: unknown;\n result_shape?: \"intent\" | \"raw\";\n shape_error?: string;\n field_status?: Record<string, \"ok\" | \"missing\" | \"rejected\" | \"unavailable\">;\n intent?: string;\n cost_usd?: number;\n duration_ms?: number;\n signal_hash?: string;\n warnings?: string[];\n}\n\n/** incompatible: the node refused the request before payment (no_compatible_miner, invalid_params); try another miner. */\nexport type AskFailureKind = \"timeout\" | \"network\" | \"refused\" | \"client_error\" | \"server_error\" | \"incompatible\" | \"mismatch\" | \"preseason\";\n\n/**\n * paidUusdc: what was signed and sent with X-PAYMENT on the failing attempt.\n * After a paid POST fails (timeout, dropped connection, 5xx, unreadable 200)\n * the node may still have billed it, so the amount is reported with\n * billing \"unknown\" and the call is never retried.\n */\nexport type AskOutcome =\n | {\n ok: true;\n response: AskResponse;\n receipt: Receipt | null;\n /** Why the receipt is not evidence of this purchase; empty when it checks out. */\n receiptProblems: string[];\n httpStatus: number;\n amountUusdc: number;\n durationMs: number;\n attempts: number;\n }\n | {\n ok: false;\n kind: AskFailureKind;\n httpStatus?: number;\n error: string;\n durationMs: number;\n attempts: number;\n paidUusdc: number;\n billing: \"none\" | \"unknown\";\n };\n\nexport type SignalStatus = \"verified\" | \"missing\" | \"mismatch\" | \"unchecked\";\n\nexport interface MinerRecord {\n id: string;\n slug: string;\n name: string;\n base_url: string;\n supported_intents: string[];\n activation_status: string;\n min_price_usdc: number;\n scored?: boolean;\n total_requests_served?: number;\n}\n\nexport interface ClientOptions {\n network: NetworkConfig;\n signer?: MessageSigner;\n fetch?: typeof fetch;\n /** Hard cap on what one call may cost, in micro-USDC. */\n maxUusdcPerCall: number;\n now?: () => number;\n nonce?: () => string;\n /** Allow paid calls before the season opens (pre-season smoke tests only; never event evidence). */\n allowPreSeason?: boolean;\n}\n\nexport class PreSeasonError extends Error {\n readonly code = \"PRE_SEASON\";\n}\n\nexport function viemSigner(privateKey: `0x${string}`): MessageSigner {\n const account = privateKeyToAccount(privateKey);\n return { address: account.address, signMessage: (message) => account.signMessage({ message }) };\n}\n\nconst RETRYABLE: ReadonlySet<AskFailureKind> = new Set([\"timeout\", \"network\", \"server_error\", \"refused\"]);\n\nexport class TelegraphClient {\n private readonly fetch: typeof fetch;\n private readonly now: () => number;\n private readonly nonce: () => string;\n\n constructor(private readonly opts: ClientOptions) {\n this.fetch = opts.fetch ?? globalThis.fetch;\n this.now = opts.now ?? Date.now;\n this.nonce = opts.nonce ?? (() => crypto.randomUUID());\n }\n\n private url(path: string): string {\n return `${this.opts.network.nodeUrl.replace(/\\/$/, \"\")}${path}`;\n }\n\n private async getJson<T>(path: string): Promise<T> {\n const r = await this.send(path, { method: \"GET\" }, 15_000);\n if (typeof r === \"string\") throw new Error(`GET ${path}: ${r}`);\n if (r.status < 200 || r.status >= 300) throw new Error(`GET ${path}: HTTP ${r.status}`);\n return JSON.parse(r.text) as T;\n }\n\n /**\n * One request with a referenced timer (unlike AbortSignal.timeout(), it keeps\n * a worker or CLI alive while an upstream call hangs) that also covers\n * reading the body, so a slow body cannot hang a verdict.\n */\n private async send(path: string, init: RequestInit, timeoutMs: number): Promise<{ status: number; headers: Headers; text: string } | \"timeout\" | \"network\"> {\n const ctl = new AbortController();\n const timer = setTimeout(() => ctl.abort(new DOMException(\"upstream timed out\", \"TimeoutError\")), timeoutMs);\n try {\n const res = await this.fetch(this.url(path), { ...init, signal: ctl.signal });\n const text = (await res.text()).slice(0, 1_000_000);\n return { status: res.status, headers: res.headers, text };\n } catch (e) {\n const err = e as Error;\n return err.name === \"TimeoutError\" || err.name === \"AbortError\" ? \"timeout\" : \"network\";\n } finally {\n clearTimeout(timer);\n }\n }\n\n listMiners(q: { intent?: string; status?: string; limit?: number } = {}): Promise<MinerRecord[]> {\n const p = new URLSearchParams();\n if (q.intent) p.set(\"intent\", q.intent);\n if (q.status) p.set(\"status\", q.status);\n if (q.limit) p.set(\"limit\", String(q.limit));\n return this.getJson(`/api/miners${p.size ? `?${p}` : \"\"}`);\n }\n\n getIntents(): Promise<{ count: number; intents: { intent_id: string; miner_count: number; canonical: boolean }[] }> {\n return this.getJson(\"/engine/v1/intents\");\n }\n\n getIntent(intent: string): Promise<Record<string, unknown>> {\n return this.getJson(`/engine/v1/intents/${encodeURIComponent(intent)}`);\n }\n\n getSignal(signalHash: string): Promise<Record<string, unknown>> {\n return this.getJson(`/engine/v1/signal/${encodeURIComponent(signalHash)}`);\n }\n\n /**\n * Looks a paid answer's signal_hash up on the node, the network's own record\n * of it. \"verified\": the node has the signal (and, when it says which miner,\n * that miner is the one paid); \"missing\": the node has no such signal;\n * \"mismatch\": it does, from another miner; \"unchecked\": the node did not answer.\n */\n async verifySignal(signalHash: string | undefined, expect: { minerId?: string } = {}): Promise<SignalStatus> {\n if (!signalHash || !/^0x[0-9a-fA-F]{64}$/.test(signalHash)) return \"missing\";\n const r = await this.send(`/engine/v1/signal/${encodeURIComponent(signalHash)}`, { method: \"GET\" }, 10_000);\n if (typeof r === \"string\" || r.status >= 500) return \"unchecked\";\n if (r.status === 404) return \"missing\";\n if (r.status < 200 || r.status >= 300) return \"unchecked\";\n const body = parseJson(r.text) as { miner_id?: unknown; minerId?: unknown } | null;\n const miner = body?.miner_id ?? body?.minerId;\n if (expect.minerId && miner !== undefined && miner !== null && String(miner) !== expect.minerId) return \"mismatch\";\n return \"verified\";\n }\n\n /**\n * Buys one answer on the escrow rail. Failures before payment (no answer to\n * the unpaid probe, a 5xx) and an explicit 402 refusal of the payment are\n * retried once with a fresh challenge and nonce: nothing was billed. A\n * failure after X-PAYMENT was sent is never retried, because the node may\n * have billed it; it comes back with paidUusdc set and billing \"unknown\".\n * maxUusdc lowers the client-wide cap for this one call.\n */\n async ask(req: AskRequest, opts: { minerId?: string; timeoutMs: number; maxUusdc?: number }): Promise<AskOutcome> {\n const started = this.now();\n let last: AskOutcome | null = null;\n for (let attempt = 1; attempt <= 2; attempt++) {\n const out = await this.attempt(req, opts, started, attempt);\n if (out.ok || out.billing === \"unknown\" || !RETRYABLE.has(out.kind)) return out;\n last = out;\n }\n return last!;\n }\n\n private async attempt(req: AskRequest, opts: { minerId?: string; timeoutMs: number; maxUusdc?: number }, started: number, attempts: number): Promise<AskOutcome> {\n const path = opts.minerId ? `/engine/v1/ask/${encodeURIComponent(opts.minerId)}` : \"/engine/v1/ask\";\n const body = JSON.stringify(req);\n const fail = (kind: AskFailureKind, error: string, httpStatus?: number, paidUusdc = 0): AskOutcome => ({\n ok: false, kind, error, durationMs: this.now() - started, attempts, paidUusdc, billing: paidUusdc > 0 ? \"unknown\" : \"none\",\n ...(httpStatus !== undefined ? { httpStatus } : {}),\n });\n const post = (headers: Record<string, string>) =>\n this.send(path, { method: \"POST\", headers: { \"content-type\": \"application/json\", ...headers }, body }, opts.timeoutMs);\n\n const challenge = await post({});\n if (typeof challenge === \"string\") return fail(challenge, `unpaid request: ${challenge}`);\n if (challenge.status !== 402) {\n const msg = challenge.text.slice(0, 2000);\n let err: string | undefined;\n try {\n err = (JSON.parse(msg) as { error?: string }).error;\n } catch {\n // not JSON: classify by status alone\n }\n // A 2xx to an unpaid ask is an answer nobody paid for: not evidence; try another miner.\n const kind: AskFailureKind =\n challenge.status < 300 || err === \"no_compatible_miner\" || err === \"invalid_params\" || err === \"no_input_schema\" || err === \"intent_required\"\n ? \"incompatible\"\n : challenge.status >= 500 ? \"server_error\" : \"client_error\";\n return fail(kind, `expected 402, got ${challenge.status}: ${msg}`, challenge.status);\n }\n const parsed = readChallenge(challenge.headers.get(\"payment-required\"), parseJson(challenge.text));\n if (!parsed) return fail(\"mismatch\", \"the node offered no escrow scheme\", 402);\n\n if (!this.opts.allowPreSeason && this.now() < SEASON_START) {\n return fail(\"preseason\", \"paid calls are refused before the season opens (Nov 1 UTC)\");\n }\n if (!this.opts.signer) return fail(\"mismatch\", \"no signer configured\");\n\n const maxUusdc = Math.min(this.opts.maxUusdcPerCall, opts.maxUusdc ?? Infinity);\n let signed;\n try {\n signed = await signEscrowPayment({\n accept: parsed.escrow,\n assets: parsed.assets,\n signer: this.opts.signer,\n expected: {\n caip2: this.opts.network.caip2,\n diamond: this.opts.network.diamond,\n usdc: this.opts.network.usdc,\n maxUusdc,\n // A pinned ask must be for that miner's resource, not another's.\n ...(opts.minerId ? { resource: `/v1/ask/${opts.minerId}` } : {}),\n },\n nowSec: Math.floor(this.now() / 1000),\n nonce: this.nonce(),\n });\n } catch (e) {\n if (e instanceof ChallengeMismatchError) return fail(\"mismatch\", e.message, 402);\n throw e;\n }\n\n const paid = await post({ \"X-PAYMENT\": signed.header });\n const sent = signed.amountUusdc;\n if (typeof paid === \"string\") return fail(paid, `paid request: ${paid}`, undefined, sent);\n if (paid.status === 402) {\n // An explicit refusal of the payment (nonce, window, amount, balance): not billed.\n const reason = parseJson(paid.text) as { reason?: string; error?: string } | null;\n return fail(\"refused\", `payment refused: ${reason?.reason ?? reason?.error ?? \"no reason given\"}`, 402);\n }\n if (paid.status < 200 || paid.status >= 300) {\n return fail(paid.status >= 500 ? \"server_error\" : \"client_error\", `HTTP ${paid.status}: ${paid.text.slice(0, 2000)}`, paid.status, sent);\n }\n const response = parseJson(paid.text) as AskResponse | null;\n if (!response || typeof response !== \"object\") return fail(\"server_error\", \"200 with a body that is not JSON\", paid.status, sent);\n const receipt = decodeReceipt(paid.headers.get(\"x-payment-response\"));\n const problems = receiptProblems(receipt, { payer: signed.authorization.from, amountUusdc: sent, ...(opts.minerId ? { minerId: opts.minerId } : {}) });\n if (opts.minerId && response.miner_id !== undefined && String(response.miner_id) !== opts.minerId) {\n problems.push(`answered by miner ${response.miner_id}, pinned ${opts.minerId}`);\n }\n return {\n ok: true,\n response,\n receipt,\n receiptProblems: problems,\n httpStatus: paid.status,\n amountUusdc: sent,\n durationMs: this.now() - started,\n attempts,\n };\n }\n}\n\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,219 @@
1
+ type Verdict = "APPROVE" | "REJECT" | "ESCALATE" | "ABSTAIN";
2
+ type CheckStatus = "PASS" | "WARN" | "FAIL" | "UNKNOWN" | "UNAVAILABLE" | "ERROR";
3
+ interface Subject {
4
+ chainId: number;
5
+ token: string;
6
+ }
7
+ interface Finding {
8
+ check: string;
9
+ status: CheckStatus;
10
+ severity: number;
11
+ metric?: string;
12
+ value?: number;
13
+ unit?: string;
14
+ confidence: number;
15
+ sourceGroup: string;
16
+ evidence?: {
17
+ receiptHash?: string;
18
+ signalHash?: string;
19
+ minerId?: string;
20
+ };
21
+ notes: string[];
22
+ }
23
+ /** Skein's signature over a verdict (EIP-191); check it with @skeinlabs/sdk/attest. */
24
+ interface Attestation {
25
+ scheme: "eip191";
26
+ /** Skein's attester address, published at /v1/status. */
27
+ signer: string;
28
+ /** The exact signed text: the verdict's id, subject, outcome, policy, hashes and cost. */
29
+ message: string;
30
+ signature: string;
31
+ }
32
+ /** The verdict object (build spec, section 6). */
33
+ interface VerdictObject {
34
+ id: string;
35
+ verdict: Verdict;
36
+ verdictCode: number;
37
+ riskBps: number;
38
+ confidenceBps: number;
39
+ coverage: number;
40
+ summary: string;
41
+ reasons: {
42
+ code: string;
43
+ check?: string;
44
+ detail: string;
45
+ }[];
46
+ openRisks: string[];
47
+ conflicts: string[];
48
+ subject: Subject;
49
+ findings: Finding[];
50
+ cost: {
51
+ upstreamUsdc: number;
52
+ priceUsdc: number;
53
+ currency: string;
54
+ };
55
+ bom: {
56
+ root: string;
57
+ items: number;
58
+ };
59
+ policy: {
60
+ id: string;
61
+ version: string;
62
+ };
63
+ engineVersion: string;
64
+ verdictHash: string;
65
+ channel: string;
66
+ network: string;
67
+ createdAt: string;
68
+ durationMs: number;
69
+ disclaimer: string;
70
+ cached?: boolean;
71
+ replayed?: boolean;
72
+ /** Present when the API signs verdicts. */
73
+ attestation?: Attestation;
74
+ }
75
+ interface Quote {
76
+ subject: Subject;
77
+ policy: string;
78
+ priceUsdc: number;
79
+ ok: boolean;
80
+ upstreamUsdc?: number;
81
+ checks?: {
82
+ check: string;
83
+ intent: string;
84
+ quoteUsdc: number;
85
+ }[];
86
+ dropped?: unknown[];
87
+ refusal?: {
88
+ code: string;
89
+ detail: string;
90
+ checks?: string[];
91
+ quoteUusdc?: number;
92
+ };
93
+ }
94
+ interface VerifyReport {
95
+ id: string;
96
+ ok: boolean;
97
+ checks: Record<string, boolean>;
98
+ verdictHash: {
99
+ stored: string;
100
+ recomputed: string;
101
+ };
102
+ bomRoot: {
103
+ stored: string;
104
+ recomputed: string;
105
+ };
106
+ receipts: string[];
107
+ problems: string[];
108
+ /** The signer the API checked the attestation against, when it signs verdicts. */
109
+ attester?: string;
110
+ }
111
+ interface BomLine {
112
+ check: string;
113
+ intent: string;
114
+ minerId: string | null;
115
+ minerName: string | null;
116
+ receiptHash: string | null;
117
+ signalHash: string | null;
118
+ amountUsdc: number;
119
+ billed: boolean;
120
+ error: string | null;
121
+ [k: string]: unknown;
122
+ }
123
+ interface Bom {
124
+ id: string;
125
+ root: string;
126
+ items: number;
127
+ verdictHash: string;
128
+ lines: BomLine[];
129
+ }
130
+ /** One server-sent event of a running verdict. */
131
+ type VerdictEvent = {
132
+ type: "plan";
133
+ data: {
134
+ purchases?: {
135
+ check: string;
136
+ intent: string;
137
+ quoteUusdc: number;
138
+ }[];
139
+ dropped?: unknown[];
140
+ [k: string]: unknown;
141
+ };
142
+ } | {
143
+ type: "purchase";
144
+ data: {
145
+ check: string;
146
+ minerId?: string;
147
+ minerName?: string;
148
+ amountUusdc: number;
149
+ receiptHash?: string;
150
+ error?: string;
151
+ [k: string]: unknown;
152
+ };
153
+ } | {
154
+ type: "finding";
155
+ data: Finding;
156
+ } | {
157
+ type: "decision";
158
+ data: {
159
+ decision: {
160
+ verdict: Verdict;
161
+ riskBps: number;
162
+ [k: string]: unknown;
163
+ };
164
+ verdictHash: string;
165
+ [k: string]: unknown;
166
+ };
167
+ } | {
168
+ type: "done";
169
+ data: {
170
+ id?: string;
171
+ [k: string]: unknown;
172
+ };
173
+ } | {
174
+ type: "refused";
175
+ data: {
176
+ code: string;
177
+ detail: string;
178
+ [k: string]: unknown;
179
+ };
180
+ } | {
181
+ type: "error";
182
+ data: {
183
+ message: string;
184
+ [k: string]: unknown;
185
+ };
186
+ };
187
+ /** The Telegraph ask result of a paid call, as returned by any Telegraph client. */
188
+ interface TelegraphAskOutcomeOk {
189
+ ok: true;
190
+ response: {
191
+ miner_id: string;
192
+ result: unknown;
193
+ signal_hash?: string;
194
+ cost_usd?: number;
195
+ };
196
+ receipt: {
197
+ receipt_hash: string;
198
+ } | null;
199
+ amountUusdc: number;
200
+ durationMs: number;
201
+ }
202
+ interface TelegraphAskOutcomeFail {
203
+ ok: false;
204
+ kind: string;
205
+ error: string;
206
+ httpStatus?: number;
207
+ }
208
+ /** Anything that can make one paid Telegraph ask, e.g. TelegraphClient from @skeinlabs/sdk/telegraph. */
209
+ interface TelegraphAsker {
210
+ ask(req: {
211
+ intent: string;
212
+ params?: Record<string, unknown>;
213
+ }, opts: {
214
+ minerId?: string;
215
+ timeoutMs: number;
216
+ }): Promise<TelegraphAskOutcomeOk | TelegraphAskOutcomeFail>;
217
+ }
218
+
219
+ export type { Attestation as A, Bom as B, CheckStatus as C, Finding as F, Quote as Q, Subject as S, TelegraphAsker as T, VerdictObject as V, VerifyReport as a, VerdictEvent as b, BomLine as c, TelegraphAskOutcomeFail as d, TelegraphAskOutcomeOk as e, Verdict as f };