@tarkahq/sdk 0.0.0-stage → 0.3.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.
Files changed (130) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +69 -0
  3. package/README.md +700 -2
  4. package/api-source.json +38 -0
  5. package/dist/cjs/client.d.ts +114 -0
  6. package/dist/cjs/client.js +174 -0
  7. package/dist/cjs/errors.d.ts +27 -0
  8. package/dist/cjs/errors.js +78 -0
  9. package/dist/cjs/generated/google/api/annotations.d.ts +1 -0
  10. package/dist/cjs/generated/google/api/annotations.js +10 -0
  11. package/dist/cjs/generated/google/api/http.d.ts +395 -0
  12. package/dist/cjs/generated/google/api/http.js +421 -0
  13. package/dist/cjs/generated/google/protobuf/descriptor.d.ts +873 -0
  14. package/dist/cjs/generated/google/protobuf/descriptor.js +3992 -0
  15. package/dist/cjs/generated/google/protobuf/empty.d.ts +35 -0
  16. package/dist/cjs/generated/google/protobuf/empty.js +58 -0
  17. package/dist/cjs/generated/google/protobuf/struct.d.ts +127 -0
  18. package/dist/cjs/generated/google/protobuf/struct.js +529 -0
  19. package/dist/cjs/generated/google/protobuf/timestamp.d.ts +128 -0
  20. package/dist/cjs/generated/google/protobuf/timestamp.js +95 -0
  21. package/dist/cjs/generated/protoc-gen-openapiv2/options/annotations.d.ts +1 -0
  22. package/dist/cjs/generated/protoc-gen-openapiv2/options/annotations.js +10 -0
  23. package/dist/cjs/generated/protoc-gen-openapiv2/options/openapiv2.d.ts +983 -0
  24. package/dist/cjs/generated/protoc-gen-openapiv2/options/openapiv2.js +4874 -0
  25. package/dist/cjs/generated/tarka/access/v1/access.d.ts +322 -0
  26. package/dist/cjs/generated/tarka/access/v1/access.js +2979 -0
  27. package/dist/cjs/generated/tarka/channels/v1/channels.d.ts +370 -0
  28. package/dist/cjs/generated/tarka/channels/v1/channels.js +3603 -0
  29. package/dist/cjs/generated/tarka/gateway/v1/gateway.d.ts +804 -0
  30. package/dist/cjs/generated/tarka/gateway/v1/gateway.js +8081 -0
  31. package/dist/cjs/generated/tarka/iam/v1/iam.d.ts +1291 -0
  32. package/dist/cjs/generated/tarka/iam/v1/iam.js +13207 -0
  33. package/dist/cjs/generated/tarka/inference/v1/inference.d.ts +699 -0
  34. package/dist/cjs/generated/tarka/inference/v1/inference.js +7058 -0
  35. package/dist/cjs/generated/tarka/provisioning/v1/agent_secrets.d.ts +70 -0
  36. package/dist/cjs/generated/tarka/provisioning/v1/agent_secrets.js +603 -0
  37. package/dist/cjs/generated/tarka/provisioning/v1/billing_quote.d.ts +171 -0
  38. package/dist/cjs/generated/tarka/provisioning/v1/billing_quote.js +1588 -0
  39. package/dist/cjs/generated/tarka/provisioning/v1/job.d.ts +168 -0
  40. package/dist/cjs/generated/tarka/provisioning/v1/job.js +1505 -0
  41. package/dist/cjs/generated/tarka/provisioning/v1/provisioning.d.ts +2644 -0
  42. package/dist/cjs/generated/tarka/provisioning/v1/provisioning.js +19501 -0
  43. package/dist/cjs/generated/tarka/provisioning/v1/registry_credentials.d.ts +89 -0
  44. package/dist/cjs/generated/tarka/provisioning/v1/registry_credentials.js +856 -0
  45. package/dist/cjs/generated/tarka/provisioning/v1/sandbox.d.ts +431 -0
  46. package/dist/cjs/generated/tarka/provisioning/v1/sandbox.js +3203 -0
  47. package/dist/cjs/generated/tarka/provisioning/v1/source_builds.d.ts +291 -0
  48. package/dist/cjs/generated/tarka/provisioning/v1/source_builds.js +3920 -0
  49. package/dist/cjs/grpc.d.ts +12 -0
  50. package/dist/cjs/grpc.js +55 -0
  51. package/dist/cjs/index.d.ts +6 -0
  52. package/dist/cjs/index.js +21 -0
  53. package/dist/cjs/package.json +1 -0
  54. package/dist/cjs/resources/inference.d.ts +293 -0
  55. package/dist/cjs/resources/inference.js +451 -0
  56. package/dist/cjs/resources/provisioning.d.ts +389 -0
  57. package/dist/cjs/resources/provisioning.js +552 -0
  58. package/dist/cjs/resources/sandboxes.d.ts +360 -0
  59. package/dist/cjs/resources/sandboxes.js +732 -0
  60. package/dist/cjs/rpc-types.d.ts +36 -0
  61. package/dist/cjs/rpc-types.js +29 -0
  62. package/dist/cjs/runtime.d.ts +99 -0
  63. package/dist/cjs/runtime.js +473 -0
  64. package/dist/cjs/transport.d.ts +60 -0
  65. package/dist/cjs/transport.js +114 -0
  66. package/dist/cjs/types.d.ts +309 -0
  67. package/dist/cjs/types.js +2 -0
  68. package/dist/esm/client.d.ts +114 -0
  69. package/dist/esm/client.js +169 -0
  70. package/dist/esm/errors.d.ts +27 -0
  71. package/dist/esm/errors.js +71 -0
  72. package/dist/esm/generated/google/api/annotations.d.ts +1 -0
  73. package/dist/esm/generated/google/api/annotations.js +7 -0
  74. package/dist/esm/generated/google/api/http.d.ts +395 -0
  75. package/dist/esm/generated/google/api/http.js +418 -0
  76. package/dist/esm/generated/google/protobuf/descriptor.d.ts +873 -0
  77. package/dist/esm/generated/google/protobuf/descriptor.js +3977 -0
  78. package/dist/esm/generated/google/protobuf/empty.d.ts +35 -0
  79. package/dist/esm/generated/google/protobuf/empty.js +55 -0
  80. package/dist/esm/generated/google/protobuf/struct.d.ts +127 -0
  81. package/dist/esm/generated/google/protobuf/struct.js +524 -0
  82. package/dist/esm/generated/google/protobuf/timestamp.d.ts +128 -0
  83. package/dist/esm/generated/google/protobuf/timestamp.js +92 -0
  84. package/dist/esm/generated/protoc-gen-openapiv2/options/annotations.d.ts +1 -0
  85. package/dist/esm/generated/protoc-gen-openapiv2/options/annotations.js +7 -0
  86. package/dist/esm/generated/protoc-gen-openapiv2/options/openapiv2.d.ts +983 -0
  87. package/dist/esm/generated/protoc-gen-openapiv2/options/openapiv2.js +4859 -0
  88. package/dist/esm/generated/tarka/access/v1/access.d.ts +322 -0
  89. package/dist/esm/generated/tarka/access/v1/access.js +2976 -0
  90. package/dist/esm/generated/tarka/channels/v1/channels.d.ts +370 -0
  91. package/dist/esm/generated/tarka/channels/v1/channels.js +3600 -0
  92. package/dist/esm/generated/tarka/gateway/v1/gateway.d.ts +804 -0
  93. package/dist/esm/generated/tarka/gateway/v1/gateway.js +8077 -0
  94. package/dist/esm/generated/tarka/iam/v1/iam.d.ts +1291 -0
  95. package/dist/esm/generated/tarka/iam/v1/iam.js +13203 -0
  96. package/dist/esm/generated/tarka/inference/v1/inference.d.ts +699 -0
  97. package/dist/esm/generated/tarka/inference/v1/inference.js +7055 -0
  98. package/dist/esm/generated/tarka/provisioning/v1/agent_secrets.d.ts +70 -0
  99. package/dist/esm/generated/tarka/provisioning/v1/agent_secrets.js +600 -0
  100. package/dist/esm/generated/tarka/provisioning/v1/billing_quote.d.ts +171 -0
  101. package/dist/esm/generated/tarka/provisioning/v1/billing_quote.js +1585 -0
  102. package/dist/esm/generated/tarka/provisioning/v1/job.d.ts +168 -0
  103. package/dist/esm/generated/tarka/provisioning/v1/job.js +1500 -0
  104. package/dist/esm/generated/tarka/provisioning/v1/provisioning.d.ts +2644 -0
  105. package/dist/esm/generated/tarka/provisioning/v1/provisioning.js +19496 -0
  106. package/dist/esm/generated/tarka/provisioning/v1/registry_credentials.d.ts +89 -0
  107. package/dist/esm/generated/tarka/provisioning/v1/registry_credentials.js +853 -0
  108. package/dist/esm/generated/tarka/provisioning/v1/sandbox.d.ts +431 -0
  109. package/dist/esm/generated/tarka/provisioning/v1/sandbox.js +3192 -0
  110. package/dist/esm/generated/tarka/provisioning/v1/source_builds.d.ts +291 -0
  111. package/dist/esm/generated/tarka/provisioning/v1/source_builds.js +3917 -0
  112. package/dist/esm/grpc.d.ts +12 -0
  113. package/dist/esm/grpc.js +11 -0
  114. package/dist/esm/index.d.ts +6 -0
  115. package/dist/esm/index.js +4 -0
  116. package/dist/esm/resources/inference.d.ts +293 -0
  117. package/dist/esm/resources/inference.js +437 -0
  118. package/dist/esm/resources/provisioning.d.ts +389 -0
  119. package/dist/esm/resources/provisioning.js +531 -0
  120. package/dist/esm/resources/sandboxes.d.ts +360 -0
  121. package/dist/esm/resources/sandboxes.js +718 -0
  122. package/dist/esm/rpc-types.d.ts +36 -0
  123. package/dist/esm/rpc-types.js +26 -0
  124. package/dist/esm/runtime.d.ts +99 -0
  125. package/dist/esm/runtime.js +466 -0
  126. package/dist/esm/transport.d.ts +60 -0
  127. package/dist/esm/transport.js +110 -0
  128. package/dist/esm/types.d.ts +309 -0
  129. package/dist/esm/types.js +1 -0
  130. package/package.json +155 -4
