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