@qed-proof/sdk 0.1.1
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/CHANGELOG.md +23 -0
- package/LICENSE +202 -0
- package/README.md +83 -0
- package/dist/index.cjs +1074 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +317 -0
- package/dist/index.d.ts +317 -0
- package/dist/index.js +1047 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1074 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
QedProof: () => QedProof,
|
|
34
|
+
QedProofError: () => QedProofError,
|
|
35
|
+
RateLimitError: () => RateLimitError,
|
|
36
|
+
SIG_DOMAIN: () => SIG_DOMAIN,
|
|
37
|
+
SPEC_VERSION: () => SPEC_VERSION,
|
|
38
|
+
actions: () => actions_exports,
|
|
39
|
+
b64u: () => b64u,
|
|
40
|
+
b64uDecode: () => b64uDecode,
|
|
41
|
+
claimDigest: () => claimDigest,
|
|
42
|
+
hasFloatText: () => hasFloatText,
|
|
43
|
+
hasFloatValue: () => hasFloatValue,
|
|
44
|
+
jcs: () => jcs,
|
|
45
|
+
keyId: () => keyId,
|
|
46
|
+
leafHash: () => leafHash,
|
|
47
|
+
rootFromInclusion: () => rootFromInclusion,
|
|
48
|
+
sha256: () => sha256,
|
|
49
|
+
verifyReceipt: () => verifyReceipt,
|
|
50
|
+
verifySignature: () => verifySignature
|
|
51
|
+
});
|
|
52
|
+
module.exports = __toCommonJS(index_exports);
|
|
53
|
+
|
|
54
|
+
// src/errors.ts
|
|
55
|
+
var QedProofError = class extends Error {
|
|
56
|
+
status;
|
|
57
|
+
detail;
|
|
58
|
+
constructor(status, detail) {
|
|
59
|
+
super(`QedProof API error ${status}: ${detail}`);
|
|
60
|
+
this.name = "QedProofError";
|
|
61
|
+
this.status = status;
|
|
62
|
+
this.detail = detail;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var RateLimitError = class extends QedProofError {
|
|
66
|
+
retryAfter;
|
|
67
|
+
constructor(detail, retryAfter) {
|
|
68
|
+
super(429, detail);
|
|
69
|
+
this.name = "RateLimitError";
|
|
70
|
+
this.retryAfter = retryAfter;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/primitives.ts
|
|
75
|
+
var import_ed25519 = require("@noble/curves/ed25519");
|
|
76
|
+
var import_sha2 = require("@noble/hashes/sha2");
|
|
77
|
+
var import_canonicalize = __toESM(require("canonicalize"), 1);
|
|
78
|
+
var SPEC_VERSION = "poaw/0.1";
|
|
79
|
+
var SIG_DOMAIN = new TextEncoder().encode("POAW-RECEIPT-V0\n");
|
|
80
|
+
function b64u(data) {
|
|
81
|
+
let binary = "";
|
|
82
|
+
for (let i = 0; i < data.length; i++) binary += String.fromCharCode(data[i]);
|
|
83
|
+
const base64 = btoa(binary);
|
|
84
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
85
|
+
}
|
|
86
|
+
function b64uDecode(text) {
|
|
87
|
+
const padded = text + "=".repeat((4 - text.length % 4) % 4);
|
|
88
|
+
const base64 = padded.replace(/-/g, "+").replace(/_/g, "/");
|
|
89
|
+
const binary = atob(base64);
|
|
90
|
+
const out = new Uint8Array(binary.length);
|
|
91
|
+
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
function jcs(obj) {
|
|
95
|
+
const text = (0, import_canonicalize.default)(obj);
|
|
96
|
+
if (text === void 0) {
|
|
97
|
+
throw new TypeError("jcs: value is not JSON-serializable");
|
|
98
|
+
}
|
|
99
|
+
return new TextEncoder().encode(text);
|
|
100
|
+
}
|
|
101
|
+
function sha256(data) {
|
|
102
|
+
return (0, import_sha2.sha256)(data);
|
|
103
|
+
}
|
|
104
|
+
function concatBytes(...parts) {
|
|
105
|
+
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
106
|
+
const out = new Uint8Array(total);
|
|
107
|
+
let offset = 0;
|
|
108
|
+
for (const p of parts) {
|
|
109
|
+
out.set(p, offset);
|
|
110
|
+
offset += p.length;
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
function hasFloatValue(value) {
|
|
115
|
+
if (typeof value === "boolean") return false;
|
|
116
|
+
if (typeof value === "number") return !Number.isInteger(value);
|
|
117
|
+
if (Array.isArray(value)) return value.some(hasFloatValue);
|
|
118
|
+
if (value !== null && typeof value === "object") {
|
|
119
|
+
return Object.values(value).some(hasFloatValue);
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
function hasFloatText(text) {
|
|
124
|
+
let i = 0;
|
|
125
|
+
const n = text.length;
|
|
126
|
+
while (i < n) {
|
|
127
|
+
const ch = text[i];
|
|
128
|
+
if (ch === '"') {
|
|
129
|
+
i++;
|
|
130
|
+
while (i < n) {
|
|
131
|
+
if (text[i] === "\\") {
|
|
132
|
+
i += 2;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (text[i] === '"') {
|
|
136
|
+
i++;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
i++;
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (ch === "-" || ch >= "0" && ch <= "9") {
|
|
144
|
+
const start = i;
|
|
145
|
+
if (ch === "-") i++;
|
|
146
|
+
let sawFloatChar = false;
|
|
147
|
+
while (i < n && text[i] >= "0" && text[i] <= "9") i++;
|
|
148
|
+
if (text[i] === ".") {
|
|
149
|
+
sawFloatChar = true;
|
|
150
|
+
i++;
|
|
151
|
+
while (i < n && text[i] >= "0" && text[i] <= "9") i++;
|
|
152
|
+
}
|
|
153
|
+
if (text[i] === "e" || text[i] === "E") {
|
|
154
|
+
sawFloatChar = true;
|
|
155
|
+
i++;
|
|
156
|
+
if (text[i] === "+" || text[i] === "-") i++;
|
|
157
|
+
while (i < n && text[i] >= "0" && text[i] <= "9") i++;
|
|
158
|
+
}
|
|
159
|
+
if (i === start) i++;
|
|
160
|
+
if (sawFloatChar) return true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
i++;
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
function keyId(pkRaw) {
|
|
168
|
+
return "ed25519:" + b64u(sha256(pkRaw));
|
|
169
|
+
}
|
|
170
|
+
function verifySignature(pkRaw, body, sigValue) {
|
|
171
|
+
try {
|
|
172
|
+
const sig = b64uDecode(sigValue);
|
|
173
|
+
const message = concatBytes(SIG_DOMAIN, jcs(body));
|
|
174
|
+
return import_ed25519.ed25519.verify(sig, message, pkRaw);
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function claimDigest(claim) {
|
|
180
|
+
const stripped = {};
|
|
181
|
+
for (const k of Object.keys(claim)) {
|
|
182
|
+
if (k !== "claim_digest") stripped[k] = claim[k];
|
|
183
|
+
}
|
|
184
|
+
return b64u(sha256(jcs(stripped)));
|
|
185
|
+
}
|
|
186
|
+
function leafHash(receipt) {
|
|
187
|
+
return sha256(concatBytes(new Uint8Array([0]), jcs({ body: receipt.body, signature: receipt.signature })));
|
|
188
|
+
}
|
|
189
|
+
function nodeHash(left, right) {
|
|
190
|
+
return sha256(concatBytes(new Uint8Array([1]), left, right));
|
|
191
|
+
}
|
|
192
|
+
function bytesEqual(a, b) {
|
|
193
|
+
if (a.length !== b.length) return false;
|
|
194
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
function rootFromInclusion(index, size, leaf, path) {
|
|
198
|
+
if (index >= size) return null;
|
|
199
|
+
let fn = index;
|
|
200
|
+
let sn = size - 1;
|
|
201
|
+
let r = leaf;
|
|
202
|
+
for (const p of path) {
|
|
203
|
+
if (sn === 0) return null;
|
|
204
|
+
if ((fn & 1) === 1 || fn === sn) {
|
|
205
|
+
r = nodeHash(p, r);
|
|
206
|
+
if ((fn & 1) === 0) {
|
|
207
|
+
while (fn !== 0 && (fn & 1) === 0) {
|
|
208
|
+
fn >>= 1;
|
|
209
|
+
sn >>= 1;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
r = nodeHash(r, p);
|
|
214
|
+
}
|
|
215
|
+
fn >>= 1;
|
|
216
|
+
sn >>= 1;
|
|
217
|
+
}
|
|
218
|
+
return sn === 0 ? r : null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/schema.ts
|
|
222
|
+
var import__ = __toESM(require("ajv/dist/2020.js"), 1);
|
|
223
|
+
var import_ajv_formats = __toESM(require("ajv-formats"), 1);
|
|
224
|
+
|
|
225
|
+
// src/generated/receipt-schema.ts
|
|
226
|
+
var receiptSchema = {
|
|
227
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
228
|
+
"$id": "urn:poaw:schema:0.1:receipt",
|
|
229
|
+
"title": "PoAW receipt (poaw/0.1)",
|
|
230
|
+
"description": "Structural schema for a Proof of Agent Work receipt. Signature, inclusion and anchor checks are defined in SPEC.md and are not expressible in JSON Schema.",
|
|
231
|
+
"type": "object",
|
|
232
|
+
"required": [
|
|
233
|
+
"body",
|
|
234
|
+
"signature"
|
|
235
|
+
],
|
|
236
|
+
"additionalProperties": false,
|
|
237
|
+
"properties": {
|
|
238
|
+
"body": {
|
|
239
|
+
"$ref": "#/$defs/body"
|
|
240
|
+
},
|
|
241
|
+
"signature": {
|
|
242
|
+
"$ref": "#/$defs/signature"
|
|
243
|
+
},
|
|
244
|
+
"proof": {
|
|
245
|
+
"$ref": "#/$defs/proof"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
"$defs": {
|
|
249
|
+
"timestamp": {
|
|
250
|
+
"type": "string",
|
|
251
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,3})?Z$"
|
|
252
|
+
},
|
|
253
|
+
"b64url": {
|
|
254
|
+
"type": "string",
|
|
255
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
256
|
+
},
|
|
257
|
+
"keyId": {
|
|
258
|
+
"type": "string",
|
|
259
|
+
"pattern": "^ed25519:[A-Za-z0-9_-]{43}$"
|
|
260
|
+
},
|
|
261
|
+
"fingerprint": {
|
|
262
|
+
"type": "string",
|
|
263
|
+
"pattern": "^sha256:[A-Za-z0-9_-]{43}$"
|
|
264
|
+
},
|
|
265
|
+
"action": {
|
|
266
|
+
"type": "string",
|
|
267
|
+
"pattern": "^[a-z0-9]+(\\.[a-z0-9_]+){2}$"
|
|
268
|
+
},
|
|
269
|
+
"verdictValue": {
|
|
270
|
+
"enum": [
|
|
271
|
+
"verified",
|
|
272
|
+
"late",
|
|
273
|
+
"mismatch",
|
|
274
|
+
"failed",
|
|
275
|
+
"unverifiable"
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
"reasonCode": {
|
|
279
|
+
"enum": [
|
|
280
|
+
"not_found",
|
|
281
|
+
"content_mismatch",
|
|
282
|
+
"target_mismatch",
|
|
283
|
+
"observed_after_tolerance",
|
|
284
|
+
"no_connection",
|
|
285
|
+
"permission_denied",
|
|
286
|
+
"rate_limited",
|
|
287
|
+
"destination_unavailable",
|
|
288
|
+
"unsupported_action",
|
|
289
|
+
"claim_ambiguous",
|
|
290
|
+
"verifier_error"
|
|
291
|
+
]
|
|
292
|
+
},
|
|
293
|
+
"body": {
|
|
294
|
+
"type": "object",
|
|
295
|
+
"required": [
|
|
296
|
+
"spec_version",
|
|
297
|
+
"receipt_id",
|
|
298
|
+
"issued_at",
|
|
299
|
+
"issuer",
|
|
300
|
+
"agent",
|
|
301
|
+
"claim",
|
|
302
|
+
"observation",
|
|
303
|
+
"verdict",
|
|
304
|
+
"trust_level"
|
|
305
|
+
],
|
|
306
|
+
"properties": {
|
|
307
|
+
"spec_version": {
|
|
308
|
+
"const": "poaw/0.1"
|
|
309
|
+
},
|
|
310
|
+
"receipt_id": {
|
|
311
|
+
"type": "string",
|
|
312
|
+
"minLength": 1,
|
|
313
|
+
"maxLength": 128
|
|
314
|
+
},
|
|
315
|
+
"issued_at": {
|
|
316
|
+
"$ref": "#/$defs/timestamp"
|
|
317
|
+
},
|
|
318
|
+
"issuer": {
|
|
319
|
+
"type": "object",
|
|
320
|
+
"required": [
|
|
321
|
+
"key_id"
|
|
322
|
+
],
|
|
323
|
+
"properties": {
|
|
324
|
+
"key_id": {
|
|
325
|
+
"$ref": "#/$defs/keyId"
|
|
326
|
+
},
|
|
327
|
+
"name": {
|
|
328
|
+
"type": "string",
|
|
329
|
+
"maxLength": 128
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
"agent": {
|
|
334
|
+
"type": "object",
|
|
335
|
+
"required": [
|
|
336
|
+
"id"
|
|
337
|
+
],
|
|
338
|
+
"properties": {
|
|
339
|
+
"id": {
|
|
340
|
+
"type": "string",
|
|
341
|
+
"minLength": 1,
|
|
342
|
+
"maxLength": 256
|
|
343
|
+
},
|
|
344
|
+
"erc8004": {
|
|
345
|
+
"type": "object",
|
|
346
|
+
"required": [
|
|
347
|
+
"chain",
|
|
348
|
+
"registry",
|
|
349
|
+
"agent_id"
|
|
350
|
+
],
|
|
351
|
+
"properties": {
|
|
352
|
+
"chain": {
|
|
353
|
+
"type": "string",
|
|
354
|
+
"pattern": "^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$"
|
|
355
|
+
},
|
|
356
|
+
"registry": {
|
|
357
|
+
"type": "string"
|
|
358
|
+
},
|
|
359
|
+
"agent_id": {
|
|
360
|
+
"type": "string"
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
"operator": {
|
|
367
|
+
"type": "string",
|
|
368
|
+
"maxLength": 256
|
|
369
|
+
},
|
|
370
|
+
"claim": {
|
|
371
|
+
"type": "object",
|
|
372
|
+
"required": [
|
|
373
|
+
"action",
|
|
374
|
+
"target",
|
|
375
|
+
"params",
|
|
376
|
+
"claimed_at",
|
|
377
|
+
"claim_digest"
|
|
378
|
+
],
|
|
379
|
+
"properties": {
|
|
380
|
+
"action": {
|
|
381
|
+
"$ref": "#/$defs/action"
|
|
382
|
+
},
|
|
383
|
+
"target": {
|
|
384
|
+
"type": "string",
|
|
385
|
+
"minLength": 1,
|
|
386
|
+
"maxLength": 512
|
|
387
|
+
},
|
|
388
|
+
"params": {
|
|
389
|
+
"type": "object"
|
|
390
|
+
},
|
|
391
|
+
"claimed_at": {
|
|
392
|
+
"$ref": "#/$defs/timestamp"
|
|
393
|
+
},
|
|
394
|
+
"client_claim_id": {
|
|
395
|
+
"type": "string",
|
|
396
|
+
"maxLength": 256
|
|
397
|
+
},
|
|
398
|
+
"claim_digest": {
|
|
399
|
+
"$ref": "#/$defs/b64url"
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
"observation": {
|
|
404
|
+
"type": "object",
|
|
405
|
+
"required": [
|
|
406
|
+
"verifier",
|
|
407
|
+
"observed_at",
|
|
408
|
+
"deadline_at",
|
|
409
|
+
"attempts",
|
|
410
|
+
"facts"
|
|
411
|
+
],
|
|
412
|
+
"properties": {
|
|
413
|
+
"verifier": {
|
|
414
|
+
"type": "object",
|
|
415
|
+
"required": [
|
|
416
|
+
"id",
|
|
417
|
+
"version"
|
|
418
|
+
],
|
|
419
|
+
"properties": {
|
|
420
|
+
"id": {
|
|
421
|
+
"$ref": "#/$defs/action"
|
|
422
|
+
},
|
|
423
|
+
"version": {
|
|
424
|
+
"type": "string",
|
|
425
|
+
"pattern": "^\\d+$"
|
|
426
|
+
},
|
|
427
|
+
"code_hash": {
|
|
428
|
+
"$ref": "#/$defs/fingerprint"
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
"observed_at": {
|
|
433
|
+
"$ref": "#/$defs/timestamp"
|
|
434
|
+
},
|
|
435
|
+
"deadline_at": {
|
|
436
|
+
"$ref": "#/$defs/timestamp"
|
|
437
|
+
},
|
|
438
|
+
"attempts": {
|
|
439
|
+
"type": "integer",
|
|
440
|
+
"minimum": 1
|
|
441
|
+
},
|
|
442
|
+
"facts": {
|
|
443
|
+
"type": "object"
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
"verdict": {
|
|
448
|
+
"type": "object",
|
|
449
|
+
"required": [
|
|
450
|
+
"value"
|
|
451
|
+
],
|
|
452
|
+
"properties": {
|
|
453
|
+
"value": {
|
|
454
|
+
"$ref": "#/$defs/verdictValue"
|
|
455
|
+
},
|
|
456
|
+
"reason_code": {
|
|
457
|
+
"$ref": "#/$defs/reasonCode"
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
"if": {
|
|
461
|
+
"properties": {
|
|
462
|
+
"value": {
|
|
463
|
+
"const": "unverifiable"
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
"then": {
|
|
468
|
+
"required": [
|
|
469
|
+
"value",
|
|
470
|
+
"reason_code"
|
|
471
|
+
]
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
"trust_level": {
|
|
475
|
+
"type": "integer",
|
|
476
|
+
"minimum": 1,
|
|
477
|
+
"maximum": 4
|
|
478
|
+
},
|
|
479
|
+
"attestation": {
|
|
480
|
+
"type": "object",
|
|
481
|
+
"required": [
|
|
482
|
+
"type",
|
|
483
|
+
"document"
|
|
484
|
+
],
|
|
485
|
+
"properties": {
|
|
486
|
+
"type": {
|
|
487
|
+
"type": "string"
|
|
488
|
+
},
|
|
489
|
+
"document": {
|
|
490
|
+
"$ref": "#/$defs/b64url"
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
"supersedes": {
|
|
495
|
+
"type": "string"
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
"if": {
|
|
499
|
+
"properties": {
|
|
500
|
+
"trust_level": {
|
|
501
|
+
"minimum": 3
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
"then": {
|
|
506
|
+
"required": [
|
|
507
|
+
"attestation"
|
|
508
|
+
],
|
|
509
|
+
"properties": {
|
|
510
|
+
"observation": {
|
|
511
|
+
"properties": {
|
|
512
|
+
"verifier": {
|
|
513
|
+
"required": [
|
|
514
|
+
"id",
|
|
515
|
+
"version",
|
|
516
|
+
"code_hash"
|
|
517
|
+
]
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
"signature": {
|
|
525
|
+
"type": "object",
|
|
526
|
+
"required": [
|
|
527
|
+
"alg",
|
|
528
|
+
"key_id",
|
|
529
|
+
"value"
|
|
530
|
+
],
|
|
531
|
+
"additionalProperties": false,
|
|
532
|
+
"properties": {
|
|
533
|
+
"alg": {
|
|
534
|
+
"const": "Ed25519"
|
|
535
|
+
},
|
|
536
|
+
"key_id": {
|
|
537
|
+
"$ref": "#/$defs/keyId"
|
|
538
|
+
},
|
|
539
|
+
"value": {
|
|
540
|
+
"type": "string",
|
|
541
|
+
"pattern": "^[A-Za-z0-9_-]{86}$"
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
"proof": {
|
|
546
|
+
"type": "object",
|
|
547
|
+
"required": [
|
|
548
|
+
"log_id",
|
|
549
|
+
"leaf_index",
|
|
550
|
+
"tree_size",
|
|
551
|
+
"root_hash",
|
|
552
|
+
"inclusion"
|
|
553
|
+
],
|
|
554
|
+
"properties": {
|
|
555
|
+
"log_id": {
|
|
556
|
+
"$ref": "#/$defs/b64url"
|
|
557
|
+
},
|
|
558
|
+
"leaf_index": {
|
|
559
|
+
"type": "integer",
|
|
560
|
+
"minimum": 0
|
|
561
|
+
},
|
|
562
|
+
"tree_size": {
|
|
563
|
+
"type": "integer",
|
|
564
|
+
"minimum": 1
|
|
565
|
+
},
|
|
566
|
+
"root_hash": {
|
|
567
|
+
"$ref": "#/$defs/b64url"
|
|
568
|
+
},
|
|
569
|
+
"inclusion": {
|
|
570
|
+
"type": "array",
|
|
571
|
+
"items": {
|
|
572
|
+
"$ref": "#/$defs/b64url"
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
"anchor": {
|
|
576
|
+
"type": "object",
|
|
577
|
+
"required": [
|
|
578
|
+
"chain",
|
|
579
|
+
"scheme",
|
|
580
|
+
"uid",
|
|
581
|
+
"tx_hash",
|
|
582
|
+
"tree_size"
|
|
583
|
+
],
|
|
584
|
+
"properties": {
|
|
585
|
+
"chain": {
|
|
586
|
+
"type": "string"
|
|
587
|
+
},
|
|
588
|
+
"scheme": {
|
|
589
|
+
"const": "eas"
|
|
590
|
+
},
|
|
591
|
+
"uid": {
|
|
592
|
+
"type": "string",
|
|
593
|
+
"pattern": "^0x[0-9a-fA-F]{64}$"
|
|
594
|
+
},
|
|
595
|
+
"tx_hash": {
|
|
596
|
+
"type": "string",
|
|
597
|
+
"pattern": "^0x[0-9a-fA-F]{64}$"
|
|
598
|
+
},
|
|
599
|
+
"tree_size": {
|
|
600
|
+
"type": "integer",
|
|
601
|
+
"minimum": 1
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
// src/schema.ts
|
|
611
|
+
var ajv = new import__.default({ allErrors: false, strict: false });
|
|
612
|
+
(0, import_ajv_formats.default)(ajv);
|
|
613
|
+
var validate = ajv.compile(receiptSchema);
|
|
614
|
+
function schemaValid(receipt) {
|
|
615
|
+
return Boolean(validate(receipt));
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/anchor.ts
|
|
619
|
+
var import_sha3 = require("@noble/hashes/sha3");
|
|
620
|
+
|
|
621
|
+
// src/abi.ts
|
|
622
|
+
function readWord(data, wordIndex) {
|
|
623
|
+
const start = wordIndex * 32;
|
|
624
|
+
return data.subarray(start, start + 32);
|
|
625
|
+
}
|
|
626
|
+
function decodeStaticField(word, kind) {
|
|
627
|
+
switch (kind) {
|
|
628
|
+
case "bytes32":
|
|
629
|
+
return word.slice();
|
|
630
|
+
case "uint64": {
|
|
631
|
+
let v = 0n;
|
|
632
|
+
for (let i = 24; i < 32; i++) v = v << 8n | BigInt(word[i]);
|
|
633
|
+
return v;
|
|
634
|
+
}
|
|
635
|
+
case "address": {
|
|
636
|
+
const bytes = word.subarray(12, 32);
|
|
637
|
+
return "0x" + Buffer_toHex(bytes);
|
|
638
|
+
}
|
|
639
|
+
case "bool":
|
|
640
|
+
return word[31] !== 0;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
function Buffer_toHex(bytes) {
|
|
644
|
+
let s = "";
|
|
645
|
+
for (const b of bytes) s += b.toString(16).padStart(2, "0");
|
|
646
|
+
return s;
|
|
647
|
+
}
|
|
648
|
+
function decodeTupleWithTrailingDynamic(data, staticKinds, dynamicKind) {
|
|
649
|
+
const statics = staticKinds.map((kind, i) => decodeStaticField(readWord(data, i), kind));
|
|
650
|
+
const offsetWord = readWord(data, staticKinds.length);
|
|
651
|
+
let offset = 0n;
|
|
652
|
+
for (const b of offsetWord) offset = offset << 8n | BigInt(b);
|
|
653
|
+
const off = Number(offset);
|
|
654
|
+
const lenWord = data.subarray(off, off + 32);
|
|
655
|
+
let len = 0n;
|
|
656
|
+
for (const b of lenWord) len = len << 8n | BigInt(b);
|
|
657
|
+
const length = Number(len);
|
|
658
|
+
const raw = data.subarray(off + 32, off + 32 + length);
|
|
659
|
+
const dynamic = dynamicKind === "string" ? new TextDecoder().decode(raw) : raw.slice();
|
|
660
|
+
return { statics, dynamic };
|
|
661
|
+
}
|
|
662
|
+
function decodeSingleDynamicTupleReturn(returnData, staticKinds, dynamicKind) {
|
|
663
|
+
const outerOffsetWord = readWord(returnData, 0);
|
|
664
|
+
let outerOffset = 0n;
|
|
665
|
+
for (const b of outerOffsetWord) outerOffset = outerOffset << 8n | BigInt(b);
|
|
666
|
+
const tupleStart = Number(outerOffset);
|
|
667
|
+
return decodeTupleWithTrailingDynamic(returnData.subarray(tupleStart), staticKinds, dynamicKind);
|
|
668
|
+
}
|
|
669
|
+
function bytesToHex0x(bytes) {
|
|
670
|
+
return "0x" + Buffer_toHex(bytes);
|
|
671
|
+
}
|
|
672
|
+
function hexToBytes(hex) {
|
|
673
|
+
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
674
|
+
const out = new Uint8Array(clean.length / 2);
|
|
675
|
+
for (let i = 0; i < out.length; i++) out[i] = parseInt(clean.substr(i * 2, 2), 16);
|
|
676
|
+
return out;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/anchor.ts
|
|
680
|
+
var EAS = "0x4200000000000000000000000000000000000021";
|
|
681
|
+
function selector(signature) {
|
|
682
|
+
return (0, import_sha3.keccak_256)(new TextEncoder().encode(signature)).slice(0, 4);
|
|
683
|
+
}
|
|
684
|
+
var GET_SELECTOR = selector("getAttestation(bytes32)");
|
|
685
|
+
var ATT_STATIC_KINDS = ["bytes32", "bytes32", "uint64", "uint64", "uint64", "bytes32", "address", "address", "bool"];
|
|
686
|
+
var DATA_STATIC_KINDS = ["bytes32", "uint64", "bytes32", "uint64", "bytes32"];
|
|
687
|
+
async function rpc(url, method, ...params) {
|
|
688
|
+
const res = await fetch(url, {
|
|
689
|
+
method: "POST",
|
|
690
|
+
headers: { "content-type": "application/json", "user-agent": "qed-proof-sdk-js/0.1" },
|
|
691
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
|
|
692
|
+
});
|
|
693
|
+
const body = await res.json();
|
|
694
|
+
if (body && body.error) {
|
|
695
|
+
throw new Error(`${method}: ${body.error.message ?? "rpc error"}`);
|
|
696
|
+
}
|
|
697
|
+
return body.result;
|
|
698
|
+
}
|
|
699
|
+
async function checkAnchor(proof, keyset, rpcUrl) {
|
|
700
|
+
const a = proof.anchor;
|
|
701
|
+
if (!a) return { ok: false, reason: "unknown_scheme", proven_by: null };
|
|
702
|
+
try {
|
|
703
|
+
if (a.scheme !== "eas") return { ok: false, reason: "unknown_scheme", proven_by: null };
|
|
704
|
+
const chainIdHex = await rpc(rpcUrl, "eth_chainId");
|
|
705
|
+
const wantChainId = a.chain.split(":")[1];
|
|
706
|
+
if (BigInt(chainIdHex) !== BigInt(wantChainId)) {
|
|
707
|
+
return { ok: false, reason: "rpc_chain_mismatch", proven_by: null };
|
|
708
|
+
}
|
|
709
|
+
const uid = hexToBytes(a.uid);
|
|
710
|
+
const callData = new Uint8Array([...GET_SELECTOR, ...uid]);
|
|
711
|
+
const out = await rpc(rpcUrl, "eth_call", { to: EAS, data: bytesToHex0x(callData) }, "latest");
|
|
712
|
+
const returnData = hexToBytes(out);
|
|
713
|
+
const { statics, dynamic } = decodeSingleDynamicTupleReturn(returnData, [...ATT_STATIC_KINDS], "bytes");
|
|
714
|
+
const [uid_, schema, time_, _exp, revokedAt, _ref, _recipient, attester, _revocable] = statics;
|
|
715
|
+
const data = dynamic;
|
|
716
|
+
if (!bytesEqual(uid_, uid) || time_ === 0n) {
|
|
717
|
+
return { ok: false, reason: "attestation_not_found", proven_by: null };
|
|
718
|
+
}
|
|
719
|
+
if (revokedAt !== 0n) {
|
|
720
|
+
return { ok: false, reason: "attestation_revoked", proven_by: null };
|
|
721
|
+
}
|
|
722
|
+
const attesterLower = attester.toLowerCase();
|
|
723
|
+
const publishedAttesters = new Set((keyset.anchor_addresses ?? []).map((x) => x.toLowerCase()));
|
|
724
|
+
if (!publishedAttesters.has(attesterLower)) {
|
|
725
|
+
return { ok: false, reason: "attester_not_published", proven_by: null };
|
|
726
|
+
}
|
|
727
|
+
const schemaHex = "0x" + bytesToHex0x(schema).slice(2);
|
|
728
|
+
const publishedSchemas = new Set((keyset.anchor_schemas ?? []).map((x) => x.toLowerCase()));
|
|
729
|
+
if (!publishedSchemas.has(schemaHex.toLowerCase())) {
|
|
730
|
+
return { ok: false, reason: "schema_not_published", proven_by: null };
|
|
731
|
+
}
|
|
732
|
+
let decodedData;
|
|
733
|
+
try {
|
|
734
|
+
decodedData = decodeTupleWithTrailingDynamic(data, [...DATA_STATIC_KINDS], "string");
|
|
735
|
+
} catch {
|
|
736
|
+
return { ok: false, reason: "data_undecodable", proven_by: null };
|
|
737
|
+
}
|
|
738
|
+
const [logId, size, root, _prevSize, _prevRoot] = decodedData.statics;
|
|
739
|
+
if (!bytesEqual(logId, b64uDecode(proof.log_id)) || size !== BigInt(a.tree_size)) {
|
|
740
|
+
return { ok: false, reason: "log_or_size_mismatch", proven_by: null };
|
|
741
|
+
}
|
|
742
|
+
if (a.tree_size !== proof.tree_size) {
|
|
743
|
+
return { ok: false, reason: "consistency_proof_required", proven_by: null };
|
|
744
|
+
}
|
|
745
|
+
if (!bytesEqual(root, b64uDecode(proof.root_hash))) {
|
|
746
|
+
return { ok: false, reason: "root_mismatch", proven_by: null };
|
|
747
|
+
}
|
|
748
|
+
return { ok: true, reason: "anchored", proven_by: Number(time_) };
|
|
749
|
+
} catch (exc) {
|
|
750
|
+
const name = exc instanceof Error ? exc.constructor.name : "Error";
|
|
751
|
+
return { ok: false, reason: `unreadable:${name}`, proven_by: null };
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// src/verify.ts
|
|
756
|
+
var RECEIPT_TEXT = /* @__PURE__ */ new WeakMap();
|
|
757
|
+
async function verifyReceipt(receipt, options) {
|
|
758
|
+
const raw = typeof receipt === "string" ? receipt : receipt && typeof receipt === "object" ? RECEIPT_TEXT.get(receipt) : void 0;
|
|
759
|
+
const parsed = typeof receipt === "string" ? JSON.parse(receipt) : receipt ?? {};
|
|
760
|
+
const keyset = options.keys ?? {};
|
|
761
|
+
const checks = {};
|
|
762
|
+
const body = (parsed && typeof parsed === "object" ? parsed.body : void 0) ?? {};
|
|
763
|
+
const version = String(body.spec_version ?? "");
|
|
764
|
+
const parts = version.split("/");
|
|
765
|
+
checks.spec_version = parts[0] === "poaw" && (parts[parts.length - 1] ?? "").split(".")[0] === "0";
|
|
766
|
+
checks.schema = schemaValid(parsed);
|
|
767
|
+
const integerFail = raw !== void 0 ? hasFloatText(raw) : hasFloatValue(parsed);
|
|
768
|
+
checks.integers_only = !integerFail;
|
|
769
|
+
const sig = (parsed && typeof parsed === "object" ? parsed.signature : void 0) ?? {};
|
|
770
|
+
const keys = keyset.keys ?? [];
|
|
771
|
+
const key = keys.find((k) => k.key_id === sig?.key_id);
|
|
772
|
+
const issued = String(body.issued_at ?? "");
|
|
773
|
+
const issuer = body.issuer;
|
|
774
|
+
let keyOk = false;
|
|
775
|
+
if (key && sig?.key_id === issuer?.key_id) {
|
|
776
|
+
try {
|
|
777
|
+
const computedKeyId = keyId(b64uDecode(key.public_key));
|
|
778
|
+
keyOk = computedKeyId === key.key_id && key.valid_from <= issued && (key.revoked_at === null || key.revoked_at === void 0 || issued < key.revoked_at);
|
|
779
|
+
} catch {
|
|
780
|
+
keyOk = false;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
checks.key = keyOk;
|
|
784
|
+
let sigOk = false;
|
|
785
|
+
if (keyOk && key) {
|
|
786
|
+
try {
|
|
787
|
+
sigOk = verifySignature(b64uDecode(key.public_key), body, sig?.value ?? "");
|
|
788
|
+
} catch {
|
|
789
|
+
sigOk = false;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
checks.signature = sigOk;
|
|
793
|
+
const claim = body.claim;
|
|
794
|
+
checks.claim_digest = Boolean(claim) && typeof claim === "object" && claim.claim_digest === claimDigest(claim);
|
|
795
|
+
const proof = parsed && typeof parsed === "object" ? parsed.proof : void 0;
|
|
796
|
+
let provenBy;
|
|
797
|
+
if (!proof) {
|
|
798
|
+
checks.inclusion = "absent";
|
|
799
|
+
} else {
|
|
800
|
+
const root = rootFromInclusion(
|
|
801
|
+
proof.leaf_index,
|
|
802
|
+
proof.tree_size,
|
|
803
|
+
leafHash({ body: parsed.body, signature: parsed.signature }),
|
|
804
|
+
proof.inclusion.map((h) => b64uDecode(h))
|
|
805
|
+
);
|
|
806
|
+
checks.inclusion = root !== null && b64u(root) === proof.root_hash;
|
|
807
|
+
}
|
|
808
|
+
if (!(proof && proof.anchor)) {
|
|
809
|
+
checks.anchor = "absent";
|
|
810
|
+
} else if (!options.rpcUrl) {
|
|
811
|
+
checks.anchor = "not_checked_offline";
|
|
812
|
+
} else {
|
|
813
|
+
const a = await checkAnchor(proof, keyset, options.rpcUrl);
|
|
814
|
+
checks.anchor = a.ok ? true : a.reason;
|
|
815
|
+
provenBy = a.proven_by;
|
|
816
|
+
}
|
|
817
|
+
const required = [
|
|
818
|
+
"spec_version",
|
|
819
|
+
"schema",
|
|
820
|
+
"integers_only",
|
|
821
|
+
"key",
|
|
822
|
+
"signature",
|
|
823
|
+
"claim_digest"
|
|
824
|
+
];
|
|
825
|
+
const valid = required.every((k) => checks[k] === true) && (checks.inclusion === true || checks.inclusion === "absent");
|
|
826
|
+
const achievedTrustLevel = valid ? checks.inclusion === true && checks.anchor === true ? 2 : 1 : 0;
|
|
827
|
+
const verdictValue = body.verdict;
|
|
828
|
+
const verdict = valid ? verdictValue?.value ?? null : null;
|
|
829
|
+
const report = {
|
|
830
|
+
checks,
|
|
831
|
+
valid,
|
|
832
|
+
verdict: verdict ?? null,
|
|
833
|
+
achieved_trust_level: achievedTrustLevel
|
|
834
|
+
};
|
|
835
|
+
if (provenBy !== void 0) {
|
|
836
|
+
report.proven_by = provenBy;
|
|
837
|
+
}
|
|
838
|
+
return report;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// src/client.ts
|
|
842
|
+
var LIST_CLAIMS_DEFAULT_LIMIT = 50;
|
|
843
|
+
var LIST_CLAIMS_MIN_LIMIT = 1;
|
|
844
|
+
var LIST_CLAIMS_MAX_LIMIT = 200;
|
|
845
|
+
var DEFAULT_BASE_URL = "https://api.qedproof.site";
|
|
846
|
+
function isoNow() {
|
|
847
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
848
|
+
}
|
|
849
|
+
var QedProof = class {
|
|
850
|
+
apiKey;
|
|
851
|
+
baseUrl;
|
|
852
|
+
fetchImpl;
|
|
853
|
+
constructor(options = {}) {
|
|
854
|
+
this.apiKey = options.apiKey;
|
|
855
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
856
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
857
|
+
if (!this.fetchImpl) {
|
|
858
|
+
throw new Error("QedProof: no fetch implementation available; pass { fetch } explicitly");
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
async request(method, path, { auth = true, body, text = false } = {}) {
|
|
862
|
+
const headers = { accept: "application/json" };
|
|
863
|
+
if (body !== void 0) headers["content-type"] = "application/json";
|
|
864
|
+
if (auth) {
|
|
865
|
+
if (!this.apiKey) {
|
|
866
|
+
throw new QedProofError(401, "no API key configured");
|
|
867
|
+
}
|
|
868
|
+
headers["authorization"] = `Bearer ${this.apiKey}`;
|
|
869
|
+
}
|
|
870
|
+
let res;
|
|
871
|
+
try {
|
|
872
|
+
res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
873
|
+
method,
|
|
874
|
+
headers,
|
|
875
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
876
|
+
});
|
|
877
|
+
} catch (exc) {
|
|
878
|
+
throw new QedProofError(0, `network error: ${exc instanceof Error ? exc.message : String(exc)}`);
|
|
879
|
+
}
|
|
880
|
+
if (res.status === 429) {
|
|
881
|
+
const retryAfterHeader = res.headers.get("retry-after");
|
|
882
|
+
const retryAfter = retryAfterHeader ? Number(retryAfterHeader) : void 0;
|
|
883
|
+
let detail = "rate limit exceeded";
|
|
884
|
+
try {
|
|
885
|
+
const parsed = await res.json();
|
|
886
|
+
if (parsed?.detail) detail = parsed.detail;
|
|
887
|
+
} catch {
|
|
888
|
+
}
|
|
889
|
+
throw new RateLimitError(detail, Number.isFinite(retryAfter) ? retryAfter : void 0);
|
|
890
|
+
}
|
|
891
|
+
if (!res.ok) {
|
|
892
|
+
let detail = res.statusText;
|
|
893
|
+
try {
|
|
894
|
+
const parsed = await res.json();
|
|
895
|
+
if (parsed?.detail) detail = parsed.detail;
|
|
896
|
+
} catch {
|
|
897
|
+
}
|
|
898
|
+
throw new QedProofError(res.status, detail);
|
|
899
|
+
}
|
|
900
|
+
if (res.status === 204) return void 0;
|
|
901
|
+
if (text) return res.text();
|
|
902
|
+
return res.json();
|
|
903
|
+
}
|
|
904
|
+
async submitClaim(claim, options) {
|
|
905
|
+
const claimedAt = options.claimedAt instanceof Date ? options.claimedAt.toISOString() : options.claimedAt ?? isoNow();
|
|
906
|
+
const body = {
|
|
907
|
+
client_claim_id: options.clientClaimId ?? cryptoRandomId(),
|
|
908
|
+
agent_id: options.agentId,
|
|
909
|
+
action: claim.action,
|
|
910
|
+
target: claim.target,
|
|
911
|
+
params: claim.params,
|
|
912
|
+
claimed_at: claimedAt
|
|
913
|
+
};
|
|
914
|
+
return await this.request("POST", "/v1/claims", { body });
|
|
915
|
+
}
|
|
916
|
+
async getClaim(claimId) {
|
|
917
|
+
return await this.request("GET", `/v1/claims/${encodeURIComponent(claimId)}`);
|
|
918
|
+
}
|
|
919
|
+
async waitForVerdict(claimId, options = {}) {
|
|
920
|
+
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
921
|
+
const pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
922
|
+
const deadline = Date.now() + timeoutMs;
|
|
923
|
+
for (; ; ) {
|
|
924
|
+
let wait = pollIntervalMs;
|
|
925
|
+
try {
|
|
926
|
+
const status = await this.getClaim(claimId);
|
|
927
|
+
if (status.state === "decided") return status;
|
|
928
|
+
} catch (exc) {
|
|
929
|
+
if (exc instanceof RateLimitError && exc.retryAfter !== void 0) {
|
|
930
|
+
wait = Math.max(wait, exc.retryAfter * 1e3);
|
|
931
|
+
} else {
|
|
932
|
+
throw exc;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (Date.now() >= deadline) {
|
|
936
|
+
throw new QedProofError(408, `timed out after ${timeoutMs}ms waiting for claim ${claimId} to decide`);
|
|
937
|
+
}
|
|
938
|
+
await sleep(Math.min(wait, Math.max(0, deadline - Date.now())));
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* `GET /v1/claims`, newest first. `options.limit` (1-200, default 50) is validated locally —
|
|
943
|
+
* out of range throws a `RangeError` before any request is made.
|
|
944
|
+
*/
|
|
945
|
+
async listClaims(options = {}) {
|
|
946
|
+
const limit = options.limit ?? LIST_CLAIMS_DEFAULT_LIMIT;
|
|
947
|
+
if (limit < LIST_CLAIMS_MIN_LIMIT || limit > LIST_CLAIMS_MAX_LIMIT) {
|
|
948
|
+
throw new RangeError(`limit must be between ${LIST_CLAIMS_MIN_LIMIT} and ${LIST_CLAIMS_MAX_LIMIT}, got ${limit}`);
|
|
949
|
+
}
|
|
950
|
+
const query = { limit: String(limit) };
|
|
951
|
+
if (options.cursor !== void 0) query.cursor = options.cursor;
|
|
952
|
+
if (options.agentId !== void 0) query.agent_id = options.agentId;
|
|
953
|
+
if (options.action !== void 0) query.action = options.action;
|
|
954
|
+
if (options.verdict !== void 0) query.verdict = options.verdict;
|
|
955
|
+
if (options.state !== void 0) query.state = options.state;
|
|
956
|
+
const qs = new URLSearchParams(query).toString();
|
|
957
|
+
const data = await this.request("GET", `/v1/claims?${qs}`);
|
|
958
|
+
return { claims: data.claims, nextCursor: data.next_cursor };
|
|
959
|
+
}
|
|
960
|
+
/** Yields every claim matching the filters, following `nextCursor` until exhausted. */
|
|
961
|
+
async *iterClaims(options = {}) {
|
|
962
|
+
const { pageSize, ...filters } = options;
|
|
963
|
+
let cursor;
|
|
964
|
+
for (; ; ) {
|
|
965
|
+
const page = await this.listClaims({ ...filters, limit: pageSize, cursor });
|
|
966
|
+
yield* page.claims;
|
|
967
|
+
if (page.nextCursor === null) return;
|
|
968
|
+
cursor = page.nextCursor;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
/** Receipts are designed to be shared and checked by anyone (SPEC §1): no Authorization header. */
|
|
972
|
+
async getReceipt(receiptId) {
|
|
973
|
+
const text = await this.request("GET", `/v1/receipts/${encodeURIComponent(receiptId)}`, { auth: false, text: true });
|
|
974
|
+
const receipt = JSON.parse(text);
|
|
975
|
+
RECEIPT_TEXT.set(receipt, text);
|
|
976
|
+
return receipt;
|
|
977
|
+
}
|
|
978
|
+
/** Public by design: `/.well-known/poaw-keys.json` (SPEC §5.2, §8.4). */
|
|
979
|
+
async getKeys() {
|
|
980
|
+
return await this.request("GET", "/.well-known/poaw-keys.json", { auth: false });
|
|
981
|
+
}
|
|
982
|
+
async verify(receipt, options = {}) {
|
|
983
|
+
const keys = options.keys ?? await this.getKeys();
|
|
984
|
+
return verifyReceipt(receipt, { keys, rpcUrl: options.rpcUrl });
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
function sleep(ms) {
|
|
988
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
989
|
+
}
|
|
990
|
+
function cryptoRandomId() {
|
|
991
|
+
const g = globalThis;
|
|
992
|
+
if (g.crypto?.randomUUID) return g.crypto.randomUUID();
|
|
993
|
+
return `claim-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// src/actions.ts
|
|
997
|
+
var actions_exports = {};
|
|
998
|
+
__export(actions_exports, {
|
|
999
|
+
githubChecksPass: () => githubChecksPass,
|
|
1000
|
+
githubCommitPush: () => githubCommitPush,
|
|
1001
|
+
githubPrOpen: () => githubPrOpen,
|
|
1002
|
+
httpUrlStatus: () => httpUrlStatus,
|
|
1003
|
+
slackMessagePost: () => slackMessagePost,
|
|
1004
|
+
xPostPublish: () => xPostPublish
|
|
1005
|
+
});
|
|
1006
|
+
var REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
1007
|
+
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
1008
|
+
var HANDLE_RE = /^@[A-Za-z0-9_]{1,15}$/;
|
|
1009
|
+
var POST_ID_RE = /^[0-9]{1,20}$/;
|
|
1010
|
+
var HEX64_RE = /^[0-9a-f]{64}$/;
|
|
1011
|
+
var SLACK_TARGET_RE = /^slack:\/\/(T[A-Z0-9]{2,20})\/([CG][A-Z0-9]{2,20})$/;
|
|
1012
|
+
var SLACK_TS_RE = /^[0-9]{10}\.[0-9]{6}$/;
|
|
1013
|
+
function assert(condition, message) {
|
|
1014
|
+
if (!condition) throw new TypeError(message);
|
|
1015
|
+
}
|
|
1016
|
+
function githubCommitPush(input) {
|
|
1017
|
+
assert(REPO_RE.test(input.target), "githubCommitPush: target must be 'owner/repo'");
|
|
1018
|
+
assert(SHA_RE.test(input.sha), "githubCommitPush: sha must be 40 lowercase hex characters");
|
|
1019
|
+
assert(input.branch && input.branch.length > 0, "githubCommitPush: branch is required");
|
|
1020
|
+
return { action: "github.commit.push", target: input.target, params: { sha: input.sha, branch: input.branch } };
|
|
1021
|
+
}
|
|
1022
|
+
function githubPrOpen(input) {
|
|
1023
|
+
assert(REPO_RE.test(input.target), "githubPrOpen: target must be 'owner/repo'");
|
|
1024
|
+
assert(Number.isInteger(input.number) && input.number >= 1, "githubPrOpen: number must be a positive integer");
|
|
1025
|
+
if (input.headSha !== void 0) {
|
|
1026
|
+
assert(SHA_RE.test(input.headSha), "githubPrOpen: headSha must be 40 lowercase hex characters");
|
|
1027
|
+
}
|
|
1028
|
+
const params = { number: input.number };
|
|
1029
|
+
if (input.base !== void 0) params.base = input.base;
|
|
1030
|
+
if (input.headSha !== void 0) params.head_sha = input.headSha;
|
|
1031
|
+
return { action: "github.pr.open", target: input.target, params };
|
|
1032
|
+
}
|
|
1033
|
+
function githubChecksPass(input) {
|
|
1034
|
+
assert(REPO_RE.test(input.target), "githubChecksPass: target must be 'owner/repo'");
|
|
1035
|
+
assert(SHA_RE.test(input.sha), "githubChecksPass: sha must be 40 lowercase hex characters");
|
|
1036
|
+
return { action: "github.checks.pass", target: input.target, params: { sha: input.sha } };
|
|
1037
|
+
}
|
|
1038
|
+
function xPostPublish(input) {
|
|
1039
|
+
assert(HANDLE_RE.test(input.target), "xPostPublish: target must be an @handle");
|
|
1040
|
+
assert(POST_ID_RE.test(input.postId), "xPostPublish: postId must be digits");
|
|
1041
|
+
if (input.textSha256 !== void 0) {
|
|
1042
|
+
assert(HEX64_RE.test(input.textSha256), "xPostPublish: textSha256 must be 64 lowercase hex characters");
|
|
1043
|
+
}
|
|
1044
|
+
const params = { post_id: input.postId };
|
|
1045
|
+
if (input.textSha256 !== void 0) params.text_sha256 = input.textSha256;
|
|
1046
|
+
return { action: "x.post.publish", target: input.target, params };
|
|
1047
|
+
}
|
|
1048
|
+
function slackMessagePost(input) {
|
|
1049
|
+
assert(SLACK_TARGET_RE.test(input.target), "slackMessagePost: target must be slack://<team_id>/<channel_id>");
|
|
1050
|
+
assert(SLACK_TS_RE.test(input.ts), "slackMessagePost: ts must match ^[0-9]{10}\\.[0-9]{6}$");
|
|
1051
|
+
if (input.textSha256 !== void 0) {
|
|
1052
|
+
assert(HEX64_RE.test(input.textSha256), "slackMessagePost: textSha256 must be 64 lowercase hex characters");
|
|
1053
|
+
}
|
|
1054
|
+
const params = { ts: input.ts };
|
|
1055
|
+
if (input.textSha256 !== void 0) params.text_sha256 = input.textSha256;
|
|
1056
|
+
return { action: "slack.message.post", target: input.target, params };
|
|
1057
|
+
}
|
|
1058
|
+
function httpUrlStatus(input) {
|
|
1059
|
+
let url;
|
|
1060
|
+
try {
|
|
1061
|
+
url = new URL(input.target);
|
|
1062
|
+
} catch {
|
|
1063
|
+
throw new TypeError("httpUrlStatus: target must be a valid URL");
|
|
1064
|
+
}
|
|
1065
|
+
assert(url.protocol === "https:", "httpUrlStatus: target must be an https:// URL");
|
|
1066
|
+
const params = {};
|
|
1067
|
+
if (input.status !== void 0) {
|
|
1068
|
+
assert(Number.isInteger(input.status), "httpUrlStatus: status must be an integer");
|
|
1069
|
+
params.status = input.status;
|
|
1070
|
+
}
|
|
1071
|
+
if (input.contentFingerprint !== void 0) params.content_fingerprint = input.contentFingerprint;
|
|
1072
|
+
return { action: "http.url.status", target: input.target, params };
|
|
1073
|
+
}
|
|
1074
|
+
//# sourceMappingURL=index.cjs.map
|