@hotcell/sdk 0.1.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/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # @hotcell/sdk
2
+
3
+ TypeScript client for [**hotcell**](https://github.com/sinameraji/hotcell) — self-hostable sandbox infrastructure for AI agents. Zero runtime dependencies; mirrors the Cloudflare Sandbox SDK surface so existing harnesses port with minimal changes, but points at *your* self-hosted daemon.
4
+
5
+ ```bash
6
+ npm install @hotcell/sdk
7
+ ```
8
+
9
+ ```ts
10
+ import { HotcellClient } from "@hotcell/sdk";
11
+
12
+ const client = new HotcellClient({ endpoint: "http://127.0.0.1:4750" /*, apiKey */ });
13
+
14
+ const sandbox = await client.getSandbox(); // fresh sandbox
15
+ const { stdout } = await sandbox.exec("python3 -c 'print(2+2)'");
16
+
17
+ await sandbox.writeFile("/workspace/app.py", "print('hi')");
18
+ for await (const ev of sandbox.execStream("python3 /workspace/app.py")) {
19
+ if (ev.type === "stdout") process.stdout.write(ev.data);
20
+ }
21
+
22
+ await sandbox.destroy();
23
+ ```
24
+
25
+ ## Highlights
26
+
27
+ - `exec` / `execStream`, `writeFile`/`readFile`/`mkdir`/`listFiles`, `watch`
28
+ - background processes (`startProcess`/`listProcesses`/`killProcess`/`streamLogs`/`waitForPort`)
29
+ - preview URLs (`exposePort`), persistent `Session`s, stateful `CodeContext`s
30
+ - backups, lifecycle (`stop`/`start`, idle auto-pause), `metrics`/`metricsHistory`
31
+ - egress credential proxy: `createEgressToken`/`listEgressTokens`/`revokeEgressToken`, or create with `{ egress: true }`
32
+
33
+ Configure the endpoint via the constructor or `HOTCELL_ENDPOINT`, and the API key via `apiKey` or `HOTCELL_API_KEY`.
34
+
35
+ See the [main README](https://github.com/sinameraji/hotcell) for the daemon, CLI, dashboard, and full docs.
36
+
37
+ ## License
38
+
39
+ Apache-2.0
@@ -0,0 +1,534 @@
1
+ /**
2
+ * @hotcell/sdk — TypeScript client for the hotcell daemon.
3
+ *
4
+ * Mirrors the Cloudflare Sandbox SDK surface so existing harnesses port with
5
+ * minimal changes, but points at *your* self-hosted daemon instead of the edge.
6
+ *
7
+ * const client = new HotcellClient({ endpoint: "http://127.0.0.1:4750" });
8
+ * const sandbox = await client.getSandbox();
9
+ * const { stdout } = await sandbox.exec("python3 -c 'print(2+2)'");
10
+ * await sandbox.destroy();
11
+ */
12
+ export type ExecEvent = {
13
+ type: "stdout";
14
+ data: string;
15
+ } | {
16
+ type: "stderr";
17
+ data: string;
18
+ } | {
19
+ type: "exit";
20
+ exitCode: number;
21
+ };
22
+ export interface ExecOptions {
23
+ cwd?: string;
24
+ env?: Record<string, string>;
25
+ /** Run inside a persistent session (its cwd/env apply and `cd` persists). */
26
+ sessionId?: string;
27
+ /** Called for each stdout/stderr chunk as it streams in. */
28
+ onOutput?: (stream: "stdout" | "stderr", data: string) => void;
29
+ }
30
+ export interface ExecResult {
31
+ stdout: string;
32
+ stderr: string;
33
+ exitCode: number;
34
+ success: boolean;
35
+ }
36
+ export type SandboxStatus = "running" | "paused" | "stopped";
37
+ /** Hard per-sandbox resource caps (0/absent = unlimited). */
38
+ export interface SandboxLimits {
39
+ /** Memory cap in MiB. */
40
+ memoryMb?: number;
41
+ /** CPU cap in fractional cores (e.g. 0.5, 2). */
42
+ cpus?: number;
43
+ /** Max processes/threads. */
44
+ pidsLimit?: number;
45
+ }
46
+ export interface SandboxInfo {
47
+ id: string;
48
+ image: string;
49
+ /**
50
+ * Lifecycle state. `paused` is entered automatically after `sleepAfterMs` of
51
+ * inactivity (the next operation transparently resumes it); `stopped` is a
52
+ * manual stop that does not auto-resume.
53
+ */
54
+ status: SandboxStatus;
55
+ createdAt: string;
56
+ labels: Record<string, string>;
57
+ /** Whether `/workspace` is backed by a volume that survives stop/start. */
58
+ persist: boolean;
59
+ /** ISO timestamp of the last activity (drives idle auto-pause). */
60
+ lastActivityAt: string;
61
+ /** Idle timeout (ms) before auto-pause; 0 disables it. */
62
+ sleepAfterMs: number;
63
+ /** Resolved hard resource caps applied to the sandbox ({} = unlimited). */
64
+ limits: SandboxLimits;
65
+ }
66
+ export interface FileInfo {
67
+ path: string;
68
+ name: string;
69
+ isDirectory: boolean;
70
+ size: number;
71
+ modifiedAt: string;
72
+ }
73
+ /** Live resource snapshot for a running sandbox. */
74
+ export interface SandboxStats {
75
+ cpuPercent: number;
76
+ cpuTotalUsageNs: number;
77
+ onlineCpus: number;
78
+ memBytes: number;
79
+ memLimitBytes: number;
80
+ netRxBytes: number;
81
+ netTxBytes: number;
82
+ pids: number;
83
+ sampledAt: string;
84
+ }
85
+ /** Cumulative, time-integrated usage backing the cost meter. */
86
+ export interface SandboxUsage {
87
+ cpuSeconds: number;
88
+ memByteSeconds: number;
89
+ egressBytes: number;
90
+ /** LLM-provider calls made through the egress credential proxy. */
91
+ providerCalls: number;
92
+ /** Bytes exchanged with providers through the egress proxy. */
93
+ providerBytes: number;
94
+ /** Prompt/input tokens billed across provider calls. */
95
+ providerTokensIn: number;
96
+ /** Completion/output tokens billed across provider calls. */
97
+ providerTokensOut: number;
98
+ /** Provider-reported LLM cost in USD (e.g. OpenRouter `usage.cost`), summed. */
99
+ providerCost: number;
100
+ }
101
+ /** A provider reachable through the egress credential proxy (LLM gateway). */
102
+ export interface EgressProvider {
103
+ name: string;
104
+ /** Base URL to point the provider SDK at (sandbox uses the token as the key). */
105
+ baseUrl: string;
106
+ /** Conventional env var for the base URL (e.g. OPENAI_BASE_URL). */
107
+ baseUrlEnv?: string;
108
+ /** Conventional env var for the API key (set it to the egress token). */
109
+ keyEnv?: string;
110
+ }
111
+ /**
112
+ * Per-token egress policy. Every field is optional; an omitted/empty policy means
113
+ * "unlimited" (the original behaviour). Enforced by the daemon's egress gateway.
114
+ */
115
+ export interface EgressPolicy {
116
+ /** ISO timestamp after which the token is rejected (403). */
117
+ expiresAt?: string;
118
+ /** Sugar accepted on mint: expire `ttlMs` after creation. */
119
+ ttlMs?: number;
120
+ /** Cumulative USD this token may spend (402 once reached). */
121
+ spendCapUsd?: number;
122
+ /** Sliding-window rate limit (per token). */
123
+ rateLimit?: {
124
+ calls?: number;
125
+ tokens?: number;
126
+ windowMs: number;
127
+ };
128
+ /** Allowed model ids/prefix-globs. Omit = all. */
129
+ models?: string[];
130
+ /** Allowed provider names. Omit = every configured provider. */
131
+ providers?: string[];
132
+ }
133
+ /** A minted egress token plus the provider base URLs it unlocks. */
134
+ export interface EgressToken {
135
+ token: string;
136
+ /** The policy bound to the token (echoed back on mint). */
137
+ policy?: EgressPolicy;
138
+ providers: EgressProvider[];
139
+ }
140
+ /** An egress token with its policy + running spend, as returned by `listEgressTokens`. */
141
+ export interface EgressTokenInfo {
142
+ token: string;
143
+ policy: EgressPolicy;
144
+ spendUsd: number;
145
+ /** USD left under the spend cap, or null when uncapped. */
146
+ spendRemaining: number | null;
147
+ }
148
+ /** Per-resource cost breakdown in the daemon's configured currency. */
149
+ export interface CostBreakdown {
150
+ cpu: number;
151
+ mem: number;
152
+ egress: number;
153
+ /** LLM cost (provider-reported, e.g. OpenRouter `usage.cost`). */
154
+ provider: number;
155
+ total: number;
156
+ }
157
+ /** Metrics + cost for a sandbox; `live` is null when not running. */
158
+ export interface SandboxMetrics {
159
+ status: SandboxStatus;
160
+ live: SandboxStats | null;
161
+ usage: SandboxUsage;
162
+ cost: CostBreakdown;
163
+ }
164
+ export interface CreateOptions {
165
+ image?: string;
166
+ env?: Record<string, string>;
167
+ labels?: Record<string, string>;
168
+ /**
169
+ * Back `/workspace` with a named volume so files survive `stop`/`start` and
170
+ * container recreation. Defaults to true.
171
+ */
172
+ persist?: boolean;
173
+ /**
174
+ * Auto-pause the sandbox after this many milliseconds of inactivity (the idle
175
+ * reaper frees compute while the workspace volume persists; the next operation
176
+ * auto-resumes it). Omit or set 0 to disable. Defaults to the daemon's
177
+ * `SBX_SLEEP_AFTER_MS`.
178
+ */
179
+ sleepAfter?: number;
180
+ /**
181
+ * Wire the sandbox to the egress credential proxy: the daemon mints an egress
182
+ * token and injects provider base-URL + key env vars (e.g. `OPENAI_BASE_URL`,
183
+ * `OPENAI_API_KEY`) so LLM SDKs inside route through the gateway with no real
184
+ * keys. Only providers configured on the daemon are wired. Defaults to false.
185
+ *
186
+ * Pass an `EgressPolicy` object instead of `true` to bind the minted token to a
187
+ * policy (TTL, spend cap, rate limit, model/provider scope).
188
+ */
189
+ egress?: boolean | EgressPolicy;
190
+ /**
191
+ * Hard ceiling (USD) on this sandbox's total LLM-provider cost across all its
192
+ * egress tokens; the gateway returns 402 once reached. A blast-radius backstop
193
+ * independent of per-token caps. Omit/0 = unlimited (or the daemon default).
194
+ */
195
+ egressSpendCapUsd?: number;
196
+ /**
197
+ * Ordered shell commands run once, after the container starts at create time
198
+ * (e.g. `["npm i kimiflare"]`). Best-effort: a non-zero exit is logged on the
199
+ * daemon, not fatal. Not re-run on resume — with persistence (the default) the
200
+ * workspace volume already holds the result.
201
+ */
202
+ setup?: string[];
203
+ /**
204
+ * Git repository URL cloned into `/workspace` at create time (before `setup`),
205
+ * so an agent comes up with the code in place. Private repos: embed a token in
206
+ * the URL (`https://<token>@github.com/owner/repo.git`). A clone failure fails
207
+ * the create.
208
+ */
209
+ repo?: string;
210
+ /** Branch/tag to check out when cloning `repo` (default: the repo's default branch). */
211
+ repoRef?: string;
212
+ /** Hard memory cap in MiB (overrides the daemon default; 0 = unlimited). */
213
+ memoryMb?: number;
214
+ /** Hard CPU cap in fractional cores, e.g. 0.5 (overrides the daemon default). */
215
+ cpus?: number;
216
+ /** Hard process/thread cap (overrides the daemon default; 0 = unlimited). */
217
+ pidsLimit?: number;
218
+ }
219
+ export interface WriteFileOptions {
220
+ mode?: string;
221
+ }
222
+ export interface MkdirOptions {
223
+ parents?: boolean;
224
+ }
225
+ export interface StartProcessOptions {
226
+ cwd?: string;
227
+ env?: Record<string, string>;
228
+ }
229
+ /** A long-running background process tracked by the daemon. */
230
+ export interface ProcessHandle {
231
+ procId: string;
232
+ pid: number;
233
+ command: string;
234
+ status: "running" | "exited";
235
+ exitCode: number | null;
236
+ startedAt: string;
237
+ logPath: string;
238
+ }
239
+ export interface WaitForPortOptions {
240
+ timeoutMs?: number;
241
+ intervalMs?: number;
242
+ host?: string;
243
+ }
244
+ /** A port exposed through the daemon's preview-URL reverse proxy. */
245
+ export interface ExposedPort {
246
+ port: number;
247
+ exposeId: string;
248
+ token: string | null;
249
+ createdAt: string;
250
+ url: string;
251
+ }
252
+ /** A persistent execution context (working directory + env) inside a sandbox. */
253
+ export interface SessionInfo {
254
+ sessionId: string;
255
+ cwd: string;
256
+ env: Record<string, string>;
257
+ createdAt: string;
258
+ }
259
+ export interface CreateSessionOptions {
260
+ /** Explicit session id; a random one is assigned when omitted. */
261
+ id?: string;
262
+ cwd?: string;
263
+ env?: Record<string, string>;
264
+ }
265
+ /** A filesystem change observed by `watch`. */
266
+ export interface FileChangeEvent {
267
+ type: "created" | "modified" | "deleted";
268
+ path: string;
269
+ }
270
+ export type CodeLanguage = "python" | "javascript";
271
+ /** Public view of a code-interpreter context. */
272
+ export interface CodeContextInfo {
273
+ contextId: string;
274
+ language: CodeLanguage;
275
+ createdAt: string;
276
+ }
277
+ /** A single rich output from a code cell. */
278
+ export interface CodeOutput {
279
+ type: "text";
280
+ text: string;
281
+ }
282
+ /** The result of running a code cell. */
283
+ export interface CodeResult {
284
+ stdout: string;
285
+ stderr: string;
286
+ results: CodeOutput[];
287
+ error: string | null;
288
+ }
289
+ /** Metadata for a workspace backup. */
290
+ export interface BackupInfo {
291
+ backupId: string;
292
+ sandboxId: string;
293
+ createdAt: string;
294
+ /** Size of the backup tarball in bytes. */
295
+ bytes: number;
296
+ }
297
+ /** Host capacity + admission status from `GET /capacity`. */
298
+ export interface CapacitySnapshot {
299
+ enforced: boolean;
300
+ overcommit: number;
301
+ defaultReservationMb: number;
302
+ memory: {
303
+ budgetMb: number;
304
+ committedMb: number;
305
+ availableMb: number;
306
+ };
307
+ cpu: {
308
+ budget: number;
309
+ committed: number;
310
+ available: number;
311
+ };
312
+ running: number;
313
+ /** Approx. number of additional default-reservation sandboxes that still fit. */
314
+ fits: number;
315
+ }
316
+ /** Daemon metadata from `GET /info`. */
317
+ export interface DaemonInfo {
318
+ driver: string;
319
+ drivers: string[];
320
+ defaultImage: string;
321
+ proxyPort: number;
322
+ egressPort: number;
323
+ egressProviders: string[];
324
+ costCpuPerHour: number;
325
+ costMemGbPerHour: number;
326
+ costEgressPerGb: number;
327
+ defaultSleepAfterMs: number;
328
+ auth: boolean;
329
+ otlp: boolean;
330
+ }
331
+ export interface HotcellClientOptions {
332
+ endpoint?: string;
333
+ /**
334
+ * API key sent as `Authorization: Bearer <key>` on every request. Required when
335
+ * the daemon is started with `SBX_API_KEY`. Defaults to the `SBX_API_KEY` env
336
+ * var when omitted.
337
+ */
338
+ apiKey?: string;
339
+ }
340
+ /** One live-metrics sample (for sparklines / history charts). */
341
+ export interface MetricSample {
342
+ at: string;
343
+ cpuPercent: number;
344
+ memBytes: number;
345
+ netRxBytes: number;
346
+ netTxBytes: number;
347
+ pids: number;
348
+ }
349
+ export declare class HotcellClient {
350
+ readonly endpoint: string;
351
+ private readonly apiKey;
352
+ constructor(opts?: HotcellClientOptions);
353
+ /** Build request headers, attaching the API key and any extras. */
354
+ authHeaders(extra?: Record<string, string>): Record<string, string>;
355
+ /** Create a fresh sandbox (Cloudflare-style: omit id to provision a new one). */
356
+ getSandbox(id?: string, options?: CreateOptions): Promise<Sandbox>;
357
+ /** List all sandboxes managed by the daemon. */
358
+ list(): Promise<SandboxInfo[]>;
359
+ /** Daemon health + active runtime driver. */
360
+ health(): Promise<{
361
+ ok: boolean;
362
+ driver: string;
363
+ }>;
364
+ /** Daemon info: active/available drivers, default image, ports, providers, cost rates. */
365
+ info(): Promise<DaemonInfo>;
366
+ /** Host capacity + admission status (committed vs budget memory, how many more fit). */
367
+ capacity(): Promise<CapacitySnapshot>;
368
+ /** List all workspace backups across sandboxes. */
369
+ listBackups(): Promise<BackupInfo[]>;
370
+ /** Delete a backup by id. */
371
+ deleteBackup(backupId: string): Promise<void>;
372
+ /** @internal */
373
+ request<T>(method: string, path: string, body?: unknown): Promise<T>;
374
+ }
375
+ export declare class Sandbox {
376
+ private readonly client;
377
+ private info;
378
+ constructor(client: HotcellClient, info: SandboxInfo);
379
+ get id(): string;
380
+ /** Current lifecycle status (`running`, `paused`, or `stopped`). */
381
+ get status(): SandboxStatus;
382
+ /** The sandbox metadata captured when this handle was created/attached. */
383
+ getInfo(): SandboxInfo;
384
+ /** Run a command to completion, returning aggregated output. */
385
+ exec(command: string, options?: ExecOptions): Promise<ExecResult>;
386
+ /** Run a command, yielding output events as they stream in. */
387
+ execStream(command: string, options?: ExecOptions): AsyncGenerator<ExecEvent>;
388
+ /**
389
+ * Stop the sandbox, freeing compute. With persistence the workspace volume is
390
+ * kept so `start` resumes with files intact; background processes do not
391
+ * survive. A no-op if already stopped.
392
+ */
393
+ stop(): Promise<void>;
394
+ /** Restart a stopped sandbox, reattaching its workspace. A no-op if running. */
395
+ start(): Promise<void>;
396
+ /**
397
+ * Pause the sandbox, freeing its compute; any later operation transparently
398
+ * resumes it. On microVM drivers (firecracker/applevz) this saves a full
399
+ * memory snapshot — background processes come back **alive** on resume, in
400
+ * ~tens of milliseconds instead of a boot. On the container driver it
401
+ * degrades to stop-with-volume (workspace survives, processes don't).
402
+ */
403
+ pause(): Promise<void>;
404
+ /**
405
+ * Fetch live resource stats, cumulative usage, and cost. `live` is null when
406
+ * the sandbox isn't running. Reading metrics does not count as activity, so it
407
+ * won't keep an idle sandbox from auto-pausing.
408
+ */
409
+ metrics(): Promise<SandboxMetrics>;
410
+ /** Recent live-metrics samples (oldest→newest) for sparklines/history charts. */
411
+ metricsHistory(): Promise<MetricSample[]>;
412
+ /** Snapshot `/workspace` to a durable backup; returns its metadata. */
413
+ createBackup(): Promise<BackupInfo>;
414
+ /** Replace `/workspace` with the contents of a backup (taken from any sandbox). */
415
+ restoreBackup(backupId: string): Promise<void>;
416
+ /** List the backups taken from this sandbox. */
417
+ listBackups(): Promise<BackupInfo[]>;
418
+ /**
419
+ * Create a persistent code-interpreter context. Variables and imports persist
420
+ * across `runCode` calls made against the returned context (Jupyter-style).
421
+ */
422
+ createCodeContext(options?: {
423
+ language?: CodeLanguage;
424
+ }): Promise<CodeContext>;
425
+ /** List the open code-interpreter contexts in this sandbox. */
426
+ listCodeContexts(): Promise<CodeContextInfo[]>;
427
+ /**
428
+ * Run a code cell and return its captured output. Pass a `context` to keep
429
+ * state across calls, or omit it for a one-off run in a throwaway kernel.
430
+ */
431
+ runCode(code: string, options?: {
432
+ context?: CodeContext;
433
+ language?: CodeLanguage;
434
+ timeoutMs?: number;
435
+ }): Promise<CodeResult>;
436
+ /**
437
+ * Mint an egress token for this sandbox. Configure the sandbox's LLM SDK with
438
+ * the returned `providers[].baseUrl` and use the token in place of the real API
439
+ * key — the daemon injects the real provider key and meters the call, so keys
440
+ * never live inside the sandbox.
441
+ */
442
+ createEgressToken(policy?: EgressPolicy): Promise<EgressToken>;
443
+ /** List this sandbox's egress tokens (with policy + spend) and provider routes. */
444
+ listEgressTokens(): Promise<{
445
+ tokens: EgressTokenInfo[];
446
+ providers: EgressProvider[];
447
+ }>;
448
+ /** Revoke a previously minted egress token. */
449
+ revokeEgressToken(token: string): Promise<void>;
450
+ /** Permanently destroy the sandbox, including its persistent volume. */
451
+ destroy(): Promise<void>;
452
+ /** Write a UTF-8 file inside the sandbox. */
453
+ writeFile(path: string, content: string, options?: WriteFileOptions): Promise<void>;
454
+ /** Read a UTF-8 file from the sandbox. */
455
+ readFile(path: string): Promise<string>;
456
+ /** Create a directory inside the sandbox. */
457
+ mkdir(path: string, options?: MkdirOptions): Promise<void>;
458
+ /** List files and directories at the given path. */
459
+ listFiles(path: string): Promise<FileInfo[]>;
460
+ /**
461
+ * Watch a path (recursively) for file changes, yielding events until the
462
+ * returned generator is closed (e.g. `break` out of the loop).
463
+ */
464
+ watch(path?: string, options?: {
465
+ intervalMs?: number;
466
+ }): AsyncGenerator<FileChangeEvent>;
467
+ /** Launch a long-running background process; returns immediately. */
468
+ startProcess(command: string, options?: StartProcessOptions): Promise<ProcessHandle>;
469
+ /** List background processes started in this sandbox. */
470
+ listProcesses(): Promise<ProcessHandle[]>;
471
+ /** Signal a background process (default SIGTERM). */
472
+ killProcess(procId: string, signal?: string): Promise<void>;
473
+ /** Stream a process's logs; `follow` tails until the connection closes. */
474
+ streamLogs(procId: string, options?: {
475
+ follow?: boolean;
476
+ }): AsyncGenerator<string>;
477
+ /** Block until a TCP port is listening inside the sandbox, or timeout. */
478
+ waitForPort(port: number, options?: WaitForPortOptions): Promise<boolean>;
479
+ /** Expose an in-container port and return its preview URL. */
480
+ exposePort(port: number, options?: {
481
+ token?: string;
482
+ }): Promise<ExposedPort>;
483
+ /** Remove a previously exposed port. */
484
+ unexposePort(port: number): Promise<void>;
485
+ /** List the ports currently exposed for this sandbox. */
486
+ listExposedPorts(): Promise<ExposedPort[]>;
487
+ /** Merge environment variables applied to every subsequent command. */
488
+ setEnvVars(env: Record<string, string>): Promise<Record<string, string>>;
489
+ /** Read the sandbox-level environment variables. */
490
+ getEnvVars(): Promise<Record<string, string>>;
491
+ /** Create a persistent session; commands run in it share cwd + env. */
492
+ createSession(options?: CreateSessionOptions): Promise<Session>;
493
+ /** List the sessions open in this sandbox. */
494
+ listSessions(): Promise<SessionInfo[]>;
495
+ /** @internal — used by Session to reach the env/session endpoints. */
496
+ get _client(): HotcellClient;
497
+ }
498
+ /**
499
+ * A persistent execution context inside a sandbox. `exec` runs in the session's
500
+ * working directory with its env overlay, and a `cd` persists to later commands.
501
+ */
502
+ export declare class Session {
503
+ private readonly sandbox;
504
+ private readonly info;
505
+ constructor(sandbox: Sandbox, info: SessionInfo);
506
+ get sessionId(): string;
507
+ /** Run a command to completion within this session. */
508
+ exec(command: string, options?: ExecOptions): Promise<ExecResult>;
509
+ /** Run a command, yielding output events as they stream in. */
510
+ execStream(command: string, options?: ExecOptions): AsyncGenerator<ExecEvent>;
511
+ /** Merge environment variables applied to subsequent commands in this session. */
512
+ setEnvVars(env: Record<string, string>): Promise<SessionInfo>;
513
+ /** Delete this session (the sandbox and its files are untouched). */
514
+ destroy(): Promise<void>;
515
+ }
516
+ /**
517
+ * A persistent code-interpreter context. `runCode` executes in a kernel that
518
+ * keeps its namespace across calls, so variables and imports persist.
519
+ */
520
+ export declare class CodeContext {
521
+ private readonly sandbox;
522
+ private readonly info;
523
+ constructor(sandbox: Sandbox, info: CodeContextInfo);
524
+ get contextId(): string;
525
+ get language(): CodeLanguage;
526
+ /** Run code in this context, sharing state with previous runs. */
527
+ runCode(code: string, options?: {
528
+ timeoutMs?: number;
529
+ }): Promise<CodeResult>;
530
+ /** Destroy this context and its kernel. */
531
+ destroy(): Promise<void>;
532
+ }
533
+ /** Convenience matching the Cloudflare `getSandbox(binding, id)` shape. */
534
+ export declare function getSandbox(client: HotcellClient, id?: string, options?: CreateOptions): Promise<Sandbox>;
package/dist/index.js ADDED
@@ -0,0 +1,430 @@
1
+ /**
2
+ * @hotcell/sdk — TypeScript client for the hotcell daemon.
3
+ *
4
+ * Mirrors the Cloudflare Sandbox SDK surface so existing harnesses port with
5
+ * minimal changes, but points at *your* self-hosted daemon instead of the edge.
6
+ *
7
+ * const client = new HotcellClient({ endpoint: "http://127.0.0.1:4750" });
8
+ * const sandbox = await client.getSandbox();
9
+ * const { stdout } = await sandbox.exec("python3 -c 'print(2+2)'");
10
+ * await sandbox.destroy();
11
+ */
12
+ export class HotcellClient {
13
+ endpoint;
14
+ apiKey;
15
+ constructor(opts = {}) {
16
+ this.endpoint = (opts.endpoint ?? defaultEndpoint()).replace(/\/$/, "");
17
+ this.apiKey = opts.apiKey ?? defaultApiKey();
18
+ }
19
+ /** Build request headers, attaching the API key and any extras. */
20
+ authHeaders(extra) {
21
+ const headers = { ...extra };
22
+ if (this.apiKey)
23
+ headers["authorization"] = `Bearer ${this.apiKey}`;
24
+ return headers;
25
+ }
26
+ /** Create a fresh sandbox (Cloudflare-style: omit id to provision a new one). */
27
+ async getSandbox(id, options) {
28
+ if (id) {
29
+ // Attach to an existing sandbox by id.
30
+ const info = await this.request("GET", `/sandboxes/${id}`);
31
+ return new Sandbox(this, info);
32
+ }
33
+ const info = await this.request("POST", "/sandboxes", options ?? {});
34
+ return new Sandbox(this, info);
35
+ }
36
+ /** List all sandboxes managed by the daemon. */
37
+ async list() {
38
+ const { sandboxes } = await this.request("GET", "/sandboxes");
39
+ return sandboxes;
40
+ }
41
+ /** Daemon health + active runtime driver. */
42
+ async health() {
43
+ return this.request("GET", "/healthz");
44
+ }
45
+ /** Daemon info: active/available drivers, default image, ports, providers, cost rates. */
46
+ async info() {
47
+ return this.request("GET", "/info");
48
+ }
49
+ /** Host capacity + admission status (committed vs budget memory, how many more fit). */
50
+ async capacity() {
51
+ return this.request("GET", "/capacity");
52
+ }
53
+ /** List all workspace backups across sandboxes. */
54
+ async listBackups() {
55
+ const { backups } = await this.request("GET", "/backups");
56
+ return backups;
57
+ }
58
+ /** Delete a backup by id. */
59
+ async deleteBackup(backupId) {
60
+ await this.request("DELETE", `/backups/${backupId}`);
61
+ }
62
+ /** @internal */
63
+ async request(method, path, body) {
64
+ const res = await fetch(this.endpoint + path, {
65
+ method,
66
+ headers: this.authHeaders(body ? { "content-type": "application/json" } : undefined),
67
+ body: body ? JSON.stringify(body) : undefined,
68
+ });
69
+ if (!res.ok) {
70
+ const text = await res.text();
71
+ throw new Error(`hotcell ${method} ${path} -> ${res.status}: ${text}`);
72
+ }
73
+ return (await res.json());
74
+ }
75
+ }
76
+ export class Sandbox {
77
+ client;
78
+ info;
79
+ constructor(client, info) {
80
+ this.client = client;
81
+ this.info = info;
82
+ }
83
+ get id() {
84
+ return this.info.id;
85
+ }
86
+ /** Current lifecycle status (`running`, `paused`, or `stopped`). */
87
+ get status() {
88
+ return this.info.status;
89
+ }
90
+ /** The sandbox metadata captured when this handle was created/attached. */
91
+ getInfo() {
92
+ return this.info;
93
+ }
94
+ /** Run a command to completion, returning aggregated output. */
95
+ async exec(command, options = {}) {
96
+ let stdout = "";
97
+ let stderr = "";
98
+ let exitCode = 0;
99
+ for await (const event of this.execStream(command, options)) {
100
+ if (event.type === "stdout")
101
+ stdout += event.data;
102
+ else if (event.type === "stderr")
103
+ stderr += event.data;
104
+ else
105
+ exitCode = event.exitCode;
106
+ }
107
+ return { stdout, stderr, exitCode, success: exitCode === 0 };
108
+ }
109
+ /** Run a command, yielding output events as they stream in. */
110
+ async *execStream(command, options = {}) {
111
+ const res = await fetch(`${this.client.endpoint}/sandboxes/${this.info.id}/exec`, {
112
+ method: "POST",
113
+ headers: this.client.authHeaders({ "content-type": "application/json" }),
114
+ body: JSON.stringify({
115
+ command,
116
+ cwd: options.cwd,
117
+ env: options.env,
118
+ sessionId: options.sessionId,
119
+ }),
120
+ });
121
+ if (!res.ok || !res.body) {
122
+ const text = await res.text().catch(() => "");
123
+ throw new Error(`hotcell exec -> ${res.status}: ${text}`);
124
+ }
125
+ for await (const event of parseSSE(res.body)) {
126
+ if (options.onOutput &&
127
+ (event.type === "stdout" || event.type === "stderr")) {
128
+ options.onOutput(event.type, event.data);
129
+ }
130
+ yield event;
131
+ }
132
+ }
133
+ /**
134
+ * Stop the sandbox, freeing compute. With persistence the workspace volume is
135
+ * kept so `start` resumes with files intact; background processes do not
136
+ * survive. A no-op if already stopped.
137
+ */
138
+ async stop() {
139
+ this.info = await this.client.request("POST", `/sandboxes/${this.info.id}/stop`);
140
+ }
141
+ /** Restart a stopped sandbox, reattaching its workspace. A no-op if running. */
142
+ async start() {
143
+ this.info = await this.client.request("POST", `/sandboxes/${this.info.id}/start`);
144
+ }
145
+ /**
146
+ * Pause the sandbox, freeing its compute; any later operation transparently
147
+ * resumes it. On microVM drivers (firecracker/applevz) this saves a full
148
+ * memory snapshot — background processes come back **alive** on resume, in
149
+ * ~tens of milliseconds instead of a boot. On the container driver it
150
+ * degrades to stop-with-volume (workspace survives, processes don't).
151
+ */
152
+ async pause() {
153
+ this.info = await this.client.request("POST", `/sandboxes/${this.info.id}/pause`);
154
+ }
155
+ /**
156
+ * Fetch live resource stats, cumulative usage, and cost. `live` is null when
157
+ * the sandbox isn't running. Reading metrics does not count as activity, so it
158
+ * won't keep an idle sandbox from auto-pausing.
159
+ */
160
+ async metrics() {
161
+ return this.client.request("GET", `/sandboxes/${this.info.id}/metrics`);
162
+ }
163
+ /** Recent live-metrics samples (oldest→newest) for sparklines/history charts. */
164
+ async metricsHistory() {
165
+ const { samples } = await this.client.request("GET", `/sandboxes/${this.info.id}/metrics/history`);
166
+ return samples;
167
+ }
168
+ /** Snapshot `/workspace` to a durable backup; returns its metadata. */
169
+ async createBackup() {
170
+ return this.client.request("POST", `/sandboxes/${this.info.id}/backups`);
171
+ }
172
+ /** Replace `/workspace` with the contents of a backup (taken from any sandbox). */
173
+ async restoreBackup(backupId) {
174
+ await this.client.request("POST", `/sandboxes/${this.info.id}/restore`, { backupId });
175
+ }
176
+ /** List the backups taken from this sandbox. */
177
+ async listBackups() {
178
+ const { backups } = await this.client.request("GET", `/sandboxes/${this.info.id}/backups`);
179
+ return backups;
180
+ }
181
+ /**
182
+ * Create a persistent code-interpreter context. Variables and imports persist
183
+ * across `runCode` calls made against the returned context (Jupyter-style).
184
+ */
185
+ async createCodeContext(options = {}) {
186
+ const info = await this.client.request("POST", `/sandboxes/${this.info.id}/code-contexts`, { language: options.language ?? "python" });
187
+ return new CodeContext(this, info);
188
+ }
189
+ /** List the open code-interpreter contexts in this sandbox. */
190
+ async listCodeContexts() {
191
+ const { contexts } = await this.client.request("GET", `/sandboxes/${this.info.id}/code-contexts`);
192
+ return contexts;
193
+ }
194
+ /**
195
+ * Run a code cell and return its captured output. Pass a `context` to keep
196
+ * state across calls, or omit it for a one-off run in a throwaway kernel.
197
+ */
198
+ async runCode(code, options = {}) {
199
+ return this.client.request("POST", `/sandboxes/${this.info.id}/run-code`, {
200
+ code,
201
+ contextId: options.context?.contextId,
202
+ language: options.language,
203
+ timeoutMs: options.timeoutMs,
204
+ });
205
+ }
206
+ /**
207
+ * Mint an egress token for this sandbox. Configure the sandbox's LLM SDK with
208
+ * the returned `providers[].baseUrl` and use the token in place of the real API
209
+ * key — the daemon injects the real provider key and meters the call, so keys
210
+ * never live inside the sandbox.
211
+ */
212
+ async createEgressToken(policy) {
213
+ return this.client.request("POST", `/sandboxes/${this.info.id}/egress-tokens`, policy ?? {});
214
+ }
215
+ /** List this sandbox's egress tokens (with policy + spend) and provider routes. */
216
+ async listEgressTokens() {
217
+ return this.client.request("GET", `/sandboxes/${this.info.id}/egress-tokens`);
218
+ }
219
+ /** Revoke a previously minted egress token. */
220
+ async revokeEgressToken(token) {
221
+ await this.client.request("DELETE", `/sandboxes/${this.info.id}/egress-tokens/${token}`);
222
+ }
223
+ /** Permanently destroy the sandbox, including its persistent volume. */
224
+ async destroy() {
225
+ await this.client.request("DELETE", `/sandboxes/${this.info.id}`);
226
+ }
227
+ /** Write a UTF-8 file inside the sandbox. */
228
+ async writeFile(path, content, options = {}) {
229
+ await this.client.request("POST", `/sandboxes/${this.info.id}/files/write`, { path, content, mode: options.mode });
230
+ }
231
+ /** Read a UTF-8 file from the sandbox. */
232
+ async readFile(path) {
233
+ const { content } = await this.client.request("POST", `/sandboxes/${this.info.id}/files/read`, { path });
234
+ return content;
235
+ }
236
+ /** Create a directory inside the sandbox. */
237
+ async mkdir(path, options = {}) {
238
+ await this.client.request("POST", `/sandboxes/${this.info.id}/files/mkdir`, { path, parents: options.parents });
239
+ }
240
+ /** List files and directories at the given path. */
241
+ async listFiles(path) {
242
+ const { entries } = await this.client.request("POST", `/sandboxes/${this.info.id}/files/list`, { path });
243
+ return entries;
244
+ }
245
+ /**
246
+ * Watch a path (recursively) for file changes, yielding events until the
247
+ * returned generator is closed (e.g. `break` out of the loop).
248
+ */
249
+ async *watch(path = "/workspace", options = {}) {
250
+ const params = new URLSearchParams({ path });
251
+ if (options.intervalMs)
252
+ params.set("interval", String(options.intervalMs));
253
+ const res = await fetch(`${this.client.endpoint}/sandboxes/${this.info.id}/watch?${params}`, { headers: this.client.authHeaders() });
254
+ if (!res.ok || !res.body) {
255
+ const text = await res.text().catch(() => "");
256
+ throw new Error(`sbx watch -> ${res.status}: ${text}`);
257
+ }
258
+ for await (const event of parseSSE(res.body)) {
259
+ yield event;
260
+ }
261
+ }
262
+ /** Launch a long-running background process; returns immediately. */
263
+ async startProcess(command, options = {}) {
264
+ return this.client.request("POST", `/sandboxes/${this.info.id}/processes`, { command, cwd: options.cwd, env: options.env });
265
+ }
266
+ /** List background processes started in this sandbox. */
267
+ async listProcesses() {
268
+ const { processes } = await this.client.request("GET", `/sandboxes/${this.info.id}/processes`);
269
+ return processes;
270
+ }
271
+ /** Signal a background process (default SIGTERM). */
272
+ async killProcess(procId, signal) {
273
+ await this.client.request("DELETE", `/sandboxes/${this.info.id}/processes/${procId}`, signal ? { signal } : undefined);
274
+ }
275
+ /** Stream a process's logs; `follow` tails until the connection closes. */
276
+ async *streamLogs(procId, options = {}) {
277
+ const follow = options.follow ? "1" : "0";
278
+ const res = await fetch(`${this.client.endpoint}/sandboxes/${this.info.id}/processes/${procId}/logs?follow=${follow}`, { headers: this.client.authHeaders() });
279
+ if (!res.ok || !res.body) {
280
+ const text = await res.text().catch(() => "");
281
+ throw new Error(`sbx logs -> ${res.status}: ${text}`);
282
+ }
283
+ for await (const event of parseSSE(res.body)) {
284
+ if (event.type === "log")
285
+ yield event.data;
286
+ else if (event.type === "end")
287
+ return;
288
+ }
289
+ }
290
+ /** Block until a TCP port is listening inside the sandbox, or timeout. */
291
+ async waitForPort(port, options = {}) {
292
+ const { ready } = await this.client.request("POST", `/sandboxes/${this.info.id}/wait-port`, { port, ...options });
293
+ return ready;
294
+ }
295
+ /** Expose an in-container port and return its preview URL. */
296
+ async exposePort(port, options = {}) {
297
+ return this.client.request("POST", `/sandboxes/${this.info.id}/expose`, { port, token: options.token });
298
+ }
299
+ /** Remove a previously exposed port. */
300
+ async unexposePort(port) {
301
+ await this.client.request("DELETE", `/sandboxes/${this.info.id}/expose/${port}`);
302
+ }
303
+ /** List the ports currently exposed for this sandbox. */
304
+ async listExposedPorts() {
305
+ const { exposed } = await this.client.request("GET", `/sandboxes/${this.info.id}/expose`);
306
+ return exposed;
307
+ }
308
+ /** Merge environment variables applied to every subsequent command. */
309
+ async setEnvVars(env) {
310
+ const res = await this.client.request("POST", `/sandboxes/${this.info.id}/env`, { env });
311
+ return res.env;
312
+ }
313
+ /** Read the sandbox-level environment variables. */
314
+ async getEnvVars() {
315
+ const res = await this.client.request("GET", `/sandboxes/${this.info.id}/env`);
316
+ return res.env;
317
+ }
318
+ /** Create a persistent session; commands run in it share cwd + env. */
319
+ async createSession(options = {}) {
320
+ const info = await this.client.request("POST", `/sandboxes/${this.info.id}/sessions`, { id: options.id, cwd: options.cwd, env: options.env });
321
+ return new Session(this, info);
322
+ }
323
+ /** List the sessions open in this sandbox. */
324
+ async listSessions() {
325
+ const { sessions } = await this.client.request("GET", `/sandboxes/${this.info.id}/sessions`);
326
+ return sessions;
327
+ }
328
+ /** @internal — used by Session to reach the env/session endpoints. */
329
+ get _client() {
330
+ return this.client;
331
+ }
332
+ }
333
+ /**
334
+ * A persistent execution context inside a sandbox. `exec` runs in the session's
335
+ * working directory with its env overlay, and a `cd` persists to later commands.
336
+ */
337
+ export class Session {
338
+ sandbox;
339
+ info;
340
+ constructor(sandbox, info) {
341
+ this.sandbox = sandbox;
342
+ this.info = info;
343
+ }
344
+ get sessionId() {
345
+ return this.info.sessionId;
346
+ }
347
+ /** Run a command to completion within this session. */
348
+ exec(command, options = {}) {
349
+ return this.sandbox.exec(command, { ...options, sessionId: this.sessionId });
350
+ }
351
+ /** Run a command, yielding output events as they stream in. */
352
+ execStream(command, options = {}) {
353
+ return this.sandbox.execStream(command, {
354
+ ...options,
355
+ sessionId: this.sessionId,
356
+ });
357
+ }
358
+ /** Merge environment variables applied to subsequent commands in this session. */
359
+ async setEnvVars(env) {
360
+ return this.sandbox._client.request("POST", `/sandboxes/${this.sandbox.id}/sessions/${this.sessionId}/env`, { env });
361
+ }
362
+ /** Delete this session (the sandbox and its files are untouched). */
363
+ async destroy() {
364
+ await this.sandbox._client.request("DELETE", `/sandboxes/${this.sandbox.id}/sessions/${this.sessionId}`);
365
+ }
366
+ }
367
+ /**
368
+ * A persistent code-interpreter context. `runCode` executes in a kernel that
369
+ * keeps its namespace across calls, so variables and imports persist.
370
+ */
371
+ export class CodeContext {
372
+ sandbox;
373
+ info;
374
+ constructor(sandbox, info) {
375
+ this.sandbox = sandbox;
376
+ this.info = info;
377
+ }
378
+ get contextId() {
379
+ return this.info.contextId;
380
+ }
381
+ get language() {
382
+ return this.info.language;
383
+ }
384
+ /** Run code in this context, sharing state with previous runs. */
385
+ runCode(code, options = {}) {
386
+ return this.sandbox.runCode(code, { context: this, timeoutMs: options.timeoutMs });
387
+ }
388
+ /** Destroy this context and its kernel. */
389
+ async destroy() {
390
+ await this.sandbox._client.request("DELETE", `/sandboxes/${this.sandbox.id}/code-contexts/${this.info.contextId}`);
391
+ }
392
+ }
393
+ /** Convenience matching the Cloudflare `getSandbox(binding, id)` shape. */
394
+ export function getSandbox(client, id, options) {
395
+ return client.getSandbox(id, options);
396
+ }
397
+ // --- internals -------------------------------------------------------------
398
+ function defaultEndpoint() {
399
+ return envVar("HOTCELL_ENDPOINT") ?? envVar("SBX_ENDPOINT") ?? "http://127.0.0.1:4750";
400
+ }
401
+ function defaultApiKey() {
402
+ return envVar("HOTCELL_API_KEY") ?? envVar("SBX_API_KEY") ?? "";
403
+ }
404
+ function envVar(name) {
405
+ return globalThis.process?.env?.[name];
406
+ }
407
+ /** Parse a `text/event-stream` body into typed JSON events. */
408
+ async function* parseSSE(body) {
409
+ const reader = body.getReader();
410
+ const decoder = new TextDecoder();
411
+ let buffer = "";
412
+ for (;;) {
413
+ const { done, value } = await reader.read();
414
+ if (done)
415
+ break;
416
+ buffer += decoder.decode(value, { stream: true });
417
+ let idx;
418
+ while ((idx = buffer.indexOf("\n\n")) !== -1) {
419
+ const frame = buffer.slice(0, idx);
420
+ buffer = buffer.slice(idx + 2);
421
+ const line = frame.split("\n").find((l) => l.startsWith("data:"));
422
+ if (!line)
423
+ continue;
424
+ const json = line.slice(5).trim();
425
+ if (json)
426
+ yield JSON.parse(json);
427
+ }
428
+ }
429
+ }
430
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@hotcell/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Zero-dependency TypeScript client for the hotcell sandbox daemon",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "keywords": [
20
+ "sandbox",
21
+ "ai-agents",
22
+ "firecracker",
23
+ "microvm",
24
+ "apple-virtualization",
25
+ "code-execution",
26
+ "llm-gateway",
27
+ "self-hosted"
28
+ ],
29
+ "homepage": "https://hotcell-bw3.pages.dev",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/sinameraji/hotcell.git"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^20.14.0",
42
+ "typescript": "^5.5.0"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/sinameraji/hotcell/issues"
46
+ },
47
+ "engines": {
48
+ "node": ">=18"
49
+ }
50
+ }