@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.
@@ -213,6 +213,7 @@ var PrivateRouter = class {
213
213
 
214
214
  // src/client.ts
215
215
  var DEFAULT_BASE_URL = "https://router.tangle.tools/v1";
216
+ var SDK_VERSION = "0.4.0";
216
217
  async function proxiedFetch(privacy, url, init, streaming) {
217
218
  if (!privacy || privacy.mode === "direct") {
218
219
  return fetch(url, init);
@@ -259,8 +260,10 @@ var DEFAULT_RETRY = {
259
260
  retryableStatuses: [429, 500, 502, 503, 504]
260
261
  };
261
262
  var DEFAULT_TIMEOUT_MS = 6e4;
263
+ var DEFAULT_PLATFORM_URL = "https://id.tangle.tools";
262
264
  var TCloudClient = class _TCloudClient {
263
265
  baseURL;
266
+ platformURL;
264
267
  apiKey;
265
268
  model;
266
269
  headers;
@@ -277,6 +280,7 @@ var TCloudClient = class _TCloudClient {
277
280
  static OPERATORS_TTL_MS = 5 * 60 * 1e3;
278
281
  constructor(config = {}) {
279
282
  this.baseURL = (config.baseURL || DEFAULT_BASE_URL).replace(/\/$/, "");
283
+ this.platformURL = (config.platformURL || DEFAULT_PLATFORM_URL).replace(/\/$/, "");
280
284
  this.apiKey = config.apiKey || process.env.TCLOUD_API_KEY;
281
285
  this.model = config.model || "gpt-4o-mini";
282
286
  this.privacy = config.privacy;
@@ -285,7 +289,7 @@ var TCloudClient = class _TCloudClient {
285
289
  this.timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
286
290
  this.headers = {
287
291
  "Content-Type": "application/json",
288
- "X-Tangle-Client": "tcloud-sdk/0.2.0"
292
+ "X-Tangle-Client": `tcloud-sdk/${SDK_VERSION}`
289
293
  };
290
294
  if (this.apiKey) {
291
295
  this.headers["Authorization"] = `Bearer ${this.apiKey}`;
@@ -465,10 +469,11 @@ var TCloudClient = class _TCloudClient {
465
469
  return res;
466
470
  }
467
471
  /**
468
- * Prepare headers for chat requests — operator routing + SpendAuth.
472
+ * Prepare headers for chat requests — operator routing + SpendAuth +
473
+ * bridge short-circuit headers when `options.bridge` is set.
469
474
  * Shared between chat() and chatStream() to eliminate duplication.
470
475
  */
471
- async _prepareChatRequest(model) {
476
+ async _prepareChatRequest(model, bridge) {
472
477
  const headers = { ...this.headers };
473
478
  if (this.spendAuthFn) {
474
479
  const auth = await this.spendAuthFn();
@@ -485,12 +490,28 @@ var TCloudClient = class _TCloudClient {
485
490
  delete headers["Authorization"];
486
491
  }
487
492
  }
493
+ if (bridge) {
494
+ headers["X-Bridge-Unlock"] = bridge.unlock;
495
+ if (bridge.resume) headers["X-Resume"] = bridge.resume;
496
+ if (bridge.bridgeUrl) headers["X-Bridge-Url"] = bridge.bridgeUrl;
497
+ if (bridge.bridgeBearer) headers["X-Bridge-Bearer"] = bridge.bridgeBearer;
498
+ }
488
499
  return { headers, baseURL };
489
500
  }
501
+ /**
502
+ * Resolve the effective model string. When a bridge is set, rewrite to
503
+ * `bridge/<harness>/<model>` (or `bridge/<harness>` if no model).
504
+ */
505
+ _effectiveModel(options) {
506
+ if (options.bridge) {
507
+ return options.bridge.model ? `bridge/${options.bridge.harness}/${options.bridge.model}` : `bridge/${options.bridge.harness}`;
508
+ }
509
+ return options.model || this.model;
510
+ }
490
511
  /** Build the chat completions request body */
491
512
  _chatBody(options, stream) {
492
513
  return JSON.stringify({
493
- model: options.model || this.model,
514
+ model: this._effectiveModel(options),
494
515
  messages: options.messages,
495
516
  temperature: options.temperature,
496
517
  max_tokens: options.maxTokens,
@@ -502,13 +523,17 @@ var TCloudClient = class _TCloudClient {
502
523
  response_format: options.responseFormat,
503
524
  tools: options.tools,
504
525
  tool_choice: options.toolChoice,
526
+ ...options.gateway ? { gateway: options.gateway } : {},
505
527
  ...options.providerOptions
506
528
  });
507
529
  }
508
530
  /** Chat completion (non-streaming) */
509
531
  async chat(options) {
510
532
  this.checkLimits();
511
- const { headers, baseURL } = await this._prepareChatRequest(options.model || this.model);
533
+ const { headers, baseURL } = await this._prepareChatRequest(
534
+ this._effectiveModel(options),
535
+ options.bridge
536
+ );
512
537
  const res = await this._doFetch(`${baseURL}/chat/completions`, {
513
538
  method: "POST",
514
539
  headers,
@@ -522,7 +547,10 @@ var TCloudClient = class _TCloudClient {
522
547
  async *chatStream(options) {
523
548
  this.checkLimits();
524
549
  this._requestCount++;
525
- const { headers, baseURL } = await this._prepareChatRequest(options.model || this.model);
550
+ const { headers, baseURL } = await this._prepareChatRequest(
551
+ this._effectiveModel(options),
552
+ options.bridge
553
+ );
526
554
  const res = await this._doFetch(`${baseURL}/chat/completions`, {
527
555
  method: "POST",
528
556
  headers,
@@ -551,6 +579,24 @@ var TCloudClient = class _TCloudClient {
551
579
  }
552
580
  }
553
581
  }
582
+ /**
583
+ * Bridge — scoped helper for a subscription-backed CLI harness behind
584
+ * the Tangle Router's cli-bridge. Returns a mini-client bound to
585
+ * (harness, unlock, resume) so you don't thread those through every
586
+ * call.
587
+ *
588
+ * ```ts
589
+ * const kimi = tcloud.bridge({ harness: 'kimi-code', model: 'kimi-for-coding', unlock: UNLOCK, resume: 'pr-42' })
590
+ * await kimi.ask('review this diff…')
591
+ * for await (const chunk of kimi.stream('continue…')) process.stdout.write(chunk)
592
+ * ```
593
+ *
594
+ * Sessions persist across process restarts — use the same `resume` id
595
+ * to land on the same CLI conversation (context intact, no replay tax).
596
+ */
597
+ bridge(cfg) {
598
+ return new BridgeSession(this, cfg);
599
+ }
554
600
  /** Convenience: send a single message and get the text response */
555
601
  async ask(message, modelOrOptions) {
556
602
  const options = typeof modelOrOptions === "string" ? { model: modelOrOptions } : modelOrOptions;
@@ -589,43 +635,89 @@ var TCloudClient = class _TCloudClient {
589
635
  const apiRoot = this.baseURL.replace(/\/v1$/, "");
590
636
  return this._fetch(`${apiRoot}/api/operators`);
591
637
  }
638
+ // ── Billing (via id.tangle.tools) ──
592
639
  /** Get credit balance */
593
640
  async credits() {
594
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
595
- return this._fetch(`${apiRoot}/api/billing`);
641
+ const { data } = await this._fetch(`${this.platformURL}/v1/billing/balance`);
642
+ return data;
596
643
  }
597
- /** Add credits */
644
+ /** Add credits via Stripe checkout. Returns the checkout URL. */
598
645
  async addCredits(amount) {
599
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
600
- return this._fetch(`${apiRoot}/api/billing`, {
646
+ const { data } = await this._fetch(`${this.platformURL}/v1/billing/topup`, {
601
647
  method: "POST",
602
648
  body: JSON.stringify({ amount })
603
649
  });
650
+ return data;
604
651
  }
605
- /** Create a new API key */
606
- async createKey(name) {
607
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
608
- return this._fetch(`${apiRoot}/api/keys`, {
652
+ /** Get transaction history */
653
+ async transactions(limit = 50) {
654
+ const { data } = await this._fetch(`${this.platformURL}/v1/billing/transactions?limit=${limit}`);
655
+ return data;
656
+ }
657
+ // ── API Keys (via id.tangle.tools) ──
658
+ /**
659
+ * Create a new API key.
660
+ * When called with an API key (not session), the new key is automatically
661
+ * a child of the calling key — enabling hierarchical key delegation.
662
+ *
663
+ * Pass `parentKeyId` explicitly to create a child of a specific key.
664
+ * Child keys inherit the parent's product scope, allowedModels, and rpmLimit
665
+ * if not specified. Budget cannot exceed the parent's remaining budget.
666
+ */
667
+ async createKey(opts) {
668
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys`, {
609
669
  method: "POST",
610
- body: JSON.stringify({ name })
670
+ body: JSON.stringify(opts)
611
671
  });
672
+ return data;
612
673
  }
613
- /** List API keys */
614
- async keys() {
615
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
616
- return this._fetch(`${apiRoot}/api/keys`);
674
+ /** Get a single API key by ID */
675
+ async getKey(id) {
676
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys/${id}`);
677
+ return data;
678
+ }
679
+ /**
680
+ * List API keys.
681
+ * Pass `children: true` to list child keys of the calling API key.
682
+ */
683
+ async keys(opts) {
684
+ const q = opts?.children ? "?children=true" : "";
685
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys${q}`);
686
+ return data;
687
+ }
688
+ /**
689
+ * Update an API key's limits.
690
+ * Can adjust budget, allowedModels, rpmLimit, expiresAt, and name.
691
+ */
692
+ async updateKey(id, updates) {
693
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys/${id}`, {
694
+ method: "PATCH",
695
+ body: JSON.stringify(updates)
696
+ });
697
+ return data;
617
698
  }
618
- /** Revoke an API key */
699
+ /** Revoke an API key. If the key has children, they are also revoked recursively. */
619
700
  async revokeKey(id) {
620
- const apiRoot = this.baseURL.replace(/\/v1$/, "");
621
- const res = await proxiedFetch(this.privacy, `${apiRoot}/api/keys/${id}`, {
622
- method: "DELETE",
623
- headers: this.headers
624
- }, false);
625
- if (!res.ok) {
626
- const err = await res.json().catch(() => ({ error: res.statusText }));
627
- throw new TCloudError(res.status, err.error?.message || err.error || err.message || res.statusText);
628
- }
701
+ await this._fetch(`${this.platformURL}/v1/keys/${id}`, { method: "DELETE" });
702
+ }
703
+ /** Rotate an API key — creates new key with same config, revokes old */
704
+ async rotateKey(id) {
705
+ const { data } = await this._fetch(`${this.platformURL}/v1/keys/${id}/rotate`, { method: "POST" });
706
+ return data;
707
+ }
708
+ // ── Projects (via id.tangle.tools) ──
709
+ /** Create a project for usage attribution */
710
+ async createProject(name, product) {
711
+ const { data } = await this._fetch(`${this.platformURL}/v1/projects`, {
712
+ method: "POST",
713
+ body: JSON.stringify({ name, product })
714
+ });
715
+ return data;
716
+ }
717
+ /** List projects */
718
+ async projects() {
719
+ const { data } = await this._fetch(`${this.platformURL}/v1/projects`);
720
+ return data;
629
721
  }
630
722
  /** Generate embeddings */
631
723
  async embeddings(options) {
@@ -936,6 +1028,61 @@ var TCloudClient = class _TCloudClient {
936
1028
  };
937
1029
  });
938
1030
  }
1031
+ // ── Eval ──────────────────────────────────────────────────────────────
1032
+ get _apiRoot() {
1033
+ return this.baseURL.replace(/\/v1$/, "");
1034
+ }
1035
+ async eval(opts) {
1036
+ return this._request(`${this._apiRoot}/api/eval`, { method: "POST", body: JSON.stringify(opts) });
1037
+ }
1038
+ async createSuite(opts) {
1039
+ return this._request(`${this._apiRoot}/api/eval/suites`, { method: "POST", body: JSON.stringify(opts) });
1040
+ }
1041
+ async listSuites() {
1042
+ return this._fetch(`${this._apiRoot}/api/eval/suites`);
1043
+ }
1044
+ async runSuite(suiteId, opts) {
1045
+ return this._request(`${this._apiRoot}/api/eval/suites/${suiteId}/runs`, { method: "POST", body: JSON.stringify(opts || {}) });
1046
+ }
1047
+ async listRuns(suiteId) {
1048
+ return this._fetch(`${this._apiRoot}/api/eval/suites/${suiteId}/runs`);
1049
+ }
1050
+ async getRun(runId) {
1051
+ return this._fetch(`${this._apiRoot}/api/eval/runs/${runId}`);
1052
+ }
1053
+ async setBaseline(runId) {
1054
+ await this._request(`${this._apiRoot}/api/eval/runs/${runId}`, { method: "PATCH", body: JSON.stringify({ baseline: true }) });
1055
+ }
1056
+ // ── Sandbox ──────────────────────────────────────────────────────────
1057
+ async sandboxPricing(opts) {
1058
+ const p = new URLSearchParams();
1059
+ if (opts?.cpu) p.set("cpu", String(opts.cpu));
1060
+ if (opts?.ram) p.set("ram", String(opts.ram));
1061
+ if (opts?.disk) p.set("disk", String(opts.disk));
1062
+ return this._fetch(`${this._apiRoot}/api/sandbox/pricing?${p}`);
1063
+ }
1064
+ async sandboxStatus() {
1065
+ return this._fetch(`${this._apiRoot}/api/sandbox/link-key`);
1066
+ }
1067
+ async sandboxProvision() {
1068
+ return this._request(`${this._apiRoot}/api/sandbox/provision`, { method: "POST" });
1069
+ }
1070
+ async sandboxCreate(opts) {
1071
+ return this._request(`${this._apiRoot}/api/sandbox/sessions`, { method: "POST", body: JSON.stringify(opts) });
1072
+ }
1073
+ async sandboxList() {
1074
+ return this._fetch(`${this._apiRoot}/api/sandbox/sessions`);
1075
+ }
1076
+ async sandboxStats(sandboxId) {
1077
+ return this._fetch(`${this._apiRoot}/api/sandbox/stats/${sandboxId}`);
1078
+ }
1079
+ async sandboxDestroy(sessionId) {
1080
+ return this._request(`${this._apiRoot}/api/sandbox/sessions/${sessionId}`, { method: "DELETE" });
1081
+ }
1082
+ // ── User Info ────────────────────────────────────────────────────────
1083
+ async userInfo() {
1084
+ return this._fetch(`${this._apiRoot}/api/auth/userinfo`);
1085
+ }
939
1086
  };
940
1087
  var ALL_TIERS = [
941
1088
  { name: "cpu-only", cpu: 4, ramGb: 16, gpu: 0, tee: false },
@@ -946,6 +1093,59 @@ var ALL_TIERS = [
946
1093
  { name: "max-gpu", cpu: 64, ramGb: 256, gpu: 4, tee: false },
947
1094
  { name: "max-gpu-tee", cpu: 64, ramGb: 256, gpu: 4, tee: true }
948
1095
  ];
1096
+ var BridgeSession = class _BridgeSession {
1097
+ constructor(client, cfg) {
1098
+ this.client = client;
1099
+ this.cfg = cfg;
1100
+ }
1101
+ /** Full chat completion (non-streaming). */
1102
+ async chat(options) {
1103
+ return this.client.chat({ ...options, bridge: this.cfg });
1104
+ }
1105
+ /** Stream OpenAI chat.completion.chunks. */
1106
+ chatStream(options) {
1107
+ return this.client.chatStream({ ...options, bridge: this.cfg });
1108
+ }
1109
+ /** One-shot: send a string, get the assistant text. */
1110
+ async ask(message, extra) {
1111
+ const completion = await this.chat({
1112
+ messages: [{ role: "user", content: message }],
1113
+ ...extra
1114
+ });
1115
+ return completion.choices[0]?.message?.content || "";
1116
+ }
1117
+ /** One-shot: send a string, stream text deltas. */
1118
+ async *stream(message, extra) {
1119
+ for await (const chunk of this.chatStream({
1120
+ messages: [{ role: "user", content: message }],
1121
+ ...extra
1122
+ })) {
1123
+ const content = chunk.choices?.[0]?.delta?.content;
1124
+ if (content) yield content;
1125
+ }
1126
+ }
1127
+ /** Turn-based: send full message history, get assistant text. */
1128
+ async turn(messages, extra) {
1129
+ const completion = await this.chat({ messages, ...extra });
1130
+ return completion.choices[0]?.message?.content || "";
1131
+ }
1132
+ /** Clone with a new resume id — same harness, different logical conversation. */
1133
+ withResume(resume) {
1134
+ return new _BridgeSession(this.client, { ...this.cfg, resume });
1135
+ }
1136
+ /** Clone with a different model inside the same harness. */
1137
+ withModel(model) {
1138
+ return new _BridgeSession(this.client, { ...this.cfg, model });
1139
+ }
1140
+ /** The effective model id that will land on the router (`bridge/<harness>/<model>`). */
1141
+ get model() {
1142
+ return this.cfg.model ? `bridge/${this.cfg.harness}/${this.cfg.model}` : `bridge/${this.cfg.harness}`;
1143
+ }
1144
+ /** The resume id currently bound to this session, if any. */
1145
+ get resume() {
1146
+ return this.cfg.resume;
1147
+ }
1148
+ };
949
1149
  function selectTiers(all, n) {
950
1150
  if (n >= all.length) return [...all];
951
1151
  if (n <= 1) return [all[0]];
@@ -972,5 +1172,6 @@ var TCloudError = class extends Error {
972
1172
  export {
973
1173
  PrivateRouter,
974
1174
  TCloudClient,
1175
+ BridgeSession,
975
1176
  TCloudError
976
1177
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TCloudClient
3
- } from "./chunk-HL4CXKET.js";
3
+ } from "./chunk-AKR7CS4P.js";
4
4
 
5
5
  // src/shielded.ts
6
6
  import { privateKeyToAccount } from "viem/accounts";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createShieldedClient,
3
3
  generateWallet
4
- } from "./chunk-VD4RNZOC.js";
4
+ } from "./chunk-MKLCBBHZ.js";
5
5
  import {
6
6
  TCloudClient
7
- } from "./chunk-HL4CXKET.js";
7
+ } from "./chunk-AKR7CS4P.js";
8
8
 
9
9
  // src/index.ts
10
10
  var TCloud = class _TCloud extends TCloudClient {