@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/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
- // -- Processor helpers ----------------------------------------------------
495
- async requestWithWalletAuth(method, path, wallet, body) {
496
- const url = `${this.baseUrl}${path}`;
497
- const controller = new AbortController();
498
- const timer = setTimeout(() => controller.abort(), this.timeout);
499
- const reqHeaders = {
500
- ...this.headers,
501
- "X-Auth-Address": wallet.address,
502
- "X-Auth-Chain": wallet.chain ?? "solana",
503
- "X-Auth-Signature": wallet.signature,
504
- "X-Auth-Timestamp": wallet.timestamp,
505
- "X-Auth-Nonce": wallet.nonce
506
- };
507
- let response;
508
- try {
509
- response = await fetch(url, {
510
- method,
511
- headers: reqHeaders,
512
- body: body ? JSON.stringify(body) : void 0,
513
- signal: controller.signal
514
- });
515
- } catch (err) {
516
- if (err instanceof Error && err.name === "AbortError") {
517
- throw new SGLConnectionError(`Request to ${url} timed out`);
518
- }
519
- throw new SGLConnectionError(
520
- `Could not connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`
521
- );
522
- } finally {
523
- clearTimeout(timer);
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 (!response.ok) {
526
- let errorBody;
527
- let message = response.statusText;
528
- try {
529
- errorBody = await response.json();
530
- const err = errorBody?.error;
531
- if (typeof err === "string") message = err;
532
- else if (err && typeof err === "object" && "message" in err)
533
- message = String(err.message);
534
- } catch {
535
- }
536
- if (response.status === 401 || response.status === 403) {
537
- throw new SGLAuthError(response.status, message, errorBody);
538
- }
539
- if (response.status === 404) {
540
- throw new SGLNotFoundError(message, errorBody);
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
- throw new SGLAPIError(response.status, message, errorBody);
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 requestWithPayment(method, path, body, paymentHeader) {
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: reqHeaders,
560
- body: body ? JSON.stringify(body) : void 0,
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 connect to ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`
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 === 402) {
574
- const requirements = await response.json();
575
- throw new SGLAPIError(402, "Payment required", requirements);
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
- let errorBody;
579
- let message = response.statusText;
580
- try {
581
- errorBody = await response.json();
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
- throw new SGLAPIError(response.status, message, errorBody);
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 await response.json();
752
+ return parsed;
589
753
  }
590
- // -- Processors -----------------------------------------------------------
591
- async deployProcessor(wallet, options) {
592
- return this.requestWithWalletAuth(
593
- "POST",
594
- "/grid/processors",
595
- wallet,
596
- options
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
- async invokeProcessor(processorName, input, options) {
600
- const body = { input };
601
- if (options?.paymentToken) body.payment_token = options.paymentToken;
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
- async listProcessors(options) {
610
- const params = new URLSearchParams();
611
- if (options?.owner) params.set("owner", options.owner);
612
- if (options?.page != null) params.set("page", String(options.page));
613
- if (options?.limit != null) params.set("limit", String(options.limit));
614
- const qs = params.toString();
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
- `/grid/processors${qs ? `?${qs}` : ""}`
824
+ `/processors/${encodeURIComponent(slug)}/runs/${encodeURIComponent(runId)}`
618
825
  );
619
826
  }
620
- async getProcessor(processorId) {
621
- return this.request("GET", `/grid/processors/${processorId}`);
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
- async deleteProcessor(processorId, wallet) {
624
- return this.requestWithWalletAuth(
625
- "DELETE",
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
- async getProcessorLogs(processorId, wallet, options) {
631
- const params = new URLSearchParams();
632
- if (options?.page != null) params.set("page", String(options.page));
633
- if (options?.limit != null) params.set("limit", String(options.limit));
634
- const qs = params.toString();
635
- return this.requestWithWalletAuth(
636
- "GET",
637
- `/grid/processors/${processorId}/logs${qs ? `?${qs}` : ""}`,
638
- wallet
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