@tangle-network/tcloud 0.2.0 → 0.4.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/shielded.cjs CHANGED
@@ -254,6 +254,7 @@ var PrivateRouter = class {
254
254
 
255
255
  // src/client.ts
256
256
  var DEFAULT_BASE_URL = "https://router.tangle.tools/v1";
257
+ var SDK_VERSION = "0.4.0";
257
258
  async function proxiedFetch(privacy, url, init, streaming) {
258
259
  if (!privacy || privacy.mode === "direct") {
259
260
  return fetch(url, init);
@@ -300,8 +301,10 @@ var DEFAULT_RETRY = {
300
301
  retryableStatuses: [429, 500, 502, 503, 504]
301
302
  };
302
303
  var DEFAULT_TIMEOUT_MS = 6e4;
304
+ var DEFAULT_PLATFORM_URL = "https://id.tangle.tools";
303
305
  var TCloudClient = class _TCloudClient {
304
306
  baseURL;
307
+ platformURL;
305
308
  apiKey;
306
309
  model;
307
310
  headers;
@@ -318,6 +321,7 @@ var TCloudClient = class _TCloudClient {
318
321
  static OPERATORS_TTL_MS = 5 * 60 * 1e3;
319
322
  constructor(config = {}) {
320
323
  this.baseURL = (config.baseURL || DEFAULT_BASE_URL).replace(/\/$/, "");
324
+ this.platformURL = (config.platformURL || DEFAULT_PLATFORM_URL).replace(/\/$/, "");
321
325
  this.apiKey = config.apiKey || process.env.TCLOUD_API_KEY;
322
326
  this.model = config.model || "gpt-4o-mini";
323
327
  this.privacy = config.privacy;
@@ -326,7 +330,7 @@ var TCloudClient = class _TCloudClient {
326
330
  this.timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
327
331
  this.headers = {
328
332
  "Content-Type": "application/json",
329
- "X-Tangle-Client": "tcloud-sdk/0.2.0"
333
+ "X-Tangle-Client": `tcloud-sdk/${SDK_VERSION}`
330
334
  };
331
335
  if (this.apiKey) {
332
336
  this.headers["Authorization"] = `Bearer ${this.apiKey}`;
@@ -506,10 +510,11 @@ var TCloudClient = class _TCloudClient {
506
510
  return res;
507
511
  }
508
512
  /**
509
- * Prepare headers for chat requests — operator routing + SpendAuth.
513
+ * Prepare headers for chat requests — operator routing + SpendAuth +
514
+ * bridge short-circuit headers when `options.bridge` is set.
510
515
  * Shared between chat() and chatStream() to eliminate duplication.
511
516
  */
512
- async _prepareChatRequest(model) {
517
+ async _prepareChatRequest(model, bridge) {
513
518
  const headers = { ...this.headers };
514
519
  if (this.spendAuthFn) {
515
520
  const auth = await this.spendAuthFn();
@@ -526,12 +531,28 @@ var TCloudClient = class _TCloudClient {
526
531
  delete headers["Authorization"];
527
532
  }
528
533
  }
534
+ if (bridge) {
535
+ headers["X-Bridge-Unlock"] = bridge.unlock;
536
+ if (bridge.resume) headers["X-Resume"] = bridge.resume;
537
+ if (bridge.bridgeUrl) headers["X-Bridge-Url"] = bridge.bridgeUrl;
538
+ if (bridge.bridgeBearer) headers["X-Bridge-Bearer"] = bridge.bridgeBearer;
539
+ }
529
540
  return { headers, baseURL };
530
541
  }
542
+ /**
543
+ * Resolve the effective model string. When a bridge is set, rewrite to
544
+ * `bridge/<harness>/<model>` (or `bridge/<harness>` if no model).
545
+ */
546
+ _effectiveModel(options) {
547
+ if (options.bridge) {
548
+ return options.bridge.model ? `bridge/${options.bridge.harness}/${options.bridge.model}` : `bridge/${options.bridge.harness}`;
549
+ }
550
+ return options.model || this.model;
551
+ }
531
552
  /** Build the chat completions request body */
532
553
  _chatBody(options, stream) {
533
554
  return JSON.stringify({
534
- model: options.model || this.model,
555
+ model: this._effectiveModel(options),
535
556
  messages: options.messages,
536
557
  temperature: options.temperature,
537
558
  max_tokens: options.maxTokens,
@@ -543,13 +564,17 @@ var TCloudClient = class _TCloudClient {
543
564
  response_format: options.responseFormat,
544
565
  tools: options.tools,
545
566
  tool_choice: options.toolChoice,
567
+ ...options.gateway ? { gateway: options.gateway } : {},
546
568
  ...options.providerOptions
547
569
  });
548
570
  }
549
571
  /** Chat completion (non-streaming) */
550
572
  async chat(options) {
551
573
  this.checkLimits();
552
- const { headers, baseURL } = await this._prepareChatRequest(options.model || this.model);
574
+ const { headers, baseURL } = await this._prepareChatRequest(
575
+ this._effectiveModel(options),
576
+ options.bridge
577
+ );
553
578
  const res = await this._doFetch(`${baseURL}/chat/completions`, {
554
579
  method: "POST",
555
580
  headers,
@@ -563,7 +588,10 @@ var TCloudClient = class _TCloudClient {
563
588
  async *chatStream(options) {
564
589
  this.checkLimits();
565
590
  this._requestCount++;
566
- const { headers, baseURL } = await this._prepareChatRequest(options.model || this.model);
591
+ const { headers, baseURL } = await this._prepareChatRequest(
592
+ this._effectiveModel(options),
593
+ options.bridge
594
+ );
567
595
  const res = await this._doFetch(`${baseURL}/chat/completions`, {
568
596
  method: "POST",
569
597
  headers,
@@ -592,6 +620,24 @@ var TCloudClient = class _TCloudClient {
592
620
  }
593
621
  }
594
622
  }
623
+ /**
624
+ * Bridge — scoped helper for a subscription-backed CLI harness behind
625
+ * the Tangle Router's cli-bridge. Returns a mini-client bound to
626
+ * (harness, unlock, resume) so you don't thread those through every
627
+ * call.
628
+ *
629
+ * ```ts
630
+ * const kimi = tcloud.bridge({ harness: 'kimi-code', model: 'kimi-for-coding', unlock: UNLOCK, resume: 'pr-42' })
631
+ * await kimi.ask('review this diff…')
632
+ * for await (const chunk of kimi.stream('continue…')) process.stdout.write(chunk)
633
+ * ```
634
+ *
635
+ * Sessions persist across process restarts — use the same `resume` id
636
+ * to land on the same CLI conversation (context intact, no replay tax).
637
+ */
638
+ bridge(cfg) {
639
+ return new BridgeSession(this, cfg);
640
+ }
595
641
  /** Convenience: send a single message and get the text response */
596
642
  async ask(message, modelOrOptions) {
597
643
  const options = typeof modelOrOptions === "string" ? { model: modelOrOptions } : modelOrOptions;
@@ -630,43 +676,89 @@ var TCloudClient = class _TCloudClient {
630
676
  const apiRoot = this.baseURL.replace(/\/v1$/, "");
631
677
  return this._fetch(`${apiRoot}/api/operators`);
632
678
  }
679
+ // ── Billing (via id.tangle.tools) ──
633
680
  /** Get credit balance */
634
681
  async credits() {
635
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
636
- return this._fetch(`${apiRoot}/api/billing`);
682
+ const { data } = await this._fetch(`${this.platformURL}/v1/billing/balance`);
683
+ return data;
637
684
  }
638
- /** Add credits */
685
+ /** Add credits via Stripe checkout. Returns the checkout URL. */
639
686
  async addCredits(amount) {
640
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
641
- return this._fetch(`${apiRoot}/api/billing`, {
687
+ const { data } = await this._fetch(`${this.platformURL}/v1/billing/topup`, {
642
688
  method: "POST",
643
689
  body: JSON.stringify({ amount })
644
690
  });
691
+ return data;
645
692
  }
646
- /** Create a new API key */
647
- async createKey(name) {
648
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
649
- return this._fetch(`${apiRoot}/api/keys`, {
693
+ /** Get transaction history */
694
+ async transactions(limit = 50) {
695
+ const { data } = await this._fetch(`${this.platformURL}/v1/billing/transactions?limit=${limit}`);
696
+ return data;
697
+ }
698
+ // ── API Keys (via id.tangle.tools) ──
699
+ /**
700
+ * Create a new API key.
701
+ * When called with an API key (not session), the new key is automatically
702
+ * a child of the calling key — enabling hierarchical key delegation.
703
+ *
704
+ * Pass `parentKeyId` explicitly to create a child of a specific key.
705
+ * Child keys inherit the parent's product scope, allowedModels, and rpmLimit
706
+ * if not specified. Budget cannot exceed the parent's remaining budget.
707
+ */
708
+ async createKey(opts) {
709
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys`, {
650
710
  method: "POST",
651
- body: JSON.stringify({ name })
711
+ body: JSON.stringify(opts)
652
712
  });
713
+ return data;
653
714
  }
654
- /** List API keys */
655
- async keys() {
656
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
657
- return this._fetch(`${apiRoot}/api/keys`);
715
+ /** Get a single API key by ID */
716
+ async getKey(id) {
717
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys/${id}`);
718
+ return data;
719
+ }
720
+ /**
721
+ * List API keys.
722
+ * Pass `children: true` to list child keys of the calling API key.
723
+ */
724
+ async keys(opts) {
725
+ const q = opts?.children ? "?children=true" : "";
726
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys${q}`);
727
+ return data;
728
+ }
729
+ /**
730
+ * Update an API key's limits.
731
+ * Can adjust budget, allowedModels, rpmLimit, expiresAt, and name.
732
+ */
733
+ async updateKey(id, updates) {
734
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys/${id}`, {
735
+ method: "PATCH",
736
+ body: JSON.stringify(updates)
737
+ });
738
+ return data;
658
739
  }
659
- /** Revoke an API key */
740
+ /** Revoke an API key. If the key has children, they are also revoked recursively. */
660
741
  async revokeKey(id) {
661
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
662
- const res = await proxiedFetch(this.privacy, `${apiRoot}/api/keys/${id}`, {
663
- method: "DELETE",
664
- headers: this.headers
665
- }, false);
666
- if (!res.ok) {
667
- const err = await res.json().catch(() => ({ error: res.statusText }));
668
- throw new TCloudError(res.status, err.error?.message || err.error || err.message || res.statusText);
669
- }
742
+ await this._fetch(`${this.platformURL}/v1/keys/${id}`, { method: "DELETE" });
743
+ }
744
+ /** Rotate an API key — creates new key with same config, revokes old */
745
+ async rotateKey(id) {
746
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys/${id}/rotate`, { method: "POST" });
747
+ return data;
748
+ }
749
+ // ── Projects (via id.tangle.tools) ──
750
+ /** Create a project for usage attribution */
751
+ async createProject(name, product) {
752
+ const { data } = await this._fetch(`${this.platformURL}/v1/projects`, {
753
+ method: "POST",
754
+ body: JSON.stringify({ name, product })
755
+ });
756
+ return data;
757
+ }
758
+ /** List projects */
759
+ async projects() {
760
+ const { data } = await this._fetch(`${this.platformURL}/v1/projects`);
761
+ return data;
670
762
  }
671
763
  /** Generate embeddings */
672
764
  async embeddings(options) {
@@ -977,6 +1069,61 @@ var TCloudClient = class _TCloudClient {
977
1069
  };
978
1070
  });
979
1071
  }
1072
+ // ── Eval ──────────────────────────────────────────────────────────────
1073
+ get _apiRoot() {
1074
+ return this.baseURL.replace(/\/v1$/, "");
1075
+ }
1076
+ async eval(opts) {
1077
+ return this._request(`${this._apiRoot}/api/eval`, { method: "POST", body: JSON.stringify(opts) });
1078
+ }
1079
+ async createSuite(opts) {
1080
+ return this._request(`${this._apiRoot}/api/eval/suites`, { method: "POST", body: JSON.stringify(opts) });
1081
+ }
1082
+ async listSuites() {
1083
+ return this._fetch(`${this._apiRoot}/api/eval/suites`);
1084
+ }
1085
+ async runSuite(suiteId, opts) {
1086
+ return this._request(`${this._apiRoot}/api/eval/suites/${suiteId}/runs`, { method: "POST", body: JSON.stringify(opts || {}) });
1087
+ }
1088
+ async listRuns(suiteId) {
1089
+ return this._fetch(`${this._apiRoot}/api/eval/suites/${suiteId}/runs`);
1090
+ }
1091
+ async getRun(runId) {
1092
+ return this._fetch(`${this._apiRoot}/api/eval/runs/${runId}`);
1093
+ }
1094
+ async setBaseline(runId) {
1095
+ await this._request(`${this._apiRoot}/api/eval/runs/${runId}`, { method: "PATCH", body: JSON.stringify({ baseline: true }) });
1096
+ }
1097
+ // ── Sandbox ──────────────────────────────────────────────────────────
1098
+ async sandboxPricing(opts) {
1099
+ const p = new URLSearchParams();
1100
+ if (opts?.cpu) p.set("cpu", String(opts.cpu));
1101
+ if (opts?.ram) p.set("ram", String(opts.ram));
1102
+ if (opts?.disk) p.set("disk", String(opts.disk));
1103
+ return this._fetch(`${this._apiRoot}/api/sandbox/pricing?${p}`);
1104
+ }
1105
+ async sandboxStatus() {
1106
+ return this._fetch(`${this._apiRoot}/api/sandbox/link-key`);
1107
+ }
1108
+ async sandboxProvision() {
1109
+ return this._request(`${this._apiRoot}/api/sandbox/provision`, { method: "POST" });
1110
+ }
1111
+ async sandboxCreate(opts) {
1112
+ return this._request(`${this._apiRoot}/api/sandbox/sessions`, { method: "POST", body: JSON.stringify(opts) });
1113
+ }
1114
+ async sandboxList() {
1115
+ return this._fetch(`${this._apiRoot}/api/sandbox/sessions`);
1116
+ }
1117
+ async sandboxStats(sandboxId) {
1118
+ return this._fetch(`${this._apiRoot}/api/sandbox/stats/${sandboxId}`);
1119
+ }
1120
+ async sandboxDestroy(sessionId) {
1121
+ return this._request(`${this._apiRoot}/api/sandbox/sessions/${sessionId}`, { method: "DELETE" });
1122
+ }
1123
+ // ── User Info ────────────────────────────────────────────────────────
1124
+ async userInfo() {
1125
+ return this._fetch(`${this._apiRoot}/api/auth/userinfo`);
1126
+ }
980
1127
  };
981
1128
  var ALL_TIERS = [
982
1129
  { name: "cpu-only", cpu: 4, ramGb: 16, gpu: 0, tee: false },
@@ -987,6 +1134,59 @@ var ALL_TIERS = [
987
1134
  { name: "max-gpu", cpu: 64, ramGb: 256, gpu: 4, tee: false },
988
1135
  { name: "max-gpu-tee", cpu: 64, ramGb: 256, gpu: 4, tee: true }
989
1136
  ];
1137
+ var BridgeSession = class _BridgeSession {
1138
+ constructor(client, cfg) {
1139
+ this.client = client;
1140
+ this.cfg = cfg;
1141
+ }
1142
+ /** Full chat completion (non-streaming). */
1143
+ async chat(options) {
1144
+ return this.client.chat({ ...options, bridge: this.cfg });
1145
+ }
1146
+ /** Stream OpenAI chat.completion.chunks. */
1147
+ chatStream(options) {
1148
+ return this.client.chatStream({ ...options, bridge: this.cfg });
1149
+ }
1150
+ /** One-shot: send a string, get the assistant text. */
1151
+ async ask(message, extra) {
1152
+ const completion = await this.chat({
1153
+ messages: [{ role: "user", content: message }],
1154
+ ...extra
1155
+ });
1156
+ return completion.choices[0]?.message?.content || "";
1157
+ }
1158
+ /** One-shot: send a string, stream text deltas. */
1159
+ async *stream(message, extra) {
1160
+ for await (const chunk of this.chatStream({
1161
+ messages: [{ role: "user", content: message }],
1162
+ ...extra
1163
+ })) {
1164
+ const content = chunk.choices?.[0]?.delta?.content;
1165
+ if (content) yield content;
1166
+ }
1167
+ }
1168
+ /** Turn-based: send full message history, get assistant text. */
1169
+ async turn(messages, extra) {
1170
+ const completion = await this.chat({ messages, ...extra });
1171
+ return completion.choices[0]?.message?.content || "";
1172
+ }
1173
+ /** Clone with a new resume id — same harness, different logical conversation. */
1174
+ withResume(resume) {
1175
+ return new _BridgeSession(this.client, { ...this.cfg, resume });
1176
+ }
1177
+ /** Clone with a different model inside the same harness. */
1178
+ withModel(model) {
1179
+ return new _BridgeSession(this.client, { ...this.cfg, model });
1180
+ }
1181
+ /** The effective model id that will land on the router (`bridge/<harness>/<model>`). */
1182
+ get model() {
1183
+ return this.cfg.model ? `bridge/${this.cfg.harness}/${this.cfg.model}` : `bridge/${this.cfg.harness}`;
1184
+ }
1185
+ /** The resume id currently bound to this session, if any. */
1186
+ get resume() {
1187
+ return this.cfg.resume;
1188
+ }
1189
+ };
990
1190
  function selectTiers(all, n) {
991
1191
  if (n >= all.length) return [...all];
992
1192
  if (n <= 1) return [all[0]];
@@ -1,5 +1,5 @@
1
1
  import { Hex } from 'viem';
2
- import { a as TCloudConfig, T as TCloudClient, w as SpendAuth } from './client-CcuHG7_w.cjs';
2
+ import { a as TCloudConfig, T as TCloudClient, H as SpendAuth } from './client-_ghO89WM.cjs';
3
3
 
4
4
  /**
5
5
  * tcloud/shielded — Ephemeral wallet generation, SpendAuth signing, private inference.
@@ -1,5 +1,5 @@
1
1
  import { Hex } from 'viem';
2
- import { a as TCloudConfig, T as TCloudClient, w as SpendAuth } from './client-CcuHG7_w.js';
2
+ import { a as TCloudConfig, T as TCloudClient, H as SpendAuth } from './client-_ghO89WM.js';
3
3
 
4
4
  /**
5
5
  * tcloud/shielded — Ephemeral wallet generation, SpendAuth signing, private inference.
package/dist/shielded.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  estimateCost,
4
4
  generateWallet,
5
5
  signSpendAuth
6
- } from "./chunk-VD4RNZOC.js";
7
- import "./chunk-HL4CXKET.js";
6
+ } from "./chunk-MKLCBBHZ.js";
7
+ import "./chunk-AKR7CS4P.js";
8
8
  export {
9
9
  createShieldedClient,
10
10
  estimateCost,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/tcloud",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "TypeScript SDK and CLI for Tangle AI Cloud — decentralized LLM inference",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -53,6 +53,7 @@
53
53
  "dependencies": {
54
54
  "@scure/bip32": "^2.0.1",
55
55
  "@scure/bip39": "^2.0.1",
56
+ "@tangle-network/sandbox": "^0.0.3",
56
57
  "commander": "^13.0.0",
57
58
  "viem": "^2.0.0"
58
59
  },