@teepin/sdk 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -1,501 +1,281 @@
1
- # TEEPIN TypeScript SDK
1
+ # Teepin SDK for TypeScript and JavaScript
2
2
 
3
- Official TypeScript/JavaScript client library for the TEEPIN API.
3
+ Teepin Web Services runs AI models in confidential enclaves (Teepin Inference) and stores your files (Teepin
4
+ Storage). This is the official client for Node 20+ and browsers, with no dependencies.
4
5
 
5
- ## Installation
6
+ Links: [Docs](https://www.teepin.com/docs) · [console.teepin.com](https://console.teepin.com) · [teepin.com](https://teepin.com)
7
+
8
+ ## Install
6
9
 
7
10
  ```bash
8
11
  npm install @teepin/sdk
9
12
  ```
10
13
 
11
- Or with Yarn:
14
+ ## Authentication
12
15
 
13
- ```bash
14
- yarn add @teepin/sdk
15
- ```
16
+ Create an API key in [console.teepin.com](https://console.teepin.com): open a project, then Settings, then API keys.
17
+ A key belongs to one project and has the permissions you give it (for example `inference:invoke`,
18
+ `storage:read`, `storage:write`).
16
19
 
17
- ## Quick Start
20
+ The client reads the key from `TEEPIN_API_KEY`, and the address from `TEEPIN_BASE_URL`, when you don't pass them.
21
+ A value you pass always wins. The address defaults to `https://api.teepin.com`.
18
22
 
19
- ### TypeScript
23
+ ```bash
24
+ export TEEPIN_API_KEY=tpk_...
25
+ ```
20
26
 
21
27
  ```typescript
22
28
  import { TeepinClient } from '@teepin/sdk';
23
29
 
24
- const client = new TeepinClient({
25
- baseUrl: 'http://localhost:8080',
26
- apiKey: 'your-api-key', // Optional
27
- });
28
-
29
- // Deploy an instance
30
- const instance = await client.instances.create({
31
- name: 'pytorch-training',
32
- image: 'pytorch/pytorch:latest',
33
- gpuVram: '25GB',
34
- cpuUnits: 4,
35
- memory: '16GB',
36
- env: {
37
- EPOCHS: '100',
38
- BATCH_SIZE: '32',
39
- },
40
- });
41
-
42
- console.log(`Instance created: ${instance.id}`);
43
- console.log(`Status: ${instance.status}`);
44
- ```
45
-
46
- ### JavaScript (CommonJS)
47
-
48
- ```javascript
49
- const { TeepinClient } = require('@teepin/sdk');
50
-
51
- const client = new TeepinClient({
52
- baseUrl: 'http://localhost:8080',
53
- });
54
-
55
- async function deploy() {
56
- const instance = await client.instances.create({
57
- name: 'my-instance',
58
- image: 'nginx:latest',
59
- gpuVram: '25GB',
60
- });
61
-
62
- console.log('Instance created:', instance.id);
63
- }
64
-
65
- deploy();
30
+ const teepin = new TeepinClient(); // reads TEEPIN_API_KEY and TEEPIN_BASE_URL
31
+ // or: new TeepinClient({ apiKey: 'tpk_...' })
66
32
  ```
67
33
 
68
- ## Features
34
+ Keep your API key on your server. To let a browser download or upload one file, sign a link on your server (see
35
+ [Signed links](#signed-links)).
69
36
 
70
- - ✅ **Type-safe** - Full TypeScript support with complete type definitions
71
- - ✅ **Promise-based** - Modern async/await API
72
- - ✅ **Error handling** - Rich error types with type guards
73
- - ✅ **Well-documented** - Comprehensive JSDoc comments
74
- - ✅ **Tree-shakeable** - ES modules support
75
- - ✅ **No dependencies** - uses fetch and WebCrypto; Node 20+ and browsers
37
+ ## Teepin Inference
76
38
 
77
- ## Usage Examples
39
+ An OpenAI-compatible chat API. The key needs `inference:invoke`, and may be limited to some models.
78
40
 
79
- ### List Instances
41
+ ### Models
80
42
 
81
43
  ```typescript
82
- const instances = await client.instances.list();
83
-
84
- for (const instance of instances) {
85
- console.log(`${instance.id}: ${instance.name} (${instance.status})`);
86
- }
87
- ```
88
-
89
- ### Get Instance Details
90
-
91
- ```typescript
92
- import { isNotFoundError } from '@teepin/sdk';
93
-
94
- try {
95
- const instance = await client.instances.get('inst-a82e7f3');
96
- console.log(`Instance: ${instance.name}`);
97
- console.log(`Status: ${instance.status}`);
98
- console.log(`GPU VRAM: ${instance.gpuVram}`);
99
- } catch (error) {
100
- if (isNotFoundError(error)) {
101
- console.log('Instance not found');
102
- } else {
103
- throw error;
104
- }
44
+ const models = await teepin.inference.listModels();
45
+ for (const m of models) {
46
+ console.log(m.id, m.confidential, m.pricing.input_per_million_tokens, m.pricing.output_per_million_tokens);
105
47
  }
106
48
  ```
107
49
 
108
- ### Delete Instance
109
-
110
- ```typescript
111
- await client.instances.delete('inst-a82e7f3');
112
- console.log('Instance deleted');
113
- ```
114
-
115
- ### Deploy with Port Mappings
50
+ ### One answer
116
51
 
117
52
  ```typescript
118
- const instance = await client.instances.create({
119
- name: 'web-app',
120
- image: 'nginx:latest',
121
- gpuVram: '10GB',
122
- cpuUnits: 2,
123
- memory: '8GB',
124
- ports: [
125
- { container: 80, public: 8080, protocol: 'tcp' },
126
- { container: 443, public: 8443, protocol: 'tcp' },
127
- ],
53
+ const res = await teepin.inference.chat.completions.create({
54
+ model: 'teepin/glm-5.3',
55
+ messages: [{ role: 'user', content: 'What is TLS? One sentence.' }],
56
+ max_completion_tokens: 300,
57
+ chat_template_kwargs: { enable_thinking: false },
128
58
  });
59
+ console.log(res.choices[0].message.content);
60
+ console.log(res.usage?.total_tokens, res.meta.requestId);
129
61
  ```
130
62
 
131
- ### Concurrent Deployments
132
-
133
- ```typescript
134
- // Deploy 10 instances concurrently
135
- const deployments = Array.from({ length: 10 }, (_, i) =>
136
- client.instances.create({
137
- name: `worker-${i}`,
138
- image: 'pytorch/pytorch:latest',
139
- gpuVram: '10GB',
140
- })
141
- );
142
-
143
- const instances = await Promise.all(deployments);
144
- console.log(`Deployed ${instances.length} instances`);
145
- ```
146
-
147
- ### With Labels
63
+ ### Streaming
148
64
 
149
65
  ```typescript
150
- const instance = await client.instances.create({
151
- name: 'training-job',
152
- image: 'pytorch/pytorch:latest',
153
- gpuVram: '40GB',
154
- labels: {
155
- team: 'ml-research',
156
- project: 'image-classification',
157
- environment: 'production',
158
- },
66
+ const stream = await teepin.inference.chat.completions.create({
67
+ model: 'teepin/glm-5.3',
68
+ messages: [{ role: 'user', content: 'Write a haiku about the sea.' }],
69
+ stream: true,
70
+ stream_options: { include_usage: true },
159
71
  });
72
+ for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta.content ?? '');
73
+ console.log('\n', stream.usage?.total_tokens);
160
74
  ```
161
75
 
162
- ### Teepin Storage
76
+ Leaving the loop early closes the connection. A stream throws `StreamError` when the server reports a failure part
77
+ way through, and `StreamEndedError` when it closes before the end.
163
78
 
164
- Works in Node 20+ and browsers (it uses `fetch` and WebCrypto, with no
165
- dependencies). Files on disk are in `@teepin/sdk/node`.
79
+ ### Reasoning models
166
80
 
167
- ```typescript
168
- import { TeepinClient } from '@teepin/sdk';
169
- import { uploadFile, downloadFile } from '@teepin/sdk/node';
170
-
171
- const teepin = new TeepinClient(); // reads TEEPIN_API_KEY (and TEEPIN_BASE_URL)
172
- await teepin.storage.createBucket('my-bucket');
81
+ Reasoning models (GLM-5.3, for example) think before they answer, and the thinking counts toward
82
+ `max_completion_tokens`. With a small limit the answer can come back empty (`content: null`,
83
+ `finish_reason: 'length'`). For a short answer, turn thinking off with
84
+ `chat_template_kwargs: { enable_thinking: false }`, or give it room. The thinking itself, when the model shows it,
85
+ is in `message.reasoning_content`.
173
86
 
174
- // Any size: one request up to 16 MiB, parallel parts above that. Each part is
175
- // retried on its own and the stored ETag is checked against the bytes sent.
176
- // resume continues an upload that was cut off.
177
- await uploadFile(teepin.storage, 'my-bucket', 'videos/a.mp4', './a.mp4', {
178
- resume: true,
179
- onProgress: (done, total) => console.log(`${done} of ${total}`),
180
- });
87
+ ### Confidential models and attestation
181
88
 
182
- // Strings, bytes, Blobs and Files (browsers), and streams of unknown size.
183
- await teepin.storage.upload('my-bucket', 'notes.txt', 'hello');
89
+ Confidential models run in hardware enclaves that prove what code they run. Each answer says how far the enclave
90
+ was verified for that call:
184
91
 
185
- // Parallel ranges into a temporary file, renamed into place, checked against the SHA-256.
186
- await downloadFile(teepin.storage, 'my-bucket', 'videos/a.mp4', './copy.mp4');
187
- const { data } = await teepin.storage.download('my-bucket', 'notes.txt'); // into memory
92
+ ```typescript
93
+ console.log(res.meta.attestation); // 'verified', 'verified-older-release' or 'not-verified'
94
+ const proof = await teepin.inference.getAttestation('teepin/glm-5.3'); // the live proof, to verify yourself
95
+ ```
188
96
 
189
- // Every object under a prefix, or one page like folders.
190
- for await (const obj of teepin.storage.objects('my-bucket', 'videos/')) console.log(obj.key, obj.size);
191
- const page = await teepin.storage.listObjects('my-bucket', { prefix: 'videos/', delimiter: '/' });
97
+ Anything less than `verified` is a warning to show, never an error. In the model list, a confidential model that
98
+ isn't fully verified right now carries `attestation: { level, warning }`.
192
99
 
193
- // Write only if nothing is there; read only if it changed.
194
- await teepin.storage.putObject('my-bucket', 'lock', '1', { ifNoneMatch: true });
195
- const changed = await teepin.storage.getObject('my-bucket', 'notes.txt', { ifNoneMatch: etag }); // null if not
100
+ ### Errors, retries and rate limits
196
101
 
197
- // Rename is instant at any size; copy writes the bytes again.
198
- await teepin.storage.renameObject('my-bucket', 'videos/a.mp4', 'archive/a.mp4');
199
- await teepin.storage.copyObject('backup', 'a.mp4', 'my-bucket', 'archive/a.mp4');
200
- await teepin.storage.deleteObjects('my-bucket', keys); // 1000 to a request
102
+ Limits are per API key: by default 60 chat completions a minute and 10 at a time. A 429 is a `RateLimitError` with
103
+ `retryAfter` in seconds.
201
104
 
202
- // Links that work without your key.
203
- const link = await teepin.storage.presignDownload('my-bucket', 'archive/a.mp4', { expiresIn: 3600 });
204
- const up = await teepin.storage.presignUpload('inbox', 'upload.bin', { maxBytes: 100 * 2 ** 20 });
205
- ```
105
+ The server bills an answer once the model has written it, even if the caller has gone. So a chat call is retried
106
+ only when it certainly wasn't billed: on a 429, on Teepin's own `internal_error`, `upstream_error`,
107
+ `model_unavailable` or `billing_unavailable`, and on a connection that never reached the server. It is never
108
+ retried after a timeout or a dropped connection. Retries happen up to `maxRetries` times (default 2), waiting as
109
+ `Retry-After` says or with backoff. A stream is never retried once it has started.
206
110
 
207
- **Keep your API key on your server.** Every call but a signed link sends it.
208
- To let a browser download or upload one object, sign a link on your server
209
- and hand the page only the link; signed links work from any website:
111
+ A chat completion that isn't streamed may take up to 600 seconds. Change it per call with `{ timeout }`, or for
112
+ the client with `teepin.inference.chatTimeout`.
210
113
 
211
114
  ```typescript
212
- // In the page: one PUT to the link your server signed.
213
- await fetch(up.url, { method: up.method, headers: up.headers, body: file });
214
- ```
115
+ import { isInsufficientCreditError, isRateLimitError, isNotFoundError } from '@teepin/sdk';
215
116
 
216
- The calls map one to one onto the API reference at
217
- `https://api.teepin.com/v1/openapi.json`.
117
+ try {
118
+ await teepin.inference.chat.completions.create({ model: 'teepin/glm-5.3', messages: [{ role: 'user', content: 'Hi' }] });
119
+ } catch (err) {
120
+ if (isInsufficientCreditError(err)) console.error('add credit in the console');
121
+ else if (isRateLimitError(err)) console.error(`try again in ${err.retryAfter ?? 'a few'} seconds`);
122
+ else if (isNotFoundError(err)) console.error('no such model, or this key may not call it');
123
+ else throw err;
124
+ }
125
+ ```
218
126
 
219
- ### Teepin Inference
127
+ ### With the OpenAI SDK
220
128
 
221
- OpenAI-compatible chat completions on Teepin's models, confidential ones
222
- included. The key needs the `inference:invoke` permission. Requests, responses
223
- and chunks use the API's own field names (`max_completion_tokens`,
224
- `finish_reason`, ...); fields the SDK does not know yet pass through and stay
225
- readable.
129
+ The API is OpenAI-compatible, so OpenAI's own SDK works too:
226
130
 
227
131
  ```typescript
228
- import { TeepinClient } from '@teepin/sdk';
132
+ import OpenAI from 'openai';
229
133
 
230
- const teepin = new TeepinClient(); // reads TEEPIN_API_KEY (and TEEPIN_BASE_URL)
231
-
232
- // The models this key may call. A confidential model that is not fully
233
- // verified right now carries attestation: { level, warning }.
234
- const models = await teepin.inference.listModels();
235
- for (const m of models) console.log(m.id, m.confidential, m.attestation?.warning ?? '');
236
-
237
- // A whole completion, with the response's metadata.
238
- const res = await teepin.inference.chat.completions.create({
239
- model: 'teepin/llama-3.3-70b',
134
+ const openai = new OpenAI({ baseURL: 'https://api.teepin.com/v1', apiKey: process.env.TEEPIN_API_KEY });
135
+ const res = await openai.chat.completions.create({
136
+ model: 'teepin/glm-5.3',
240
137
  messages: [{ role: 'user', content: 'Hello' }],
241
- max_completion_tokens: 256,
242
138
  });
243
- console.log(res.choices[0].message.content, res.usage?.total_tokens);
244
- console.log(res.meta.attestation, res.meta.requestId, res.meta.rateLimit); // 'verified' | 'verified-older-release' | 'not-verified' | undefined
245
-
246
- // A stream: it resolves once the headers are in (stream.meta is ready then).
247
- // Ask for include_usage to get the usage on the last chunk.
248
- const stream = await teepin.inference.chat.completions.create(
249
- {
250
- model: 'teepin/llama-3.3-70b',
251
- messages: [{ role: 'user', content: 'Write a haiku' }],
252
- stream: true,
253
- stream_options: { include_usage: true },
254
- },
255
- { signal: AbortSignal.timeout(120_000) },
256
- );
257
- for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta.content ?? '');
258
- console.log(stream.usage);
259
-
260
- // The live attestation proof of a confidential model, as the provider sends it.
261
- const proof = await teepin.inference.getAttestation('teepin/confidential');
262
139
  ```
263
140
 
264
- A stream throws `StreamError` (an `APIError`, code `stream_error`) when the
265
- server reports a failure part way through, and `StreamEndedError` when it
266
- closes without `[DONE]`. Leaving the `for await` loop early, or aborting the
267
- signal, closes the connection. Not verified attestation is a warning in
268
- `meta.attestation`, never an error.
269
-
270
- The server bills a completion once it has the model's answer, even if the
271
- caller has gone, so a chat call (streamed or not) is retried only when it
272
- certainly was not billed: on 429, on Teepin's own 500 `internal_error`, 502
273
- `upstream_error` and 503 `model_unavailable` or `billing_unavailable` (answers
274
- with the API's error envelope), and on a connection that never reached the
275
- server (refused, or DNS failed). It is never retried after a timeout, a
276
- connection reset or cut after sending, a 504, a 5xx without Teepin's envelope
277
- (such as a proxy's HTML 502), or a 400, 401, 402, 403, 404 or 413.
278
- `listModels` and `getAttestation` are retried on 429, any 5xx and any
279
- connection error. A stream is never retried once its headers are in.
280
-
281
- Retries happen up to `maxRetries` times (default 2; set it on the client or per
282
- call with `{ maxRetries }`), waiting as `Retry-After` says (at most 30 s) or
283
- with jittered backoff from 0.5 s to 8 s.
284
-
285
- A chat completion that is not streamed may take up to 600 s by default (a long
286
- answer takes minutes), not the client's `timeout`; change it per call with
287
- `{ timeout }` or for the client with `teepin.inference.chatTimeout`. A stream's
288
- timeout covers only the wait for its headers. Other calls follow the client's
289
- `timeout`.
290
-
291
- ## Error Handling
292
-
293
- Errors from the API are `APIError` subclasses with the API's stable `code`:
294
-
295
- ```typescript
296
- import { NotFoundError, PaymentRequiredError, ChecksumMismatchError, UploadError, isRetryable } from '@teepin/sdk';
141
+ This SDK adds the attestation results, billing-safe retries and Teepin's typed errors.
297
142
 
298
- try {
299
- await teepin.storage.statObject('my-bucket', 'nope');
300
- } catch (err) {
301
- if (err instanceof NotFoundError) {
302
- // 404: err.code is bucket_not_found or object_not_found
303
- } else if (err instanceof PaymentRequiredError && err.onHold) {
304
- // no credit; held data is deleted after err.deleteAfter
305
- } else if (isRetryable(err)) {
306
- // the network or a busy server, after the client's own retries
307
- } else {
308
- throw err;
309
- }
310
- }
311
- ```
312
-
313
- Also: `UnauthorizedError` (401), `ForbiddenError` (403, e.g. no storage:write),
314
- `ConflictError` (409), `PreconditionFailedError` (412), `TooLargeError` (413),
315
- `RateLimitError` (429), `ConnectionError`, `ObjectChangedError`,
316
- `ChecksumMismatchError`, `UploadError` (with `uploadId`, for resuming), and for
317
- inference streams `StreamError` and `StreamEndedError`. Every API error carries
318
- `status`, `code`, `type` (inference), `serverMessage`, `requestId` for support
319
- and `retryAfter` (seconds, when the server sent Retry-After). Helpers:
320
- `isNotFoundError`, `isRateLimitError`, `isInsufficientCreditError` (402),
321
- `isPermissionError` (403) and `isRetryable`. Requests that fail for a passing
322
- reason are retried with backoff, `maxRetries` times (default 4; 2 for
323
- inference).
324
-
325
- ## Configuration
143
+ ## Teepin Storage
326
144
 
327
- ### Environment Variables
145
+ Buckets and files, with large uploads in parallel parts. The key needs `storage:read` to read and `storage:write`
146
+ to write.
328
147
 
329
- `new TeepinClient()` reads these when `apiKey` or `baseUrl` isn't given, as OpenAI's SDKs read `OPENAI_API_KEY`
330
- and `OPENAI_BASE_URL`. A value given in code always wins.
148
+ ### Buckets
331
149
 
332
- ```bash
333
- export TEEPIN_API_KEY=your-api-key
334
- export TEEPIN_BASE_URL=https://api.teepin.com # optional; the default
150
+ ```typescript
151
+ await teepin.storage.createBucket('my-bucket');
152
+ const buckets = await teepin.storage.listBuckets();
153
+ await teepin.storage.deleteBucket('my-bucket'); // only when it is empty
335
154
  ```
336
155
 
337
- ### Custom Client Configuration
156
+ ### Put and get
338
157
 
339
158
  ```typescript
340
- const client = new TeepinClient({
341
- baseUrl: 'https://api.teepin.com', // the default
342
- apiKey: process.env.TEEPIN_API_KEY,
343
- timeout: 60000, // 60 seconds
344
- });
159
+ // Strings, bytes, Blobs, Files and streams.
160
+ await teepin.storage.upload('my-bucket', 'notes.txt', 'hello');
161
+
162
+ const { object, data } = await teepin.storage.download('my-bucket', 'notes.txt');
163
+ console.log(object.size, new TextDecoder().decode(data));
164
+
165
+ const info = await teepin.storage.statObject('my-bucket', 'notes.txt'); // size, etag, contentType
345
166
  ```
346
167
 
347
- ## API Reference
168
+ ### Large files
348
169
 
349
- ### TeepinClient
170
+ Files on disk use `@teepin/sdk/node`. An upload of any size goes in one request up to 16 MiB, and in parallel
171
+ parts above that. Each part is retried on its own and checked against the bytes sent. `resume` continues an upload
172
+ that was cut off. A download fetches ranges in parallel and checks the SHA-256.
350
173
 
351
174
  ```typescript
352
- class TeepinClient {
353
- constructor(config?: TeepinConfig);
175
+ import { uploadFile, downloadFile } from '@teepin/sdk/node';
354
176
 
355
- readonly instances: InstancesService;
356
- readonly storage: StorageService;
357
- readonly inference: InferenceService;
358
- }
177
+ await uploadFile(teepin.storage, 'my-bucket', 'videos/a.mp4', './a.mp4', {
178
+ resume: true,
179
+ onProgress: (done, total) => console.log(`${done} of ${total}`),
180
+ });
181
+ await downloadFile(teepin.storage, 'my-bucket', 'videos/a.mp4', './copy.mp4');
359
182
  ```
360
183
 
361
- ### InferenceService
184
+ ### List
362
185
 
363
186
  ```typescript
364
- interface InferenceService {
365
- listModels(options?: InferenceRequestOptions): Promise<Model[]>;
366
- getAttestation(modelId: string, options?: InferenceRequestOptions): Promise<Record<string, unknown>>;
367
- chat: {
368
- completions: {
369
- create(body: ChatCompletionCreateParams & { stream: true }, options?): Promise<ChatCompletionStream>;
370
- create(body: ChatCompletionCreateParams, options?): Promise<ChatCompletion & { meta: ResponseMeta }>;
371
- };
372
- };
373
- }
187
+ // Every object under a prefix.
188
+ for await (const obj of teepin.storage.objects('my-bucket', 'videos/')) console.log(obj.key, obj.size);
374
189
 
375
- interface InferenceRequestOptions {
376
- signal?: AbortSignal;
377
- maxRetries?: number;
378
- timeout?: number;
379
- headers?: Record<string, string>;
380
- }
190
+ // One page, with "folders".
191
+ const page = await teepin.storage.listObjects('my-bucket', { prefix: 'videos/', delimiter: '/' });
381
192
  ```
382
193
 
383
- ### InstancesService
194
+ ### Signed links
195
+
196
+ Links that work without your key, for a limited time:
384
197
 
385
198
  ```typescript
386
- interface InstancesService {
387
- /**
388
- * Create a new instance
389
- */
390
- create(request: CreateInstanceRequest): Promise<Instance>;
391
-
392
- /**
393
- * List all instances
394
- */
395
- list(): Promise<Instance[]>;
396
-
397
- /**
398
- * Get instance by ID
399
- */
400
- get(instanceId: string): Promise<Instance>;
401
-
402
- /**
403
- * Delete an instance
404
- */
405
- delete(instanceId: string): Promise<void>;
406
-
407
- /**
408
- * List available instance types
409
- */
410
- listTypes(): Promise<InstanceType[]>;
411
-
412
- /**
413
- * Get instance metrics
414
- */
415
- metrics(instanceId: string): Promise<Record<string, any>>;
416
- }
199
+ const link = await teepin.storage.presignDownload('my-bucket', 'videos/a.mp4', { expiresIn: 3600 });
200
+ console.log(link.url);
201
+
202
+ const up = await teepin.storage.presignUpload('my-bucket', 'inbox/upload.bin', { maxBytes: 100 * 2 ** 20 });
203
+ // In the browser: one PUT to the link your server signed.
204
+ await fetch(up.url, { method: up.method, headers: up.headers, body: file });
417
205
  ```
418
206
 
419
- ### Types
207
+ ### Rename, copy and delete
420
208
 
421
209
  ```typescript
422
- interface CreateInstanceRequest {
423
- name: string;
424
- image: string;
425
- gpuVram?: string;
426
- cpuUnits?: number;
427
- memory?: string;
428
- env?: Record<string, string>;
429
- ports?: PortMapping[];
430
- labels?: Record<string, string>;
431
- }
432
-
433
- interface Instance {
434
- id: string;
435
- name: string;
436
- image: string;
437
- status: string;
438
- gpuVram?: string;
439
- allocatedVram?: string;
440
- cpuUnits: number;
441
- memory: string;
442
- endpoint?: string;
443
- internalIp?: string;
444
- createdAt: Date;
445
- updatedAt: Date;
446
- labels?: Record<string, string>;
447
- }
210
+ await teepin.storage.renameObject('my-bucket', 'videos/a.mp4', 'archive/a.mp4'); // instant at any size
211
+ await teepin.storage.copyObject('backup', 'a.mp4', 'my-bucket', 'archive/a.mp4'); // to backup/a.mp4, from my-bucket
212
+ await teepin.storage.deleteObject('my-bucket', 'notes.txt');
213
+ await teepin.storage.deleteObjects('my-bucket', ['a.txt', 'b.txt']); // up to 1000 a request
448
214
  ```
449
215
 
450
- ## Development
216
+ ### The S3 endpoint
451
217
 
452
- ### Build
218
+ Teepin Storage also speaks S3 at `https://s3.teepin.com`, so the AWS CLI, boto3, the AWS SDKs and rclone work
219
+ unchanged. Create an S3 access key in the console (a project's Settings, then S3 access keys). The region is
220
+ `teepin`.
453
221
 
454
222
  ```bash
455
- npm run build
223
+ aws configure set aws_access_key_id TPAK... --profile teepin
224
+ aws configure set aws_secret_access_key ... --profile teepin
225
+ aws configure set region teepin --profile teepin
226
+ aws --profile teepin --endpoint-url https://s3.teepin.com s3 ls
456
227
  ```
457
228
 
458
- ### Watch Mode
229
+ The buckets are the same ones this SDK and the console show.
459
230
 
460
- ```bash
461
- npm run dev
462
- ```
231
+ ## Errors
463
232
 
464
- ### Lint
233
+ Errors from the API are `APIError` subclasses with the API's stable `code`, `status`, `serverMessage` and
234
+ `requestId` (for support):
465
235
 
466
- ```bash
467
- npm run lint
468
- ```
236
+ - `UnauthorizedError` (401), `PaymentRequiredError` (402), `ForbiddenError` (403), `NotFoundError` (404),
237
+ `ConflictError` (409), `PreconditionFailedError` (412), `TooLargeError` (413), `RateLimitError` (429);
238
+ - `ConnectionError` when the server couldn't be reached;
239
+ - `StreamError` and `StreamEndedError` for inference streams;
240
+ - `ChecksumMismatchError`, `ObjectChangedError` and `UploadError` (with `uploadId`, for resuming) for storage.
469
241
 
470
- ### Format
242
+ Helpers: `isNotFoundError`, `isRateLimitError`, `isInsufficientCreditError`, `isPermissionError` and
243
+ `isRetryable`. Storage calls that fail for a passing reason are retried with backoff, up to `maxRetries` times
244
+ (default 4).
471
245
 
472
- ```bash
473
- npm run format
246
+ ## Configuration
247
+
248
+ ```typescript
249
+ const teepin = new TeepinClient({
250
+ apiKey: 'tpk_...', // default: TEEPIN_API_KEY
251
+ baseUrl: 'https://api.teepin.com', // default: TEEPIN_BASE_URL, else this
252
+ timeout: 60_000, // milliseconds for a call that isn't a transfer or a chat completion
253
+ maxRetries: 4, // 0 turns retries off
254
+ });
474
255
  ```
475
256
 
476
- ## Examples
257
+ ## Other APIs
477
258
 
478
- See the `examples/` directory for more examples:
259
+ The SDK also has clients for services that aren't offered yet. They answer 404 until they launch.
479
260
 
480
- - `basic.ts` - Basic usage
481
- - `concurrent.ts` - Concurrent deployments
482
- - `storage.ts` - Create a bucket, upload/download/delete objects, mint a signed link
483
- - `inference.ts` - List models, chat, stream and check attestation
261
+ ## Examples
484
262
 
485
- Run examples:
263
+ `examples/inference.mts` and `examples/storage.mts` run with Node 22.6 or newer:
486
264
 
487
265
  ```bash
488
- # Install ts-node
489
- npm install -g ts-node
490
-
491
- # Run example
492
- ts-node examples/basic.ts
266
+ npm run build
267
+ TEEPIN_API_KEY=tpk_... node --experimental-strip-types examples/inference.mts
493
268
  ```
494
269
 
495
- ## Contributing
270
+ ## Development
496
271
 
497
- See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
272
+ ```bash
273
+ npm install
274
+ npm run build
275
+ npm test
276
+ npm run typecheck
277
+ ```
498
278
 
499
279
  ## License
500
280
 
501
- Apache License 2.0
281
+ Apache License 2.0. See [LICENSE](LICENSE).
@@ -8,7 +8,7 @@ const inference_js_1 = require("./inference.js");
8
8
  const instances_js_1 = require("./instances.js");
9
9
  const storage_js_1 = require("./storage.js");
10
10
  /** This SDK's version, sent in the User-Agent where the runtime allows it. */
11
- exports.VERSION = '0.2.0';
11
+ exports.VERSION = '0.2.1';
12
12
  /** The public API. */
13
13
  exports.DEFAULT_BASE_URL = 'https://api.teepin.com';
14
14
  /** An environment variable, where there is an environment (Node, Deno, Bun;
@@ -5,7 +5,7 @@ import { InferenceService } from './inference.js';
5
5
  import { InstancesService } from './instances.js';
6
6
  import { StorageService } from './storage.js';
7
7
  /** This SDK's version, sent in the User-Agent where the runtime allows it. */
8
- export const VERSION = '0.2.0';
8
+ export const VERSION = '0.2.1';
9
9
  /** The public API. */
10
10
  export const DEFAULT_BASE_URL = 'https://api.teepin.com';
11
11
  /** An environment variable, where there is an environment (Node, Deno, Bun;
@@ -2,7 +2,7 @@ import { InferenceService } from './inference.js';
2
2
  import { InstancesService } from './instances.js';
3
3
  import { StorageService } from './storage.js';
4
4
  /** This SDK's version, sent in the User-Agent where the runtime allows it. */
5
- export declare const VERSION = "0.2.0";
5
+ export declare const VERSION = "0.2.1";
6
6
  /** The public API. */
7
7
  export declare const DEFAULT_BASE_URL = "https://api.teepin.com";
8
8
  /** Client configuration. Usually nothing: the key and address come from the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@teepin/sdk",
3
- "version": "0.2.0",
4
- "description": "Official TypeScript/JavaScript SDK for Teepin Web Services: storage and compute, for Node 20+ and browsers",
3
+ "version": "0.2.1",
4
+ "description": "Official TypeScript/JavaScript SDK for Teepin Web Services: Teepin Inference (AI models in confidential enclaves, OpenAI-compatible) and Teepin Storage, for Node 20+ and browsers",
5
5
  "files": [
6
6
  "dist",
7
7
  "README.md",
@@ -18,7 +18,10 @@
18
18
  "storage",
19
19
  "object-storage",
20
20
  "s3",
21
- "gpu",
21
+ "inference",
22
+ "llm",
23
+ "openai",
24
+ "confidential-computing",
22
25
  "cloud",
23
26
  "sdk",
24
27
  "typescript"
@@ -32,7 +35,7 @@
32
35
  "bugs": {
33
36
  "url": "https://github.com/FlashbackAi/teepin-node/issues"
34
37
  },
35
- "homepage": "https://teepin.com",
38
+ "homepage": "https://www.teepin.com/docs/sdks/node",
36
39
  "dependencies": {},
37
40
  "devDependencies": {
38
41
  "@types/node": "^16.18.0",