package/README.md CHANGED
@@ -1,3 +1,701 @@
1
- # Temporary Holding Version
1
+ # Tarka JavaScript SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The official Tarka SDK for JavaScript and TypeScript. A resource API for
4
+ inference, organizations, compute, storage, Git, and sandboxes, backed by
5
+ generated protobuf bindings from the same contract as the
6
+ [Python SDK](https://github.com/TarkaHQ/sdk-python).
7
+
8
+ ```js
9
+ const models = await client.models.list();
10
+ const text = await client.chat.text({ model, prompt: "Hello!" });
11
+ const sandbox = await client.org(orgId).sandboxes.create({ template });
12
+ const result = await sandbox.run('print("Hello from Tarka")');
13
+ console.log(result.status, result.stdout);
14
+ ```
15
+
16
+ - Node.js 22+, with ESM, CommonJS, and TypeScript declarations.
17
+ - Wrappers for all 60 RPCs in the pinned API contract.
18
+ - Arrays from lists, entities from reads and writes, async iterables from streams.
19
+ - Organization context, readable statuses, and recoverable sandbox operations.
20
+ - Two direct runtime dependencies: `@grpc/grpc-js` and `@bufbuild/protobuf`.
21
+ - Raw transport access stays under `client.grpc` and `@tarkahq/sdk/grpc`.
22
+
23
+ This version targets Node.js, including server applications. Browser and edge
24
+ runtimes without Node's HTTP/2 support are outside its scope.
25
+
26
+ Inference uses `tarka.inference.v1.InferenceService`. Regenerate native gRPC
27
+ clients when upgrading from the previous inference namespace; RPC paths and
28
+ generated import paths now use `v1`. The resource API is unchanged.
29
+
30
+ ## Installation
31
+
32
+ The intended npm package name is `@tarkahq/sdk`. This repository does not imply
33
+ that the package is already published. To use this version locally:
34
+
35
+ ```sh
36
+ git clone https://github.com/TarkaHQ/sdk-javascript.git
37
+ cd sdk-javascript
38
+ npm ci
39
+ npm pack
40
+ # From your application directory:
41
+ npm install /path/to/sdk-javascript/tarkahq-sdk-0.3.0.tgz
42
+ ```
43
+
44
+ After publication, use `npm install @tarkahq/sdk`. Consumers need neither
45
+ `protoc` nor an API checkout; the package includes generated code and types.
46
+
47
+ ## Quick start
48
+
49
+ ```js
50
+ import { TarkaClient } from "@tarkahq/sdk";
51
+
52
+ const accessToken = process.env.TARKA_API_KEY;
53
+ const model = process.env.TARKA_MODEL;
54
+ if (!accessToken || !model) {
55
+ throw new Error("Set TARKA_API_KEY and TARKA_MODEL");
56
+ }
57
+
58
+ const client = new TarkaClient({ accessToken, timeoutMs: 30_000 });
59
+ try {
60
+ console.log(await client.models.list());
61
+ console.log(
62
+ await client.chat.text({
63
+ model,
64
+ prompt: "Say hello in Nepali.",
65
+ maxTokens: 100,
66
+ }),
67
+ );
68
+ } finally {
69
+ client.close();
70
+ }
71
+ ```
72
+
73
+ CommonJS: `const { TarkaClient } = require("@tarkahq/sdk")`.
74
+ Reuse one client across requests and close it on application shutdown.
75
+ `close()` is idempotent and closes only connections owned by the SDK.
76
+ Runnable, type-checked scripts are in [examples](examples).
77
+
78
+ TypeScript declarations are included for ESM and CommonJS; no separate `@types`
79
+ package is needed. Public methods and types include JSDoc for editor hovers,
80
+ parameter descriptions, defaults, result semantics, and examples. JavaScript
81
+ editors use the same declarations for autocomplete and hover documentation.
82
+ Enable `// @ts-check` to also check types in a JavaScript file:
83
+
84
+ ```js
85
+ // @ts-check
86
+ import { TarkaClient } from "@tarkahq/sdk";
87
+
88
+ /** @type {import("@tarkahq/sdk").ChatInput} */
89
+ const input = { model: "your-model-alias", prompt: "Hello", maxTokens: 100 };
90
+
91
+ /**
92
+ * @param {TarkaClient} client
93
+ * @param {import("@tarkahq/sdk").ChatInput} input
94
+ * @returns {Promise<string>}
95
+ */
96
+ async function reply(client, input) {
97
+ return client.chat.text(input);
98
+ }
99
+ ```
100
+
101
+ Hover over input fields as well as methods for their documentation. For example,
102
+ `timeoutMs` describes the client wait budget, `timeoutSeconds` describes the
103
+ server limit, and chat `metadata` is distinguished from transport metadata.
104
+ See [the JavaScript example](examples/chat.mjs) for a complete script. Explicit
105
+ JSDoc annotations are useful for reusable helpers; direct SDK calls already
106
+ infer their inputs and results.
107
+
108
+ The default endpoint is `grpc.tarka.rest:443`. The SDK uses TLS and bearer
109
+ authentication. Pass the token itself without `Bearer`. Environment variables
110
+ are read by your application, not implicitly by the SDK.
111
+
112
+ Use an account access token for provisioning. Inference accepts suitably scoped
113
+ `tk_live_...` keys; sandboxes accept account tokens or keys with the `sandboxes`
114
+ scope. Organization membership, product access, and model policy still apply.
115
+
116
+ ## API conventions
117
+
118
+ | Operation | Convention |
119
+ | -------------------- | ------------------------------------------------------------------------------ |
120
+ | List | `list()` returns an array directly; no `.data`, `.items`, or service envelope. |
121
+ | Get | `get(id)` returns the entity. |
122
+ | Create / update | Return the entity; no request or response wrapper objects. |
123
+ | Delete | Resolves to `undefined` (`Promise<void>`). |
124
+ | Revoke / cancel | Return the updated entity. |
125
+ | Stream | An `AsyncIterable`; consume with `for await`. |
126
+ | Request settings | `timeoutMs`, `signal`, and plain-object `metadata`. |
127
+ | Organization context | Bind once with `client.org(orgId)`; nested calls omit `orgId`. |
128
+ | Sandbox entities | Lists, gets, creates, and waits return the same handle types. |
129
+
130
+ Calls with no input need no `{}`. Request settings go directly to input-free
131
+ methods, or after the input for other methods:
132
+
133
+ ```js
134
+ await client.models.list({ timeoutMs: 10_000 });
135
+ await client.models.get(model, { timeoutMs: 10_000 });
136
+ await client.chat.text({ model, prompt: "Hello" }, { timeoutMs: 10_000 });
137
+ ```
138
+
139
+ Methods with optional filters or input can be called with no arguments.
140
+ To supply only request settings to those methods, pass `undefined` for the input:
141
+ `client.catalog.list(undefined, { timeoutMs: 10_000 })`.
142
+
143
+ Creation of API keys and storage credentials returns the normal entity fields
144
+ plus its one-time secret. For example, an API key has `id`, `label`, and `secret`,
145
+ rather than `{ apiKey, plaintextKey }`. Store the secret securely at creation;
146
+ list calls do not return it.
147
+
148
+ ## Chat, models, and Responses
149
+
150
+ ```js
151
+ const models = await client.models.list();
152
+ const modelInfo = await client.models.get(model);
153
+ const catalog = await client.catalog.list({ modality: "text" });
154
+
155
+ const completion = await client.chat.create({
156
+ model,
157
+ messages: [{ role: "user", content: "Explain HTTP/2 briefly." }],
158
+ temperature: 0.2,
159
+ });
160
+
161
+ for await (const text of client.chat.streamText({
162
+ model,
163
+ system: "Be concise.",
164
+ prompt: "Explain HTTP/2 briefly.",
165
+ })) {
166
+ process.stdout.write(text);
167
+ }
168
+ ```
169
+
170
+ Supply either `prompt` with an optional `system`, or a non-empty `messages`
171
+ array. `create()` returns the full completion, including usage and tool calls;
172
+ `stream()` returns full chunks. `text()` selects choice 0 and rejects if it
173
+ contains no text. `streamText()` yields choice 0's text deltas, skipping usage
174
+ and tool-call chunks. Streaming mode is selected by the method.
175
+
176
+ `models` describes the inference-visible models. `catalog` exposes the control
177
+ plane model catalog, including provisioning information and modality filtering.
178
+
179
+ The JSON APIs accept ordinary OpenAI-compatible objects, retaining their
180
+ original field names, and return the response body directly. They support
181
+ forward-compatible fields without protobuf envelope objects:
182
+
183
+ ```js
184
+ const completion = await client.chat.json.create({
185
+ model,
186
+ messages: [{ role: "user", content: "Hello" }],
187
+ max_tokens: 100,
188
+ });
189
+ console.log(completion.choices);
190
+
191
+ const response = await client.responses.create({ model, input: "Hello" });
192
+ console.log(response.id);
193
+
194
+ for await (const event of client.responses.stream({ model, input: "Hello" })) {
195
+ console.log(event.type, event.data);
196
+ }
197
+ ```
198
+
199
+ JSON streams yield `{ type, data }`. Both JSON chat and Responses select
200
+ `stream: false` for `create()` and `stream: true` for `stream()`.
201
+ Responses also provides `get(id)`, `cancel(id)`, and `delete(id)`.
202
+ JSON outputs use `JsonObject` / `JsonValue`; narrow dynamic values before using
203
+ them in TypeScript. The typed chat API provides completion-specific types.
204
+
205
+ ## OCR and audio
206
+
207
+ ```js
208
+ import { readFile, writeFile } from "node:fs/promises";
209
+
210
+ const text = await client.ocr.text({ model: ocrModel, image: imageUrl });
211
+ const transcript = await client.audio.transcriptions.text({
212
+ model: transcriptionModel,
213
+ file: await readFile("recording.wav"),
214
+ filename: "recording.wav",
215
+ });
216
+ const speech = await client.audio.speech.create({
217
+ model: speechModel,
218
+ input: "Hello from Tarka.",
219
+ });
220
+ await writeFile("speech.audio", speech.data);
221
+ console.log(speech.contentType);
222
+ ```
223
+
224
+ OCR takes either `image` or a non-empty `images` array. It offers `create()` and
225
+ `stream()` for complete results, plus `text()` and `streamText()` for strings.
226
+ Both `audio.transcriptions` and `audio.translations` offer `create()` and `text()`.
227
+
228
+ `audio.speech.create()` collects the response into `{ data: Buffer, contentType }`.
229
+ Use `audio.speech.stream()` for large outputs; it yields chunks of the same shape.
230
+ `audio.voices` provides `create({ model, name, sample, consentSignedBy,
231
+ consentDate, ... })`, `get(id)`, and `delete(id)` for voice cloning.
232
+
233
+ ## Organizations, keys, compute, storage, and Git
234
+
235
+ ```js
236
+ const user = await client.user.get();
237
+ const org = client.org(user.defaultOrgId); // No network request.
238
+
239
+ const keys = await org.apiKeys.list();
240
+ const key = await org.apiKeys.create({
241
+ label: "my-app",
242
+ scopes: ["inference"],
243
+ });
244
+ const usage = await org.usage.summary();
245
+
246
+ const bucket = await org.storage.buckets.create({ name: "my-data" });
247
+ const credential = await org.storage
248
+ .credentials(bucket.id)
249
+ .create({ label: "worker" });
250
+ // credential includes accessKeyId, secretAccessKey, endpoint, and region.
251
+
252
+ const repository = await org.repositories.create({
253
+ name: "my-project",
254
+ initialize: true,
255
+ });
256
+ ```
257
+
258
+ The complete resource surface is below. IDs are strings, inputs are objects,
259
+ and each method accepts request settings in the last argument.
260
+
261
+ | Resource | Methods |
262
+ | ---------------------------------------------- | ------------------------------------------------------------------------- |
263
+ | `client.models` | `list()`, `get(id)` |
264
+ | `client.catalog` | `list({ modality? }?)` |
265
+ | `client.chat` | `create(input)`, `stream(input)`, `text(input)`, `streamText(input)` |
266
+ | `client.chat.json` | `create(body)`, `stream(body)` |
267
+ | `client.responses` | `create(body)`, `stream(body)`, `get(id)`, `cancel(id)`, `delete(id)` |
268
+ | `client.ocr` | `create(input)`, `stream(input)`, `text(input)`, `streamText(input)` |
269
+ | `client.audio.transcriptions`, `.translations` | `create(input)`, `text(input)` |
270
+ | `client.audio.speech` | `create(input)`, `stream(input)` |
271
+ | `client.audio.voices` | `create(input)`, `get(id)`, `delete(id)` |
272
+ | `client.user` | `get()`, `bootstrap({ orgName? }?)` |
273
+ | `client.organizations` | `list()`, `get(id)`, `create({ name, slug? })` |
274
+ | `client.git` | `ensureAccess()` |
275
+ | `org` | `get()`, `update({ name })` |
276
+ | `org.members` | `list()` |
277
+ | `org.accessRequests` | `list({ product?, limit? }?)`, `create({ product, ... })` |
278
+ | `org.apiKeys` | `list()`, `create({ label?, scopes?, ... }?)`, `revoke(id, { reason? }?)` |
279
+ | `org.quotas` | `get()`, `update({ allowAllModels, ... })` |
280
+ | `org.usage` | `list({ limit? }?)`, `summary()` |
281
+ | `org.compute.resources` | `list({ resourceType? }?)`, `get(id)` |
282
+ | `org.compute.inferenceServices` | `create({ name, modelAlias, ... })` |
283
+ | `org.compute.agentHosts` | `create({ name, image, ... })` |
284
+ | `org.compute.jobs` | `create({ name, image, command?, ... })` |
285
+ | `org.storage.buckets` | `list()`, `create({ name })`, `delete(id)` |
286
+ | `org.storage.credentials(bucketId)` | `list()`, `create({ label? }?)`, `revoke(id)` |
287
+ | `org.repositories` | `list()`, `create({ name, ... })`, `delete(id)` |
288
+ | `org.sandboxes` | `list()`, `get(id)`, `create({ template, ... })`, `delete(id)` |
289
+ | `org.sandboxes.templates` | `list()`, `get(id)`, `create(input)`, `delete(id)` |
290
+ | `sandbox.executions` | `get(id)`, `create({ code, ... })` |
291
+
292
+ `org.quotas.update()` replaces the policy: omitted optional limits become
293
+ unlimited. Pass the desired full policy, including `allowedModelAliases` when
294
+ restricting models. List filters use the server's default limits; lists do not
295
+ silently fetch additional pages. Exported input and result types describe every
296
+ field and appear in editor completion.
297
+
298
+ ## Sandboxes and templates
299
+
300
+ ```js
301
+ const sandboxes = client.org(orgId).sandboxes;
302
+ const templates = await sandboxes.templates.list();
303
+ const sandbox = await sandboxes.create({ template, timeoutSeconds: 600 });
304
+
305
+ const execution = await sandbox.run(
306
+ 'print("hello from Tarka")',
307
+ { timeoutSeconds: 30 },
308
+ { timeoutMs: 60_000 },
309
+ );
310
+ console.log(execution.status, execution.stdout, execution.stderr);
311
+ await sandbox.delete();
312
+ ```
313
+
314
+ `run()` waits for readiness, submits code, then waits for completion within
315
+ **one total timeout**. To submit without waiting, call `sandbox.execute(code)`
316
+ or `sandbox.executions.create({ code })`; both return an `Execution` handle.
317
+ Fetch one with `sandbox.executions.get(id)` and finish waiting with `execution.wait()`.
318
+
319
+ `Sandbox`, `SandboxTemplate`, and `Execution` handles expose `id`, `status`,
320
+ `info` (the latest snapshot), `refresh()`, and `toJSON()`. Refreshing and waiting
321
+ update the existing handle and return it. `list()`, `get()`, and `create()`
322
+ return the same handle type for a resource. Handles keep their organization
323
+ and resource IDs; you never pass that context again.
324
+
325
+ Sandbox and template handles add `waitUntilReady()` and `delete()`.
326
+ Execution handles add `wait()`, `stdout`, `stderr`, `exitCode`, and `outputTruncated`.
327
+ Statuses are readable strings such as `"ready"`, `"running"`, `"succeeded"`,
328
+ `"failed"`, and `"timed_out"`; runtimes are `"python"` or `"node"`.
329
+
330
+ A finished execution is returned even when the code failed or reached its server
331
+ time limit: inspect `status`, `exitCode`, `stderr`, and `outputTruncated`.
332
+ Waiting on a failed sandbox/template or an unknown execution status throws
333
+ `ResourceStateError`, with `resource` and the server snapshot in `info`.
334
+
335
+ Create a template with exactly one dependency manifest. There are no protobuf
336
+ `oneof` tags in the public input:
337
+
338
+ ```js
339
+ const template = await sandboxes.templates.create({
340
+ alias: "python-with-numpy",
341
+ baseTemplateAlias: "your-python-base-alias",
342
+ requirementsTxt: "numpy==2.0.0",
343
+ });
344
+ await template.waitUntilReady({ timeoutMs: 300_000 });
345
+ ```
346
+
347
+ The alternatives are `packageJson` and `pyprojectToml`, each containing the file
348
+ contents as a string. Discover available base templates with `templates.list()`.
349
+
350
+ ## Timeouts and recovery
351
+
352
+ Every request accepts `{ timeoutMs, signal, metadata }`. `timeoutMs` must be
353
+ finite and non-negative; zero expires immediately. It overrides the client's
354
+ default. Without a per-call or client timeout, ordinary RPCs have no SDK deadline.
355
+ For a stream, the timeout covers its entire lifetime, not just gaps between chunks.
356
+
357
+ Sandbox `run()`, `waitUntilReady()`, and execution `wait()` default to a **60-second
358
+ total budget**, overriding the client default, with a **1-second poll interval**.
359
+ Set `pollIntervalMs` to change polling. The budget includes requests and delays.
360
+ `timeoutSeconds` in resource input is a server-side lifetime/execution limit;
361
+ `timeoutMs` in request settings limits how long the client waits.
362
+
363
+ Timeout or cancellation does not establish that a mutation failed. Code may
364
+ still be running remotely. The sandbox helpers attach recovery context and a
365
+ typed `resume()` to transient failures:
366
+
367
+ ```js
368
+ import { ExecutionOperationError } from "@tarkahq/sdk";
369
+
370
+ let execution;
371
+ try {
372
+ execution = await sandbox.run('print("hello")', undefined, {
373
+ timeoutMs: 10_000,
374
+ });
375
+ } catch (error) {
376
+ if (!(error instanceof ExecutionOperationError)) throw error;
377
+ console.log(error.code, error.recovery);
378
+ execution = await error.resume({ timeoutMs: 60_000 });
379
+ }
380
+ console.log(execution.status, execution.stdout);
381
+ await sandbox.delete();
382
+ ```
383
+
384
+ - If an execution ID is known, `resume()` polls that execution. It does not
385
+ submit the code again.
386
+ - If creation/submission was not acknowledged, `resume()` repeats the original
387
+ request with the same idempotency key, allowing the server to deduplicate it.
388
+ - If `run()` timed out before readiness, recovery waits for readiness and then
389
+ submits the preserved code and key.
390
+ - A resumed `run()` still waits for execution completion. A resumed `execute()`
391
+ only completes submission, just like the original method.
392
+ - Recovery starts a fresh timeout and cancellation scope. The previous aborted
393
+ signal and deadline are discarded. Metadata and polling interval are retained
394
+ unless overridden. A further transient failure carries another resumable error.
395
+
396
+ `SandboxOperationError`, `TemplateOperationError`, and `ExecutionOperationError`
397
+ all extend `OperationError<T>` and `TarkaError`. Each provides `code`, `recovery`,
398
+ and `resume(options?)`, returning the original operation's entity type.
399
+ Recovery details include the operation, organization, available resource IDs,
400
+ and idempotency key. Code, stdin, and manifests are kept privately for replay;
401
+ they are not included in `recovery` or serialized error fields.
402
+
403
+ Keys are generated automatically for sandbox/template creation and execution.
404
+ You can supply your own `idempotencyKey` for durable application workflows.
405
+ The API contract guarantees deduplication for **24 hours**, with unchanged
406
+ request content. Resume within that window; replay after it can create a new
407
+ resource or execution. The SDK does not automatically retry mutations or resume
408
+ partially consumed inference streams.
409
+
410
+ An error's `resume()` lives in memory. Across process restarts, save your chosen
411
+ key and original input for unacknowledged mutations, or save IDs and reattach:
412
+
413
+ ```js
414
+ const sandbox = await client.org(orgId).sandboxes.get(sandboxId);
415
+ const execution = await sandbox.executions.get(executionId);
416
+ await execution.wait({ timeoutMs: 60_000 });
417
+ ```
418
+
419
+ Aborting a wait stops polling; it does not delete the sandbox or necessarily
420
+ stop submitted code. Keep the sandbox available while recovering, then delete
421
+ it when finished. Other provisioning mutations lack this idempotency contract:
422
+ after a timeout, inspect their state before deciding whether to retry.
423
+
424
+ ## Streaming, cancellation, and errors
425
+
426
+ ```js
427
+ import { TarkaError } from "@tarkahq/sdk";
428
+
429
+ const controller = new AbortController();
430
+ try {
431
+ for await (const text of client.chat.streamText(
432
+ { model, prompt: "Write a short sentence." },
433
+ {
434
+ timeoutMs: 60_000,
435
+ signal: controller.signal,
436
+ metadata: { "x-request-id": "my-request" },
437
+ },
438
+ )) {
439
+ process.stdout.write(text);
440
+ }
441
+ } catch (error) {
442
+ if (error instanceof TarkaError && error.code === "deadline_exceeded") {
443
+ console.error("The request timed out");
444
+ } else {
445
+ throw error;
446
+ }
447
+ }
448
+ ```
449
+
450
+ Streams start on iteration and preserve Node stream backpressure. Calling
451
+ `controller.abort()` cancels the active RPC; breaking out of `for await` also
452
+ cancels it. Metadata accepts strings, Buffers, and arrays of those values;
453
+ binary keys must end in `-bin`.
454
+
455
+ Resource methods normalize transport failures to `TarkaError`, with a readable
456
+ `message`, string `code`, plain trailing `metadata`, and the original error as
457
+ `cause`. Typical codes include `deadline_exceeded`, `cancelled`, `unavailable`,
458
+ `unauthenticated`, `permission_denied`, `not_found`, and `invalid_argument`.
459
+ Local argument validation throws `TypeError`. Native gRPC error objects and
460
+ numeric status codes are available only through `client.grpc` calls.
461
+
462
+ ## Values and serialization
463
+
464
+ Public fields use camelCase, except opaque OpenAI JSON bodies. Bytes are Node
465
+ `Buffer`s, timestamps are `Date`s, and 64-bit integers are `bigint`s to preserve
466
+ precision. Use bigint literals for input limits, such as `1_000_000n`. Optional
467
+ values remain `undefined`; explicit zero and false are preserved.
468
+
469
+ Sandbox `JSON.stringify(handle)` serializes its latest snapshot. For other
470
+ entities containing bigints, choose a JSON representation explicitly:
471
+
472
+ ```js
473
+ const json = JSON.stringify(usage, (_key, value) =>
474
+ typeof value === "bigint" ? value.toString() : value,
475
+ );
476
+ ```
477
+
478
+ ## Advanced gRPC access
479
+
480
+ Normal application code needs only `@tarkahq/sdk`. Every underlying RPC remains
481
+ available through `client.grpc`, with generated envelopes, enums, and native
482
+ call options and errors:
483
+
484
+ ```js
485
+ const response = await client.grpc.inference.listModels();
486
+ await client.grpc.provisioning.getCurrentUser({ timeoutMs: 10_000 });
487
+ await client.grpc.sandboxes.listSandboxes({ orgId });
488
+ ```
489
+
490
+ Transport utilities and generated codecs are isolated in these exports:
491
+
492
+ ```js
493
+ import {
494
+ Channel,
495
+ credentials,
496
+ status,
497
+ createChannel,
498
+ createServiceClient,
499
+ } from "@tarkahq/sdk/grpc";
500
+ import {
501
+ ChatCompletionRequest,
502
+ InferenceServiceDefinition,
503
+ } from "@tarkahq/sdk/grpc/inference/v1";
504
+ import { UsageSummary } from "@tarkahq/sdk/grpc/provisioning/v1";
505
+ import { ExecuteRequest } from "@tarkahq/sdk/grpc/sandboxes/v1";
506
+ ```
507
+
508
+ `@tarkahq/sdk/grpc` also exports `inference`, `provisioning`, and `sandboxes`
509
+ codec namespaces. Codecs offer `create`, `fromPartial`, `encode`, `decode`,
510
+ `fromJSON`, and `toJSON`. Raw enums are numeric and raw oneofs use
511
+ `{ $case, value }`. Native call options support absolute `deadline`, gRPC
512
+ `Metadata`, credentials, and interceptors in addition to SDK request settings.
513
+ The earlier deadline wins when both `deadline` and `timeoutMs` are given.
514
+
515
+ Use `channelCredentials: credentials.createSsl(ca)` for custom TLS roots,
516
+ `target` for a different endpoint, and `channelOptions` for gRPC configuration.
517
+ The authenticated `createChannel(accessToken, options)` helper requires TLS.
518
+ Alternatively, supply a channel whose authentication and lifecycle you own:
519
+
520
+ ```js
521
+ const channel = new Channel(
522
+ "localhost:50051",
523
+ credentials.createInsecure(),
524
+ {},
525
+ );
526
+ const local = new TarkaClient({ channel, timeoutMs: 10_000 });
527
+ try {
528
+ await local.models.list();
529
+ } finally {
530
+ channel.close();
531
+ }
532
+ ```
533
+
534
+ An injected channel cannot be combined with `accessToken`, `target`, or channel
535
+ configuration. `client.close()` leaves caller-owned channels open.
536
+
537
+ ## Migrating from 0.1
538
+
539
+ Version 0.2 deliberately makes resource wrappers the default API.
540
+
541
+ | Before | Now |
542
+ | ------------------------------------------------------- | ------------------------------------------------------------------------------- |
543
+ | `client.inference.listModels()` | `client.models.list()`; returns the array |
544
+ | `client.provisioning.getCurrentUser()` | `client.user.get()` |
545
+ | `client.provisioning.createApiKey({ orgId, ...input })` | `client.org(orgId).apiKeys.create(input)`; secret is `.secret` |
546
+ | `client.sandboxes.forOrg(orgId)` | `client.org(orgId).sandboxes` |
547
+ | `sandbox.getExecution(id)` | `sandbox.executions.get(id)`; returns an `Execution` handle |
548
+ | `sandbox.run(code, { timeoutMs, timeoutSeconds })` | `sandbox.run(code, { timeoutSeconds }, { timeoutMs })` |
549
+ | Numeric sandbox/execution statuses | String statuses, such as `"ready"` and `"succeeded"` |
550
+ | Sandbox lists returning plain snapshots | Lists of `Sandbox` handles; use `.info` for snapshots |
551
+ | `SandboxStateError` / `ExecutionStateError` | `ResourceStateError`, with `.resource` and `.info` |
552
+ | Native SDK errors with numeric codes | `TarkaError` with string codes; resumable operation errors for sandbox recovery |
553
+ | Direct services / `client.channel` | `client.grpc.inference`, `.provisioning`, `.sandboxes`, `.channel` |
554
+ | Transport imports from package root | Imports from `@tarkahq/sdk/grpc` |
555
+ | `@tarkahq/sdk/inference/v2` (and other codec paths) | `@tarkahq/sdk/grpc/inference/v1` (and corresponding service paths) |
556
+
557
+ Raw RPC signatures remain available under `grpc`, including optional empty
558
+ requests and the older `rpc({}, options)` form.
559
+
560
+ ## Development and generation
561
+
562
+ Building and testing need only this repository. Regeneration also needs the
563
+ authoritative API checkout, normally at `../api`, at the revision recorded in
564
+ `api-source.json`:
565
+
566
+ ```sh
567
+ npm ci
568
+ npm run generate # Uses ../api
569
+ npm run generate -- --api-dir /path/to/api
570
+ npm run check:generated # Compares without modifying files
571
+ npm run verify # Format, types, tests, build, package test
572
+ ```
573
+
574
+ `scripts/generate.mjs` discovers every `.proto` under `api/proto/tarka` and runs
575
+ the pinned `protoc` from `grpc-tools` with the pinned `ts-proto` plugin. Annotation
576
+ imports live in `proto/third_party`; well-known types come from `grpc-tools`.
577
+ There is no global compiler requirement or runtime code generation.
578
+
579
+ Generated TypeScript is checked in under `src/generated`; do not edit it by
580
+ hand. `api-source.json` records the API commit, source/import hashes, and
581
+ generator settings. Generation does not fetch or change the API checkout.
582
+ When the contract adds an RPC, add its resource wrapper: the coverage test
583
+ asserts that every generated RPC is exercised through the public resource API.
584
+
585
+ CI checks generation against the recorded revision and verifies Node 22 and 24.
586
+ Tests use local gRPC servers, including TLS/authentication, streaming,
587
+ cancellation, all 60 wrapped RPCs, and recovery after lost acknowledgements.
588
+ They do not require Tarka credentials or call production. The package test
589
+ installs a tarball into an isolated consumer, checks TypeScript types, and runs
590
+ ESM and CommonJS consumers. That test needs npm registry access for dependencies.
591
+
592
+ ## Preparing an npm release
593
+
594
+ 1. Confirm the package name and access to the `@tarkahq` scope.
595
+ 2. Select the API revision, regenerate, and review generated and wrapper changes.
596
+ 3. Set the version in `package.json` and `package-lock.json`.
597
+ 4. Run `npm run check:generated`, `npm run verify`, and `npm pack --dry-run`.
598
+ 5. Run the `SDK release` workflow on `main` with `publish=true` after reviewing its package artifact.
599
+
600
+ There is no automatic publication workflow.
601
+
602
+ ## License
603
+
604
+ Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
605
+
606
+ ## Gateway connection settings
607
+
608
+ Gateway create, update, get and list responses expose `endpoints` when routing
609
+ is configured. `apiBaseUrl` accepts published operation paths;
610
+ `apiAudience` is the exact OAuth resource without `/api`. `mcpUrl` and
611
+ `mcpAudience` are the same MCP endpoint. `issuer` identifies the environment's
612
+ OAuth issuer. Use these server-provided settings instead of constructing URLs.
613
+ They are available for setup before publication and do not grant access.
614
+
615
+ `client.gateway.listPrivateServices()` lists controller-observed services for an
616
+ organization and realm. Set a target's `serviceId` and `servicePath` instead of
617
+ `url` for a private destination. Only a `ready` service can be published; a
618
+ redeployment requires a new gateway publication. Its exact credential scope is
619
+ `tarka-service://<service-id>` in `allowedOrigins`.
620
+
621
+ ## Trusted agent harness
622
+
623
+ Use `RuntimeClient` in trusted server-side harness code. The token provider must
624
+ supply a current OAuth token with `runtime.execute` for the realm's exact runtime
625
+ resource. The server derives the customer, agent and consent; platform API keys
626
+ cannot establish that identity.
627
+
628
+ ```ts
629
+ import { RuntimeClient } from "@tarkahq/sdk";
630
+
631
+ const runtime = new RuntimeClient({
632
+ origin: gatewayOrigin,
633
+ realmId,
634
+ tokenProvider: currentRuntimeOAuthToken,
635
+ });
636
+ await runtime.withRun(
637
+ {
638
+ gateway_id: gatewayId,
639
+ transport: "api",
640
+ permissions: [{ resource: capabilityId, action: "invoke" }],
641
+ },
642
+ async (run) => {
643
+ const response = await run.api("/records");
644
+ const toolResult = await response.json();
645
+ // Pass only the approved result to the model.
646
+ },
647
+ );
648
+ ```
649
+
650
+ For MCP, start with `transport: "mcp"` and pass `run.mcpFetch` as the MCP
651
+ Streamable HTTP transport's fetch implementation, using `run.audience` as its
652
+ endpoint. It rejects every other URL and supplies only the narrow run bearer.
653
+
654
+ Keep the client, run handle and parent token provider outside model-visible
655
+ state, prompts, tool arguments, workspace files and logs. Register specific tool
656
+ closures. Metadata, JSON serialization and normal inspection omit credentials.
657
+ The SDK does not isolate hostile code in its own process.
658
+
659
+ The harness serializes renewal and renews near expiry on demand. An uncertain
660
+ renewal disables later tool calls; issuance and tool actions are never retried
661
+ automatically. `withRun` completes on success and attempts cancellation if the
662
+ callback fails, preserving the callback's error. `finish()` explicitly stops a
663
+ run. Read streamed responses promptly: rotating a credential while an earlier
664
+ stream remains open may cause that stream's authorization check to terminate it.
665
+
666
+ ### Independent messaging consumers
667
+
668
+ An assigned agent worker uses its autonomous Ory token for the runtime audience
669
+ after the customer links a provider account and approves the channel's tools.
670
+ Both customer and agent need channel `use` permission. Identity and tool scope
671
+ come from the gateway.
672
+
673
+ ```ts
674
+ for await (const message of runtime.messages(channelId, { signal })) {
675
+ // Give only prompt to the model as user input. Build tool closures around
676
+ // message.api(...) or message.mcpFetch inside trusted harness code.
677
+ const result = await executeAgentTurn(message.prompt, message);
678
+ await message.reply(result);
679
+ }
680
+ ```
681
+
682
+ `claimMessage(channelId)` returns one message or `null`. The iterator polls
683
+ successfully empty queues every five seconds and cancels an unfinished run on
684
+ advance or exit. A private `reply()` finishes the run, accepts at most 2,000
685
+ Unicode characters and has no recipient argument. Failed or uncertain claims
686
+ and replies propagate without retry. Keep the message handle outside model
687
+ state; inspection and JSON serialization omit its prompt and credentials.
688
+
689
+ ## Publishing
690
+
691
+ The `SDK release` workflow runs verification, creates the npm tarball and a
692
+ SHA-256 checksum, and uploads them as a reviewable artifact. A dry run is the
693
+ default. Publishing runs only on `main` with `publish=true`, uses the exact tested
694
+ tarball, and attaches it to a GitHub release.
695
+
696
+ Configure npm's trusted publisher for `@tarkahq/sdk`, owner `TarkaHQ`, repository
697
+ `sdk-javascript`, workflow `release.yml`, environment `npm`, and the publish
698
+ permission. The package owner must establish the package and publisher identity
699
+ before the first automated release. The workflow uses a GitHub-hosted runner and
700
+ OIDC, with no stored npm token. npm does not generate provenance from a private
701
+ source repository. See [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).