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