@singularity-layer/grid 0.7.0 → 0.9.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 +34 -0
- package/dist/index.d.mts +340 -102
- package/dist/index.d.ts +340 -102
- package/dist/index.js +367 -115
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +359 -114
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -449,161 +449,406 @@ var GridClient = class {
|
|
|
449
449
|
}
|
|
450
450
|
if (!sawFinal) throw new SGLAPIError(502, "stream ended before final chunk (truncated)");
|
|
451
451
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// src/vault.ts
|
|
455
|
+
import { gcm } from "@noble/ciphers/aes";
|
|
456
|
+
import { scrypt } from "@noble/hashes/scrypt";
|
|
457
|
+
var VAULT_URL = "https://compute.x402layer.cc";
|
|
458
|
+
var SCRYPT_PARAMS = { N: 1 << 17, r: 8, p: 1 };
|
|
459
|
+
var SCRYPT_MIN_N = 1 << 15;
|
|
460
|
+
var te = new TextEncoder();
|
|
461
|
+
var td = new TextDecoder();
|
|
462
|
+
function aadBytes(aad) {
|
|
463
|
+
const { agentId, backupId, formatVersion, userId } = aad;
|
|
464
|
+
return te.encode(JSON.stringify({ agentId, backupId, formatVersion, userId }));
|
|
465
|
+
}
|
|
466
|
+
function b64(x) {
|
|
467
|
+
let s = "";
|
|
468
|
+
for (const b of x) s += String.fromCharCode(b);
|
|
469
|
+
return btoa(s);
|
|
470
|
+
}
|
|
471
|
+
function unb64(s) {
|
|
472
|
+
const bin = atob(s);
|
|
473
|
+
const out = new Uint8Array(bin.length);
|
|
474
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
475
|
+
return out;
|
|
476
|
+
}
|
|
477
|
+
function rand(n) {
|
|
478
|
+
const out = new Uint8Array(n);
|
|
479
|
+
crypto.getRandomValues(out);
|
|
480
|
+
return out;
|
|
481
|
+
}
|
|
482
|
+
function encryptEnvelope(plaintext, passphrase, aad) {
|
|
483
|
+
const salt = rand(16);
|
|
484
|
+
const kek = scrypt(te.encode(passphrase), salt, { ...SCRYPT_PARAMS, dkLen: 32 });
|
|
485
|
+
const dek = rand(32);
|
|
486
|
+
const aadBuf = aadBytes(aad);
|
|
487
|
+
const dekNonce = rand(12);
|
|
488
|
+
const wrapped = gcm(kek, dekNonce, aadBuf).encrypt(dek);
|
|
489
|
+
const blobNonce = rand(12);
|
|
490
|
+
const body = gcm(dek, blobNonce, aadBuf).encrypt(plaintext);
|
|
491
|
+
const header = te.encode(JSON.stringify({
|
|
492
|
+
formatVersion: 1,
|
|
493
|
+
kdf: "scrypt",
|
|
494
|
+
kdfParams: { ...SCRYPT_PARAMS, salt: b64(salt) },
|
|
495
|
+
cipher: "aes-256-gcm",
|
|
496
|
+
wrappedDek: { nonce: b64(dekNonce), ciphertext: b64(wrapped) },
|
|
497
|
+
blobNonce: b64(blobNonce),
|
|
498
|
+
aad
|
|
499
|
+
}));
|
|
500
|
+
const out = new Uint8Array(4 + header.length + body.length);
|
|
501
|
+
new DataView(out.buffer).setUint32(0, header.length, false);
|
|
502
|
+
out.set(header, 4);
|
|
503
|
+
out.set(body, 4 + header.length);
|
|
504
|
+
return out;
|
|
505
|
+
}
|
|
506
|
+
function decryptEnvelope(blob, passphrase, aad) {
|
|
507
|
+
if (blob.length < 4) throw new SGLAPIError(0, "malformed blob: too short");
|
|
508
|
+
const headerLen = new DataView(blob.buffer, blob.byteOffset).getUint32(0, false);
|
|
509
|
+
if (4 + headerLen > blob.length) throw new SGLAPIError(0, "malformed blob: header length out of bounds");
|
|
510
|
+
let header;
|
|
511
|
+
try {
|
|
512
|
+
header = JSON.parse(td.decode(blob.subarray(4, 4 + headerLen)));
|
|
513
|
+
} catch {
|
|
514
|
+
throw new SGLAPIError(0, "malformed blob: invalid header JSON");
|
|
515
|
+
}
|
|
516
|
+
if (header.kdf !== "scrypt") {
|
|
517
|
+
throw new SGLAPIError(0, `this backup uses ${String(header.kdf)} key derivation \u2014 restore it with the agentvault CLI`);
|
|
518
|
+
}
|
|
519
|
+
const p = header.kdfParams ?? {};
|
|
520
|
+
if (!Number.isInteger(p.N) || p.N < SCRYPT_MIN_N || p.N > SCRYPT_PARAMS.N || (p.N & p.N - 1) !== 0 || !Number.isInteger(p.r) || p.r < 8 || p.r > 16 || !Number.isInteger(p.p) || p.p < 1 || p.p > 4 || typeof p.salt !== "string" || !header.wrappedDek?.nonce || !header.wrappedDek?.ciphertext || !header.blobNonce) {
|
|
521
|
+
throw new SGLAPIError(0, "malformed blob: unsupported header parameters");
|
|
522
|
+
}
|
|
523
|
+
const salt = unb64(p.salt);
|
|
524
|
+
if (salt.length < 16) throw new SGLAPIError(0, "malformed blob: salt too short");
|
|
525
|
+
const kek = scrypt(te.encode(passphrase), salt, { N: p.N, r: p.r, p: p.p, dkLen: 32 });
|
|
526
|
+
const aadBuf = aadBytes(aad);
|
|
527
|
+
try {
|
|
528
|
+
const dek = gcm(kek, unb64(header.wrappedDek.nonce), aadBuf).decrypt(unb64(header.wrappedDek.ciphertext));
|
|
529
|
+
return gcm(dek, unb64(header.blobNonce), aadBuf).decrypt(blob.subarray(4 + headerLen));
|
|
530
|
+
} catch {
|
|
531
|
+
throw new SGLAPIError(0, "incorrect passphrase or corrupted backup");
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function parseAadFromKey(r2Key) {
|
|
535
|
+
const parts = r2Key.split("/");
|
|
536
|
+
if (parts.length !== 5 || parts[0] !== "backups" || parts[4] !== "blob.enc" || !parts[1] || !parts[2] || !parts[3]) {
|
|
537
|
+
throw new SGLAPIError(0, `malformed r2 key: ${r2Key}`);
|
|
538
|
+
}
|
|
539
|
+
return { userId: parts[1], agentId: parts[2], backupId: parts[3], formatVersion: 1 };
|
|
540
|
+
}
|
|
541
|
+
var VaultClient = class _VaultClient {
|
|
542
|
+
constructor(options) {
|
|
543
|
+
const base = (options.baseUrl ?? VAULT_URL).replace(/\/+$/, "");
|
|
544
|
+
const u = new URL(base);
|
|
545
|
+
const local = ["localhost", "127.0.0.1", "[::1]"].includes(u.hostname);
|
|
546
|
+
if (u.protocol !== "https:" && !local) {
|
|
547
|
+
throw new SGLAPIError(0, "baseUrl must be https (the API key travels in a header)");
|
|
482
548
|
}
|
|
483
|
-
if (
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
549
|
+
if (u.username || u.password || u.search || u.hash) {
|
|
550
|
+
throw new SGLAPIError(0, "baseUrl must be a bare origin");
|
|
551
|
+
}
|
|
552
|
+
this.base = base;
|
|
553
|
+
this.apiKey = options.apiKey;
|
|
554
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
555
|
+
}
|
|
556
|
+
static id(v) {
|
|
557
|
+
if (!/^[0-9a-fA-F-]{36}$/.test(v)) throw new SGLAPIError(0, `not a snapshot id: ${v}`);
|
|
558
|
+
return v.toLowerCase();
|
|
559
|
+
}
|
|
560
|
+
async call(method, path, body) {
|
|
561
|
+
const res = await this.fetchImpl(`${this.base}${path}`, {
|
|
562
|
+
method,
|
|
563
|
+
headers: {
|
|
564
|
+
"x-api-key": this.apiKey,
|
|
565
|
+
...body !== void 0 ? { "content-type": "application/json" } : {}
|
|
566
|
+
},
|
|
567
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
568
|
+
});
|
|
569
|
+
const data = await res.json().catch(() => ({}));
|
|
570
|
+
if (!res.ok) throw new SGLAPIError(res.status, String(data.error ?? `request failed: ${res.status}`));
|
|
571
|
+
return data;
|
|
572
|
+
}
|
|
573
|
+
async agents() {
|
|
574
|
+
return (await this.call("GET", "/backups/agents")).agents;
|
|
575
|
+
}
|
|
576
|
+
async createAgent(name, framework) {
|
|
577
|
+
return (await this.call("POST", "/backups/agents", { name, framework })).agent;
|
|
578
|
+
}
|
|
579
|
+
async snapshots(agentId) {
|
|
580
|
+
const q = agentId ? `?agentId=${encodeURIComponent(agentId)}` : "";
|
|
581
|
+
return (await this.call("GET", `/backups${q}`)).backups;
|
|
582
|
+
}
|
|
583
|
+
async usage() {
|
|
584
|
+
return this.call("GET", "/backups/usage");
|
|
585
|
+
}
|
|
586
|
+
/** Activate Vault Pro ($3/mo from credits). */
|
|
587
|
+
async subscribePro() {
|
|
588
|
+
return this.call("POST", "/backups/subscribe");
|
|
589
|
+
}
|
|
590
|
+
async deleteSnapshot(id) {
|
|
591
|
+
await this.call("DELETE", `/backups/${_VaultClient.id(id)}`);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Encrypt + upload arbitrary payload bytes (e.g. a tarball you packed) as a
|
|
595
|
+
* snapshot of `agentId`. Returns the snapshot id.
|
|
596
|
+
*/
|
|
597
|
+
async backupBytes(agentId, payload, passphrase) {
|
|
598
|
+
const res = await this.call(
|
|
599
|
+
"POST",
|
|
600
|
+
"/backups",
|
|
601
|
+
{ agentId, sizeBytes: payload.length }
|
|
602
|
+
);
|
|
603
|
+
const blob = encryptEnvelope(payload, passphrase, parseAadFromKey(res.r2Key));
|
|
604
|
+
const up = await this.fetchImpl(res.uploadUrl, {
|
|
605
|
+
method: "PUT",
|
|
606
|
+
body: blob,
|
|
607
|
+
headers: { "content-type": "application/octet-stream" }
|
|
608
|
+
});
|
|
609
|
+
if (!up.ok) throw new SGLAPIError(up.status, `upload failed: ${up.status}`);
|
|
610
|
+
const digest = await crypto.subtle.digest("SHA-256", blob);
|
|
611
|
+
const sha2562 = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
612
|
+
await this.call("POST", `/backups/${res.backupId}/complete`, { sha256: sha2562 });
|
|
613
|
+
return res.backupId;
|
|
614
|
+
}
|
|
615
|
+
/** Download + decrypt a snapshot's payload bytes. */
|
|
616
|
+
async restoreBytes(snapshotId, passphrase) {
|
|
617
|
+
const info = await this.call("GET", `/backups/${_VaultClient.id(snapshotId)}/restore`);
|
|
618
|
+
const dl = await this.fetchImpl(info.downloadUrl);
|
|
619
|
+
if (!dl.ok) throw new SGLAPIError(dl.status, `download failed: ${dl.status}`);
|
|
620
|
+
const blob = new Uint8Array(await dl.arrayBuffer());
|
|
621
|
+
return decryptEnvelope(blob, passphrase, parseAadFromKey(info.r2Key));
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
// src/processors.ts
|
|
626
|
+
var PROCESSORS_BASE_URL = "https://processors.x402compute.cc";
|
|
627
|
+
var DEFAULT_TIMEOUT2 = 6e4;
|
|
628
|
+
function isLoopback(hostname) {
|
|
629
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname === "::1";
|
|
630
|
+
}
|
|
631
|
+
function parseBaseUrl(raw) {
|
|
632
|
+
let u;
|
|
633
|
+
try {
|
|
634
|
+
u = new URL(raw);
|
|
635
|
+
} catch {
|
|
636
|
+
throw new Error(`ProcessorsClient baseUrl is not a valid URL: ${raw}`);
|
|
637
|
+
}
|
|
638
|
+
if (u.protocol !== "https:" && !(u.protocol === "http:" && isLoopback(u.hostname))) {
|
|
639
|
+
throw new Error(
|
|
640
|
+
`ProcessorsClient baseUrl must be https (or http on localhost); got ${u.protocol}//${u.hostname}. The API key is a long-lived full-control credential and must not be sent in the clear.`
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
return u;
|
|
644
|
+
}
|
|
645
|
+
function keyAllowedOnHost(u) {
|
|
646
|
+
return u.origin === new URL(PROCESSORS_BASE_URL).origin || isLoopback(u.hostname);
|
|
647
|
+
}
|
|
648
|
+
var ProcessorsClient = class {
|
|
649
|
+
constructor(options = {}) {
|
|
650
|
+
const parsed = parseBaseUrl(options.baseUrl ?? PROCESSORS_BASE_URL);
|
|
651
|
+
this.baseUrl = (options.baseUrl ?? PROCESSORS_BASE_URL).replace(/\/+$/, "");
|
|
652
|
+
this.timeout = options.timeoutMs ?? DEFAULT_TIMEOUT2;
|
|
653
|
+
this.headers = { Accept: "application/json", "Content-Type": "application/json" };
|
|
654
|
+
if (options.apiKey) {
|
|
655
|
+
if (!keyAllowedOnHost(parsed) && !options.allowKeyOnCustomHost) {
|
|
656
|
+
throw new Error(
|
|
657
|
+
`ProcessorsClient refuses to send an API key to ${parsed.origin}. It is neither ${new URL(PROCESSORS_BASE_URL).origin} nor loopback. If you really do run your own processors host, pass allowKeyOnCustomHost: true.`
|
|
658
|
+
);
|
|
499
659
|
}
|
|
500
|
-
|
|
660
|
+
this.headers["X-API-Key"] = options.apiKey;
|
|
501
661
|
}
|
|
502
|
-
if (response.status === 204) return {};
|
|
503
|
-
return await response.json();
|
|
504
662
|
}
|
|
505
|
-
async
|
|
663
|
+
async request(method, path, body, extraHeaders, sendApiKey = true) {
|
|
506
664
|
const url = `${this.baseUrl}${path}`;
|
|
507
665
|
const controller = new AbortController();
|
|
508
666
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
509
|
-
const reqHeaders = { ...this.headers };
|
|
510
|
-
if (paymentHeader) {
|
|
511
|
-
reqHeaders["X-Payment"] = paymentHeader;
|
|
512
|
-
}
|
|
513
667
|
let response;
|
|
514
668
|
try {
|
|
669
|
+
const base = { ...this.headers };
|
|
670
|
+
if (!sendApiKey) delete base["X-API-Key"];
|
|
515
671
|
response = await fetch(url, {
|
|
516
672
|
method,
|
|
517
|
-
headers:
|
|
518
|
-
body: body ? JSON.stringify(body)
|
|
673
|
+
headers: { ...base, ...extraHeaders },
|
|
674
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
519
675
|
signal: controller.signal
|
|
520
676
|
});
|
|
521
677
|
} catch (err) {
|
|
522
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
523
|
-
throw new SGLConnectionError(`Request to ${url} timed out`);
|
|
524
|
-
}
|
|
525
678
|
throw new SGLConnectionError(
|
|
526
|
-
`Could not
|
|
679
|
+
`Could not reach ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
527
680
|
);
|
|
528
681
|
} finally {
|
|
529
682
|
clearTimeout(timer);
|
|
530
683
|
}
|
|
531
|
-
if (response.status ===
|
|
532
|
-
|
|
533
|
-
|
|
684
|
+
if (response.status === 204) return {};
|
|
685
|
+
const text = await response.text();
|
|
686
|
+
let parsed;
|
|
687
|
+
try {
|
|
688
|
+
parsed = text ? JSON.parse(text) : void 0;
|
|
689
|
+
} catch {
|
|
690
|
+
parsed = text;
|
|
534
691
|
}
|
|
535
692
|
if (!response.ok) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const err = errorBody?.error;
|
|
541
|
-
if (typeof err === "string") message = err;
|
|
542
|
-
} catch {
|
|
693
|
+
const body2 = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
694
|
+
const detail = typeof body2?.detail === "string" && body2.detail || typeof body2?.error === "string" && body2.error || "";
|
|
695
|
+
if (response.status === 401 || response.status === 403) {
|
|
696
|
+
throw new SGLAuthError(response.status, detail || "unauthorized", body2);
|
|
543
697
|
}
|
|
544
|
-
|
|
698
|
+
if (response.status === 404) {
|
|
699
|
+
throw new SGLNotFoundError(detail || "not found", body2);
|
|
700
|
+
}
|
|
701
|
+
throw new SGLAPIError(response.status, detail || `HTTP ${response.status}`, body2);
|
|
545
702
|
}
|
|
546
|
-
return
|
|
703
|
+
return parsed;
|
|
547
704
|
}
|
|
548
|
-
//
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
705
|
+
// ── Discovery ────────────────────────────────────────────────────────────
|
|
706
|
+
/**
|
|
707
|
+
* The public catalogue.
|
|
708
|
+
*
|
|
709
|
+
* Sends NO credential, even when the client holds one. `GET /processors` is owner-scoped when a
|
|
710
|
+
* key is presented and public otherwise, so passing the key here would silently return your own
|
|
711
|
+
* processors instead of the catalogue — the opposite of what the name promises. Use `list()`
|
|
712
|
+
* when you want yours.
|
|
713
|
+
*/
|
|
714
|
+
async catalogue() {
|
|
715
|
+
return this.request("GET", "/processors", void 0, void 0, false);
|
|
556
716
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
return this.requestWithPayment(
|
|
561
|
-
"POST",
|
|
562
|
-
`/grid/processors/${encodeURIComponent(processorName)}/invoke`,
|
|
563
|
-
body,
|
|
564
|
-
options?.paymentHeader
|
|
565
|
-
);
|
|
717
|
+
/** Processors owned by this key's wallet. Needs `processors:read`. */
|
|
718
|
+
async list() {
|
|
719
|
+
return this.request("GET", "/processors");
|
|
566
720
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
721
|
+
/** Owner projection when the key owns it, public projection otherwise. */
|
|
722
|
+
async get(slug) {
|
|
723
|
+
return this.request("GET", `/processors/${encodeURIComponent(slug)}`);
|
|
724
|
+
}
|
|
725
|
+
// ── Lifecycle (needs processors:write) ───────────────────────────────────
|
|
726
|
+
/**
|
|
727
|
+
* Deploy. The wallet behind the key becomes `owner_wallet`, which is also the x402 `payTo` and
|
|
728
|
+
* the runtime-billing account — so the key must be minted on a SOLANA wallet or this returns
|
|
729
|
+
* `400 solana_wallet_required`.
|
|
730
|
+
*
|
|
731
|
+
* The `invoke_token` in the response is shown once and never again.
|
|
732
|
+
*/
|
|
733
|
+
async deploy(input) {
|
|
734
|
+
return this.request("POST", "/processors", input);
|
|
735
|
+
}
|
|
736
|
+
/** Push new code, a new manifest, or both. Omitting `manifest` keeps the stored one. */
|
|
737
|
+
async update(slug, input) {
|
|
738
|
+
return this.request("PATCH", `/processors/${encodeURIComponent(slug)}`, input);
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Delete. **Irreversible, and the slug is burned forever** — it can never be reused, by you or
|
|
742
|
+
* anyone. In-flight runs finish first; the code is wiped when they drain.
|
|
743
|
+
*/
|
|
744
|
+
async delete(slug) {
|
|
745
|
+
return this.request("DELETE", `/processors/${encodeURIComponent(slug)}`);
|
|
746
|
+
}
|
|
747
|
+
/** Stop or restart traffic WITHOUT losing the slug. This is the switch, not `delete`. */
|
|
748
|
+
async setPaused(slug, paused) {
|
|
749
|
+
return this.request("PUT", `/processors/${encodeURIComponent(slug)}/pause`, { paused });
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* List or unlist publicly. Instant, no review step.
|
|
753
|
+
*
|
|
754
|
+
* Unlisting is NOT stopping: an unlisted processor keeps answering anyone holding the URL or an
|
|
755
|
+
* invoke token, earning nothing while still drawing compute from your balance. Use `setPaused`.
|
|
756
|
+
*/
|
|
757
|
+
async setListing(slug, listed) {
|
|
758
|
+
return this.request("PUT", `/processors/${encodeURIComponent(slug)}/listing`, { listed });
|
|
759
|
+
}
|
|
760
|
+
/** Set secret VALUES. Each name must already be declared in `manifest.secrets`. */
|
|
761
|
+
async setSecrets(slug, values) {
|
|
762
|
+
return this.request("PUT", `/processors/${encodeURIComponent(slug)}/secrets`, { values });
|
|
763
|
+
}
|
|
764
|
+
/** Mint a new invoke token. The old one stops working immediately. */
|
|
765
|
+
async rotateToken(slug) {
|
|
766
|
+
return this.request("POST", `/processors/${encodeURIComponent(slug)}/rotate-token`);
|
|
767
|
+
}
|
|
768
|
+
// ── Observability ────────────────────────────────────────────────────────
|
|
769
|
+
async runs(slug) {
|
|
770
|
+
return this.request("GET", `/processors/${encodeURIComponent(slug)}/runs`);
|
|
771
|
+
}
|
|
772
|
+
async run_(slug, runId) {
|
|
573
773
|
return this.request(
|
|
574
774
|
"GET",
|
|
575
|
-
`/
|
|
775
|
+
`/processors/${encodeURIComponent(slug)}/runs/${encodeURIComponent(runId)}`
|
|
576
776
|
);
|
|
577
777
|
}
|
|
578
|
-
|
|
579
|
-
|
|
778
|
+
/** Sales (paid straight to your wallet, with the on-chain tx per row) and runtime spend. */
|
|
779
|
+
async earnings(slug) {
|
|
780
|
+
return this.request("GET", `/processors/${encodeURIComponent(slug)}/earnings`);
|
|
580
781
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
`/grid/processors/${processorId}`,
|
|
585
|
-
wallet
|
|
586
|
-
);
|
|
782
|
+
/** The processor's own key/value state. Read-only from out here, by design. */
|
|
783
|
+
async kv(slug) {
|
|
784
|
+
return this.request("GET", `/processors/${encodeURIComponent(slug)}/kv`);
|
|
587
785
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
786
|
+
// ── Webhooks ─────────────────────────────────────────────────────────────
|
|
787
|
+
async getWebhook(slug) {
|
|
788
|
+
return this.request("GET", `/processors/${encodeURIComponent(slug)}/webhook`);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Register or replace. We immediately POST a signed verification to the URL: it must answer 2xx
|
|
792
|
+
* or the webhook stays registered-but-inactive and delivers nothing. The signing secret comes
|
|
793
|
+
* back EXACTLY ONCE.
|
|
794
|
+
*/
|
|
795
|
+
async setWebhook(slug, url) {
|
|
796
|
+
return this.request("PUT", `/processors/${encodeURIComponent(slug)}/webhook`, { url });
|
|
797
|
+
}
|
|
798
|
+
async deleteWebhook(slug) {
|
|
799
|
+
return this.request("DELETE", `/processors/${encodeURIComponent(slug)}/webhook`);
|
|
800
|
+
}
|
|
801
|
+
async testWebhook(slug) {
|
|
802
|
+
return this.request("POST", `/processors/${encodeURIComponent(slug)}/webhook/test`);
|
|
803
|
+
}
|
|
804
|
+
// ── Invoking ─────────────────────────────────────────────────────────────
|
|
805
|
+
/**
|
|
806
|
+
* Run YOUR OWN processor with the invoke token from `deploy()`.
|
|
807
|
+
*
|
|
808
|
+
* Not the API key: the run route deliberately does not read a key as an ownership claim, because
|
|
809
|
+
* it is the only route with both a money path and an anonymous buyer lane. You pay for the
|
|
810
|
+
* compute; nobody pays at call time.
|
|
811
|
+
*/
|
|
812
|
+
async run(slug, input, invokeToken) {
|
|
813
|
+
return this.request(
|
|
814
|
+
"POST",
|
|
815
|
+
`/processors/${encodeURIComponent(slug)}/run`,
|
|
816
|
+
{ input },
|
|
817
|
+
{ Authorization: `Bearer ${invokeToken}` },
|
|
818
|
+
false
|
|
597
819
|
);
|
|
598
820
|
}
|
|
821
|
+
/**
|
|
822
|
+
* Run someone else's processor as a buyer, with an x402 payment header.
|
|
823
|
+
*
|
|
824
|
+
* Call once WITHOUT `paymentHeader` to get the 402 and its `accepts` array — one entry per chain
|
|
825
|
+
* that publisher takes. Match on `network`, pay that entry, and retry with the header.
|
|
826
|
+
*
|
|
827
|
+
* Re-sending the SAME header returns the run that payment already bought and does NOT charge
|
|
828
|
+
* again. That is the recovery path for every failure mode, because **there are no refunds**: the
|
|
829
|
+
* money went straight to the publisher and the platform never held it.
|
|
830
|
+
*/
|
|
831
|
+
async runWithPayment(slug, input, paymentHeader, acceptNetworks) {
|
|
832
|
+
const extra = {};
|
|
833
|
+
if (paymentHeader) extra["X-Payment"] = paymentHeader;
|
|
834
|
+
if (acceptNetworks?.length) extra["X-Accept-Networks"] = acceptNetworks.join(",");
|
|
835
|
+
return this.request("POST", `/processors/${encodeURIComponent(slug)}/run`, { input }, extra, false);
|
|
836
|
+
}
|
|
599
837
|
};
|
|
600
838
|
export {
|
|
601
839
|
DEFAULT_BASE_URL,
|
|
602
840
|
GridClient,
|
|
841
|
+
PROCESSORS_BASE_URL,
|
|
842
|
+
ProcessorsClient,
|
|
603
843
|
SGLAPIError,
|
|
604
844
|
SGLAuthError,
|
|
605
845
|
SGLConnectionError,
|
|
606
846
|
SGLError,
|
|
607
|
-
SGLNotFoundError
|
|
847
|
+
SGLNotFoundError,
|
|
848
|
+
VAULT_URL,
|
|
849
|
+
VaultClient,
|
|
850
|
+
decryptEnvelope,
|
|
851
|
+
encryptEnvelope,
|
|
852
|
+
parseAadFromKey
|
|
608
853
|
};
|
|
609
854
|
//# sourceMappingURL=index.mjs.map
|