@watasu/sdk 0.1.25 → 0.1.40

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
@@ -16,7 +16,7 @@ Set `WATASU_API_KEY` before using the SDK.
16
16
  import { Sandbox } from '@watasu/sdk'
17
17
 
18
18
  const sbx = await Sandbox.create()
19
- await sbx.filesystem.write('/home/user/a.js', 'console.log(2 + 2)')
19
+ await sbx.files.write('/home/user/a.js', 'console.log(2 + 2)')
20
20
  const result = await sbx.process.startAndWait('node /home/user/a.js')
21
21
  console.log(result.stdout)
22
22
  console.log(await sbx.isRunning())
@@ -31,23 +31,63 @@ await sbx.betaPause()
31
31
  await sbx.resume({ timeoutMs: 300_000 })
32
32
  ```
33
33
 
34
+ Choose what happens when the sandbox timeout expires:
35
+
36
+ ```ts
37
+ const sbx = await Sandbox.create({
38
+ lifecycle: { onTimeout: 'pause', autoResume: true },
39
+ })
40
+ ```
41
+
42
+ `onTimeout: 'kill'` is the default. `onTimeout: 'pause'` keeps the retained
43
+ disk after timeout; `autoResume` lets a later data-plane request resume that
44
+ paused sandbox automatically.
45
+
46
+ Mount a named persistent volume when the sandbox starts:
47
+
48
+ ```ts
49
+ const sbx = await Sandbox.create({
50
+ volumeMounts: {
51
+ '/workspace/cache': 'cache',
52
+ '/data/models': { name: 'models' },
53
+ },
54
+ })
55
+ ```
56
+
57
+ Create and edit a persistent volume while it is detached:
58
+
59
+ ```ts
60
+ import { Volume } from '@watasu/sdk'
61
+
62
+ const volume = await Volume.create('cache')
63
+ await volume.makeDir('/workspace')
64
+ await volume.writeFile('/workspace/status.txt', 'ready\n', { mode: '0644' })
65
+ console.log(await volume.readFile('/workspace/status.txt'))
66
+ console.log((await volume.list('/workspace')).map((entry) => entry.path))
67
+ await volume.remove('/workspace/status.txt')
68
+ await volume.destroy()
69
+ ```
70
+
34
71
  ## Code Interpreter
35
72
 
36
73
  ```ts
37
74
  import { Sandbox } from '@watasu/sdk/code-interpreter'
38
75
 
39
76
  const sbx = await Sandbox.create()
77
+ const context = await sbx.createCodeContext()
40
78
  const execution = await sbx.runCode("print('hello')\n2 + 3", {
41
- language: 'python',
79
+ context,
42
80
  onStdout: (message) => console.log(message.line),
43
81
  })
44
82
 
45
83
  console.log(execution.text)
84
+ await sbx.removeCodeContext(context)
46
85
  await sbx.kill()
47
86
  ```
48
87
 
49
- `@watasu/sdk/code-interpreter` starts the `code-interpreter` template by default
50
- and returns structured `results`, `logs`, and `error` fields for each execution.
88
+ `@watasu/sdk/code-interpreter` starts the `code-interpreter` template by default.
89
+ Code runs in persistent Python contexts and returns structured `results`, `logs`,
90
+ and `error` fields for each execution.
51
91
 
52
92
  ## MCP Gateway
53
93
 
@@ -112,12 +152,12 @@ const remoteUrl = await sbx.git.remoteGet('/workspace/project', 'origin')
112
152
  await sbx.git.restore('/workspace/project', { paths: ['README.md'] })
113
153
  await sbx.git.reset('/workspace/project', { mode: 'hard', target: 'HEAD' })
114
154
 
115
- await sbx.filesystem.writeFiles([
155
+ await sbx.files.writeFiles([
116
156
  { path: '/workspace/project/a.txt', data: 'alpha' },
117
157
  { path: '/workspace/project/b.bin', data: new Uint8Array([0, 1, 2]) },
118
158
  ])
119
159
 
120
- const watcher = sbx.filesystem.watchDir('/workspace/project')
160
+ const watcher = sbx.files.watchDir('/workspace/project')
121
161
  watcher.addEventListener((event) => {
122
162
  console.log(event.type, event.path)
123
163
  })
@@ -191,7 +231,10 @@ instructions into Watasu's package-spec builder.
191
231
  import { Sandbox } from '@watasu/sdk'
192
232
 
193
233
  const sbx = await Sandbox.create()
194
- const metrics = await sbx.getMetrics()
234
+ const metrics = await sbx.getMetrics({
235
+ start: new Date(Date.now() - 5 * 60_000),
236
+ end: new Date(),
237
+ })
195
238
  const snapshot = await sbx.createSnapshot({ name: 'ready' })
196
239
  const snapshots = await sbx.listSnapshots().nextItems()
197
240
  const allSnapshots = await Sandbox.listSnapshots({ limit: 100 }).nextItems()
@@ -1,20 +1,43 @@
1
1
  import { Sandbox as BaseSandbox, SandboxConnectOpts, SandboxCreateOpts } from './sandbox.js';
2
+ export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
3
+ export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
4
+ export { SandboxPaginator, SnapshotPaginator } from './sandbox.js';
5
+ export type { CreateSnapshotOpts, FileUrlInfo, McpServer, McpServerName, RestoreSnapshotOpts, SandboxConnectOpts, SandboxCreateOpts, SandboxInfo, SandboxInfoLifecycle, SandboxLifecycle, SandboxListOpts, SandboxMetrics, SandboxMetricsOpts, SandboxNetworkSelector, SandboxNetworkUpdate, SandboxNetworkUpdateOpts, SandboxUrlOpts, SnapshotInfo, } from './sandbox.js';
6
+ export { CommandExitError, CommandHandle, Commands } from './commands.js';
7
+ export type { CommandResult, CommandStartOpts, ProcessInfo } from './commands.js';
8
+ export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
9
+ export type { ProcessOpts } from './process.js';
10
+ export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
11
+ export type { EntryInfo, FilesystemEvent, FilesystemReadOpts, FilesystemRequestOpts, FilesystemWriteOpts, WatchOpts, WriteData, WriteEntry, WriteInfo, } from './filesystem.js';
12
+ export { Git } from './git.js';
13
+ export type { GitAddOpts, GitAuthOpts, GitBranches, GitBranchOpts, GitCloneOpts, GitCommandResult, GitCommitOpts, GitConfigOpts, GitConfigureUserOpts, GitCredentialOpts, GitFileStatus, GitInitOpts, GitPullOpts, GitPushOpts, GitRemoteAddOpts, GitResetMode, GitResetOpts, GitRestoreOpts, GitStatus, } from './git.js';
14
+ export { Pty } from './pty.js';
15
+ export type { PtyConnectOpts, PtyCreateOpts, PtySize } from './pty.js';
16
+ export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
17
+ export type { TerminalOpts } from './terminal.js';
18
+ export { Volume } from './volume.js';
19
+ export type { VolumeApiParams, VolumeConnectionConfig, VolumeEntryStat, VolumeFileType, VolumeInfo, VolumeListFilesOpts, VolumeListOpts, VolumeMetadataOpts, VolumeReadFileOpts, VolumeReadFormat, VolumeWriteData, VolumeWriteFileOpts, } from './volume.js';
20
+ export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
21
+ export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, } from './template.js';
22
+ export type { BuildInfo, BuildOptions, BuildStatusReason, CopyItem, GetBuildStatusOptions, LogEntry, ReadyCommand, TemplateBuilder, TemplateBuildStatus, TemplateBuildStatusResponse, TemplateClass, TemplateFactory, TemplateFinal, TemplateFromImage, TemplateOptions, TemplateTag, TemplateTagInfo, } from './template.js';
2
23
  export type RunCodeLanguage = 'python' | 'python3' | string;
3
24
  export interface RunCodeOpts {
4
25
  language?: RunCodeLanguage;
5
- context?: Context;
26
+ context?: Context | string;
6
27
  onStdout?: (message: OutputMessage) => void;
7
28
  onStderr?: (message: OutputMessage) => void;
8
29
  onResult?: (result: Result) => void;
9
30
  onError?: (error: ExecutionError) => void;
10
31
  envs?: Record<string, string>;
11
- timeout?: number;
32
+ timeoutMs?: number;
12
33
  requestTimeoutMs?: number;
34
+ signal?: AbortSignal;
13
35
  }
14
36
  export interface CreateCodeContextOpts {
15
37
  cwd?: string;
16
38
  language?: RunCodeLanguage;
17
39
  requestTimeoutMs?: number;
40
+ signal?: AbortSignal;
18
41
  }
19
42
  /** One stdout or stderr line emitted by code execution. */
20
43
  export declare class OutputMessage {
@@ -86,15 +109,18 @@ export declare class Sandbox extends BaseSandbox {
86
109
  /** Create a persistent code context. */
87
110
  createCodeContext(_opts?: CreateCodeContextOpts): Promise<Context>;
88
111
  /** Remove a persistent code context. */
89
- removeCodeContext(_context: Context, _opts?: {
112
+ removeCodeContext(context: Context | string, opts?: {
90
113
  requestTimeoutMs?: number;
91
- }): Promise<boolean>;
114
+ signal?: AbortSignal;
115
+ }): Promise<void>;
92
116
  /** List persistent code contexts. */
93
- listCodeContexts(_opts?: {
117
+ listCodeContexts(opts?: {
94
118
  requestTimeoutMs?: number;
119
+ signal?: AbortSignal;
95
120
  }): Promise<Context[]>;
96
121
  /** Restart a persistent code context. */
97
- restartCodeContext(_context: Context, _opts?: {
122
+ restartCodeContext(context: Context | string, opts?: {
98
123
  requestTimeoutMs?: number;
99
- }): Promise<Context>;
124
+ signal?: AbortSignal;
125
+ }): Promise<void>;
100
126
  }
@@ -1,5 +1,17 @@
1
- import { InvalidArgumentError, NotImplementedError } from './errors.js';
1
+ import { InvalidArgumentError } from './errors.js';
2
2
  import { Sandbox as BaseSandbox } from './sandbox.js';
3
+ export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
4
+ export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
5
+ export { SandboxPaginator, SnapshotPaginator } from './sandbox.js';
6
+ export { CommandExitError, CommandHandle, Commands } from './commands.js';
7
+ export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
8
+ export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
9
+ export { Git } from './git.js';
10
+ export { Pty } from './pty.js';
11
+ export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
12
+ export { Volume } from './volume.js';
13
+ export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
14
+ export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, } from './template.js';
3
15
  /** One stdout or stderr line emitted by code execution. */
4
16
  export class OutputMessage {
5
17
  line;
@@ -161,10 +173,11 @@ export class Sandbox extends BaseSandbox {
161
173
  language: opts.language,
162
174
  context_id: contextId(opts.context),
163
175
  env_vars: opts.envs,
164
- timeout_seconds: opts.timeout,
176
+ timeout_ms: opts.timeoutMs,
165
177
  });
166
178
  const response = await this.runtimePostJson('/runtime/v1/code/run', payload, {
167
179
  requestTimeoutMs: opts.requestTimeoutMs,
180
+ signal: opts.signal,
168
181
  });
169
182
  const execution = executionFromApi(response);
170
183
  emitCallbacks(execution, opts);
@@ -172,19 +185,37 @@ export class Sandbox extends BaseSandbox {
172
185
  }
173
186
  /** Create a persistent code context. */
174
187
  async createCodeContext(_opts = {}) {
175
- throw new NotImplementedError('code contexts are not supported by Watasu yet');
188
+ const response = await this.runtimePostJson('/runtime/v1/code/contexts', compactRecord({
189
+ cwd: _opts.cwd,
190
+ language: _opts.language,
191
+ }), {
192
+ requestTimeoutMs: _opts.requestTimeoutMs,
193
+ signal: _opts.signal,
194
+ });
195
+ return contextFromApi(response);
176
196
  }
177
197
  /** Remove a persistent code context. */
178
- async removeCodeContext(_context, _opts = {}) {
179
- throw new NotImplementedError('code contexts are not supported by Watasu yet');
198
+ async removeCodeContext(context, opts = {}) {
199
+ await this.runtimeDeleteJson(`/runtime/v1/code/contexts/${encodeURIComponent(requireContextId(context))}`, {
200
+ requestTimeoutMs: opts.requestTimeoutMs,
201
+ signal: opts.signal,
202
+ });
180
203
  }
181
204
  /** List persistent code contexts. */
182
- async listCodeContexts(_opts = {}) {
183
- throw new NotImplementedError('code contexts are not supported by Watasu yet');
205
+ async listCodeContexts(opts = {}) {
206
+ const response = await this.runtimeGetJson('/runtime/v1/code/contexts', {
207
+ requestTimeoutMs: opts.requestTimeoutMs,
208
+ signal: opts.signal,
209
+ });
210
+ const contexts = Array.isArray(response) ? response : arrayOfUnknown(response.contexts);
211
+ return contexts.map((item) => contextFromApi(record(item)));
184
212
  }
185
213
  /** Restart a persistent code context. */
186
- async restartCodeContext(_context, _opts = {}) {
187
- throw new NotImplementedError('code contexts are not supported by Watasu yet');
214
+ async restartCodeContext(context, opts = {}) {
215
+ await this.runtimePostJson(`/runtime/v1/code/contexts/${encodeURIComponent(requireContextId(context))}/restart`, {}, {
216
+ requestTimeoutMs: opts.requestTimeoutMs,
217
+ signal: opts.signal,
218
+ });
188
219
  }
189
220
  }
190
221
  function executionFromApi(payload) {
@@ -219,7 +250,17 @@ function emitCallbacks(execution, opts) {
219
250
  opts.onError?.(execution.error);
220
251
  }
221
252
  function contextId(context) {
222
- return context?.id;
253
+ if (context === undefined)
254
+ return undefined;
255
+ return requireContextId(context);
256
+ }
257
+ function requireContextId(context) {
258
+ if (typeof context === 'string')
259
+ return context;
260
+ return context.id;
261
+ }
262
+ function contextFromApi(payload) {
263
+ return new Context(String(payload.id ?? ''), stringValue(payload.language), stringValue(payload.cwd));
223
264
  }
224
265
  function compactRecord(payload) {
225
266
  return Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== undefined));
@@ -34,22 +34,17 @@ export interface CommandStartOpts {
34
34
  /** Arguments for `cmd` when starting a direct executable. */
35
35
  args?: string[];
36
36
  cwd?: string;
37
- /** Deprecated alias for `cwd`. */
38
- rootDir?: string;
39
37
  user?: string;
40
38
  envs?: Record<string, string>;
41
- /** Alias for `envs`. */
42
- envVars?: Record<string, string>;
43
39
  onStdout?: (data: string) => void | Promise<void>;
44
40
  onStderr?: (data: string) => void | Promise<void>;
45
41
  onPty?: (data: Uint8Array) => void | Promise<void>;
46
42
  onExit?: (exitCode: number) => void | Promise<void>;
47
43
  stdin?: boolean;
48
44
  timeoutMs?: number;
49
- /** Alias for `timeoutMs`. */
50
- timeout?: number;
51
45
  processID?: string;
52
46
  requestTimeoutMs?: number;
47
+ signal?: AbortSignal;
53
48
  }
54
49
  /** Live handle for one sandbox process stream. */
55
50
  export declare class CommandHandle implements Partial<CommandResult> {
@@ -98,18 +93,22 @@ export declare class Commands {
98
93
  /** List processes currently known by the sandbox runtime. */
99
94
  list(opts?: {
100
95
  requestTimeoutMs?: number;
96
+ signal?: AbortSignal;
101
97
  }): Promise<ProcessInfo[]>;
102
98
  /** Send SIGKILL to a process by pid. */
103
99
  kill(pid: number | string, opts?: {
104
100
  requestTimeoutMs?: number;
101
+ signal?: AbortSignal;
105
102
  }): Promise<boolean>;
106
103
  /** Attach to a process and send stdin bytes or text. */
107
104
  sendStdin(pid: number | string, data: string | Uint8Array, opts?: {
108
105
  requestTimeoutMs?: number;
106
+ signal?: AbortSignal;
109
107
  }): Promise<void>;
110
108
  /** Attach to a process and close stdin, signalling EOF. */
111
109
  closeStdin(pid: number | string, opts?: {
112
110
  requestTimeoutMs?: number;
111
+ signal?: AbortSignal;
113
112
  }): Promise<void>;
114
113
  run(cmd: string, opts: CommandStartOpts & {
115
114
  background: true;
package/dist/commands.js CHANGED
@@ -167,31 +167,31 @@ export class Commands {
167
167
  const handle = await this.start(cmd, opts);
168
168
  if (opts.background)
169
169
  return handle;
170
- return handle.wait(opts.timeoutMs ?? opts.timeout);
170
+ return handle.wait(opts.timeoutMs);
171
171
  }
172
172
  /** Reconnect to a live process stream by pid. */
173
173
  async connect(pid, opts = {}) {
174
- const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, `/runtime/v1/process/${pid}/connect?since=0`, opts.requestTimeoutMs ?? this.config.requestTimeoutMs).connect();
174
+ const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, `/runtime/v1/process/${pid}/connect?since=0`, opts.requestTimeoutMs ?? this.config.requestTimeoutMs, this.config.headers).connect();
175
175
  const first = await nextStarted(socket);
176
176
  const actualPid = framePid(first) ?? pid;
177
177
  return new CommandHandle(actualPid, socket, () => this.kill(actualPid), socket, opts.onStdout, opts.onStderr, opts.onPty);
178
178
  }
179
179
  /** Start a command and return a live handle immediately. */
180
180
  async start(cmd, opts = {}) {
181
- const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, '/runtime/v1/process', opts.requestTimeoutMs ?? this.config.requestTimeoutMs).connect();
182
- const environment = { ...this.sandboxEnvs, ...(opts.envVars ?? opts.envs ?? {}) };
181
+ const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, '/runtime/v1/process', opts.requestTimeoutMs ?? this.config.requestTimeoutMs, this.config.headers).connect();
182
+ const environment = { ...this.sandboxEnvs, ...(opts.envs ?? {}) };
183
183
  const processConfig = processStartConfig(cmd, opts);
184
184
  socket.sendJson({
185
185
  type: 'start',
186
186
  id: opts.processID,
187
187
  cmd: processConfig.cmd,
188
188
  args: processConfig.args,
189
- cwd: opts.cwd ?? opts.rootDir,
189
+ cwd: opts.cwd,
190
190
  user: opts.user,
191
191
  environment,
192
192
  envs: environment,
193
193
  stdin: opts.stdin ?? false,
194
- timeout_ms: opts.timeoutMs ?? opts.timeout ?? 60_000,
194
+ timeout_ms: opts.timeoutMs ?? 60_000,
195
195
  });
196
196
  const first = await nextStarted(socket);
197
197
  const pid = framePid(first);
@@ -6,9 +6,14 @@ export interface ConnectionOpts {
6
6
  accessToken?: string;
7
7
  domain?: string;
8
8
  apiUrl?: string;
9
+ /** Absolute sandbox data-plane URL override, primarily for local runtimes. */
10
+ sandboxUrl?: string;
9
11
  dataPlaneDomain?: string;
10
12
  requestTimeoutMs?: number;
11
13
  headers?: Record<string, string>;
14
+ apiHeaders?: Record<string, string>;
15
+ debug?: boolean;
16
+ signal?: AbortSignal;
12
17
  proxy?: unknown;
13
18
  }
14
19
  /** Resolved connection settings used by control-plane and data-plane clients. */
@@ -16,11 +21,30 @@ export declare class ConnectionConfig {
16
21
  readonly apiKey?: string;
17
22
  readonly domain: string;
18
23
  readonly apiUrl: string;
24
+ /** Absolute sandbox data-plane URL override, primarily for local runtimes. */
25
+ readonly sandboxUrl?: string;
19
26
  readonly dataPlaneDomain: string;
20
27
  readonly requestTimeoutMs: number;
21
28
  readonly headers: Record<string, string>;
29
+ readonly apiHeaders: Record<string, string>;
30
+ readonly debug: boolean;
31
+ readonly signal?: AbortSignal;
22
32
  readonly proxy?: unknown;
23
33
  constructor(opts?: ConnectionOpts);
24
34
  /** HTTP headers including the configured bearer token. */
25
35
  get authHeaders(): Record<string, string>;
36
+ /** Return an abort signal that follows the caller signal and optional timeout. */
37
+ getSignal(requestTimeoutMs?: number, signal?: AbortSignal | undefined): AbortSignal | undefined;
38
+ /** Return the sandbox data-plane API URL for a Watasu route token. */
39
+ getSandboxUrl(sandboxId: string, opts: {
40
+ sandboxDomain?: string;
41
+ envdPort: number;
42
+ }): string;
43
+ /** Return the direct sandbox data-plane API URL for a Watasu route token. */
44
+ getSandboxDirectUrl(sandboxId: string, opts: {
45
+ sandboxDomain?: string;
46
+ envdPort: number;
47
+ }): string;
48
+ /** Return the public hostname for a Watasu sandbox route token and port. */
49
+ getHost(sandboxId: string, port: number, sandboxDomain?: string): string;
26
50
  }
@@ -5,9 +5,14 @@ export class ConnectionConfig {
5
5
  apiKey;
6
6
  domain;
7
7
  apiUrl;
8
+ /** Absolute sandbox data-plane URL override, primarily for local runtimes. */
9
+ sandboxUrl;
8
10
  dataPlaneDomain;
9
11
  requestTimeoutMs;
10
12
  headers;
13
+ apiHeaders;
14
+ debug;
15
+ signal;
11
16
  proxy;
12
17
  constructor(opts = {}) {
13
18
  const env = typeof process !== 'undefined' ? process.env : {};
@@ -18,18 +23,60 @@ export class ConnectionConfig {
18
23
  this.domain = opts.domain ?? env.WATASU_DOMAIN ?? 'watasu.io';
19
24
  this.apiUrl =
20
25
  opts.apiUrl ?? env.WATASU_API_URL ?? `https://api.${this.domain}/v1`;
26
+ this.sandboxUrl =
27
+ opts.sandboxUrl ??
28
+ env.WATASU_SANDBOX_URL;
21
29
  this.dataPlaneDomain =
22
30
  opts.dataPlaneDomain ??
23
31
  env.WATASU_DATA_PLANE_DOMAIN ??
24
32
  'watasuhost.com';
25
33
  this.requestTimeoutMs = opts.requestTimeoutMs ?? 60_000;
26
34
  this.headers = opts.headers ?? {};
35
+ this.apiHeaders = opts.apiHeaders ?? {};
36
+ this.debug = opts.debug ?? false;
37
+ this.signal = opts.signal;
27
38
  this.proxy = opts.proxy;
28
39
  }
29
40
  /** HTTP headers including the configured bearer token. */
30
41
  get authHeaders() {
31
42
  return this.apiKey
32
- ? { ...this.headers, Authorization: `Bearer ${this.apiKey}` }
33
- : { ...this.headers };
43
+ ? { ...this.headers, ...this.apiHeaders, Authorization: `Bearer ${this.apiKey}` }
44
+ : { ...this.headers, ...this.apiHeaders };
45
+ }
46
+ /** Return an abort signal that follows the caller signal and optional timeout. */
47
+ getSignal(requestTimeoutMs = this.requestTimeoutMs, signal = this.signal) {
48
+ if (requestTimeoutMs === undefined && signal === undefined)
49
+ return undefined;
50
+ const controller = new AbortController();
51
+ if (signal?.aborted)
52
+ controller.abort();
53
+ else
54
+ signal?.addEventListener('abort', () => controller.abort(), { once: true });
55
+ if (requestTimeoutMs > 0) {
56
+ const timeout = setTimeout(() => controller.abort(), requestTimeoutMs);
57
+ const maybeTimeout = timeout;
58
+ if (typeof maybeTimeout.unref === 'function')
59
+ maybeTimeout.unref();
60
+ controller.signal.addEventListener('abort', () => clearTimeout(timeout), { once: true });
61
+ }
62
+ return controller.signal;
63
+ }
64
+ /** Return the sandbox data-plane API URL for a Watasu route token. */
65
+ getSandboxUrl(sandboxId, opts) {
66
+ if (this.sandboxUrl)
67
+ return this.sandboxUrl;
68
+ if (this.debug)
69
+ return `http://localhost:${opts.envdPort}`;
70
+ return `https://${sandboxId}.sandbox.${opts.sandboxDomain ?? this.dataPlaneDomain}`;
71
+ }
72
+ /** Return the direct sandbox data-plane API URL for a Watasu route token. */
73
+ getSandboxDirectUrl(sandboxId, opts) {
74
+ return this.getSandboxUrl(sandboxId, opts);
75
+ }
76
+ /** Return the public hostname for a Watasu sandbox route token and port. */
77
+ getHost(sandboxId, port, sandboxDomain = this.dataPlaneDomain) {
78
+ if (this.debug)
79
+ return `localhost:${port}`;
80
+ return `p${port}-${sandboxId}.sandbox.${sandboxDomain}`;
34
81
  }
35
82
  }
@@ -40,6 +40,7 @@ export interface WatchOpts {
40
40
  }
41
41
  export interface FilesystemRequestOpts {
42
42
  requestTimeoutMs?: number;
43
+ signal?: AbortSignal;
43
44
  user?: string;
44
45
  }
45
46
  export interface FilesystemReadOpts extends FilesystemRequestOpts {
@@ -56,8 +57,6 @@ export declare class WatchHandle {
56
57
  constructor(socket: ProcessSocket, events: AsyncIterable<ProcessFrame>, onEvent: (event: FilesystemEvent) => void | Promise<void>, onExit?: (error?: Error) => void | Promise<void>);
57
58
  /** Stop watching the directory. */
58
59
  stop(): void;
59
- /** Alias for `stop`. */
60
- close(): void;
61
60
  /** Resolves when the watcher stream exits. */
62
61
  wait(): Promise<void>;
63
62
  private pump;
@@ -102,30 +101,19 @@ export declare class Filesystem {
102
101
  /** Write several files in one runtime API call. */
103
102
  writeFiles(files: WriteEntry[], opts?: FilesystemWriteOpts): Promise<WriteInfo[]>;
104
103
  /** List directory entries below `path`. */
105
- list(path: string, opts?: {
106
- requestTimeoutMs?: number;
104
+ list(path: string, opts?: FilesystemRequestOpts & {
107
105
  depth?: number;
108
106
  }): Promise<EntryInfo[]>;
109
107
  /** Return whether a file or directory exists at `path`. */
110
- exists(path: string, opts?: {
111
- requestTimeoutMs?: number;
112
- }): Promise<boolean>;
108
+ exists(path: string, opts?: FilesystemRequestOpts): Promise<boolean>;
113
109
  /** Return stat metadata for `path`. */
114
- getInfo(path: string, opts?: {
115
- requestTimeoutMs?: number;
116
- }): Promise<EntryInfo>;
110
+ getInfo(path: string, opts?: FilesystemRequestOpts): Promise<EntryInfo>;
117
111
  /** Remove a file at `path`. */
118
- remove(path: string, opts?: {
119
- requestTimeoutMs?: number;
120
- }): Promise<void>;
112
+ remove(path: string, opts?: FilesystemRequestOpts): Promise<void>;
121
113
  /** Move or rename a file. */
122
- rename(oldPath: string, newPath: string, opts?: {
123
- requestTimeoutMs?: number;
124
- }): Promise<EntryInfo>;
114
+ rename(oldPath: string, newPath: string, opts?: FilesystemRequestOpts): Promise<EntryInfo>;
125
115
  /** Create a directory. */
126
- makeDir(path: string, opts?: {
127
- requestTimeoutMs?: number;
128
- }): Promise<boolean>;
116
+ makeDir(path: string, opts?: FilesystemRequestOpts): Promise<boolean>;
129
117
  /** Start watching a directory for filesystem events. */
130
118
  watchDir(path: string): FilesystemWatcher;
131
119
  watchDir(path: string, onEvent: (event: FilesystemEvent) => void | Promise<void>, opts?: WatchOpts): Promise<FilesystemWatcher>;
@@ -22,10 +22,6 @@ export class WatchHandle {
22
22
  stop() {
23
23
  this.socket.close();
24
24
  }
25
- /** Alias for `stop`. */
26
- close() {
27
- this.stop();
28
- }
29
25
  /** Resolves when the watcher stream exits. */
30
26
  wait() {
31
27
  return this.done;
@@ -66,7 +62,7 @@ export class FilesystemWatcher {
66
62
  if (this.handle)
67
63
  return;
68
64
  const nextOpts = { ...this.opts, ...opts };
69
- const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, withQuery('/runtime/v1/files/watch', { path: this.path, recursive: nextOpts.recursive ?? false, include_entry: nextOpts.includeEntry }), nextOpts.requestTimeoutMs).connect();
65
+ const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, withQuery('/runtime/v1/files/watch', { path: this.path, recursive: nextOpts.recursive ?? false, include_entry: nextOpts.includeEntry }), nextOpts.requestTimeoutMs, this.dataPlane.headers).connect();
70
66
  this.handle = new WatchHandle(socket, socket, async (event) => {
71
67
  for (const listener of this.listeners)
72
68
  await listener(event);
package/dist/git.d.ts CHANGED
@@ -20,16 +20,15 @@ export interface GitAuthOpts {
20
20
  envs?: Record<string, string>;
21
21
  user?: string;
22
22
  cwd?: string;
23
- timeout?: number;
24
23
  timeoutMs?: number;
25
24
  requestTimeoutMs?: number;
25
+ signal?: AbortSignal;
26
26
  }
27
27
  export interface GitCloneOpts extends GitAuthOpts {
28
28
  path?: string;
29
29
  branch?: string;
30
30
  depth?: number;
31
31
  recursive?: boolean;
32
- submodules?: boolean;
33
32
  dangerouslyStoreCredentials?: boolean;
34
33
  }
35
34
  export interface GitRequestOpts extends GitAuthOpts {
package/dist/git.js CHANGED
@@ -9,7 +9,7 @@ export class Git {
9
9
  return this.run('/runtime/v1/git/clone', {
10
10
  url,
11
11
  ...gitOpts(opts),
12
- ...pick(opts, ['path', 'branch', 'depth', 'recursive', 'submodules', 'username', 'password']),
12
+ ...pick(opts, ['path', 'branch', 'depth', 'recursive', 'username', 'password']),
13
13
  dangerously_store_credentials: opts.dangerouslyStoreCredentials,
14
14
  }, opts);
15
15
  }
@@ -163,7 +163,11 @@ export class Git {
163
163
  return String(result.value ?? '');
164
164
  }
165
165
  async run(path, json, opts) {
166
- const payload = await this.dataPlane.postJson(path, { json: compact(json), requestTimeoutMs: opts.requestTimeoutMs });
166
+ const payload = await this.dataPlane.postJson(path, {
167
+ json: compact(json),
168
+ requestTimeoutMs: opts.requestTimeoutMs,
169
+ signal: opts.signal,
170
+ });
167
171
  return gitResult(payload.git ?? payload);
168
172
  }
169
173
  }
@@ -172,7 +176,7 @@ function gitOpts(opts) {
172
176
  env_vars: opts.envs,
173
177
  user: opts.user,
174
178
  cwd: opts.cwd,
175
- timeout_seconds: opts.timeout ?? (opts.timeoutMs === undefined ? undefined : Math.ceil(opts.timeoutMs / 1000)),
179
+ timeout_seconds: opts.timeoutMs === undefined ? undefined : Math.ceil(opts.timeoutMs / 1000),
176
180
  };
177
181
  }
178
182
  function pick(source, keys) {
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, Invali
2
2
  export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
3
3
  export { Sandbox, SandboxPaginator, SnapshotPaginator } from './sandbox.js';
4
4
  export { Sandbox as CodeInterpreterSandbox } from './codeInterpreter.js';
5
- export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxCreateOpts, SandboxConnectOpts, SandboxInfo, SandboxListOpts, SandboxMetrics, McpServer, McpServerName, SandboxNetworkSelector, SandboxNetworkUpdate, SandboxNetworkUpdateOpts, SandboxUrlOpts, SnapshotInfo, FileUrlInfo, } from './sandbox.js';
5
+ export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxCreateOpts, SandboxConnectOpts, SandboxInfo, SandboxInfoLifecycle, SandboxLifecycle, SandboxListOpts, SandboxMetrics, SandboxMetricsOpts, McpServer, McpServerName, SandboxNetworkSelector, SandboxNetworkUpdate, SandboxNetworkUpdateOpts, SandboxUrlOpts, SnapshotInfo, FileUrlInfo, } from './sandbox.js';
6
6
  export type { CreateCodeContextOpts, RunCodeLanguage, RunCodeOpts, } from './codeInterpreter.js';
7
7
  export { Context as CodeInterpreterContext, Execution as CodeInterpreterExecution, ExecutionError as CodeInterpreterExecutionError, OutputMessage as CodeInterpreterOutputMessage, Result as CodeInterpreterResult, } from './codeInterpreter.js';
8
8
  export { CommandExitError, CommandHandle, Commands } from './commands.js';
@@ -17,6 +17,8 @@ export { Pty } from './pty.js';
17
17
  export type { PtyConnectOpts, PtyCreateOpts, PtySize } from './pty.js';
18
18
  export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
19
19
  export type { TerminalOpts } from './terminal.js';
20
+ export { Volume } from './volume.js';
21
+ export type { VolumeApiParams, VolumeConnectionConfig, VolumeEntryStat, VolumeFileType, VolumeInfo, VolumeListFilesOpts, VolumeListOpts, VolumeMetadataOpts, VolumeReadFileOpts, VolumeReadFormat, VolumeWriteData, VolumeWriteFileOpts, } from './volume.js';
20
22
  export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
21
- export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, waitForUrl, } from './template.js';
23
+ export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, } from './template.js';
22
24
  export type { BuildInfo, BuildOptions, BuildStatusReason, CopyItem, GetBuildStatusOptions, LogEntry, ReadyCommand, TemplateBuildStatus, TemplateBuildStatusResponse, TemplateBuilder, TemplateClass, TemplateFactory, TemplateFinal, TemplateFromImage, TemplateOptions, TemplateTag, TemplateTagInfo, } from './template.js';
package/dist/index.js CHANGED
@@ -9,5 +9,6 @@ export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesyst
9
9
  export { Git } from './git.js';
10
10
  export { Pty } from './pty.js';
11
11
  export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
12
+ export { Volume } from './volume.js';
12
13
  export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
13
- export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, waitForUrl, } from './template.js';
14
+ export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, } from './template.js';
package/dist/process.d.ts CHANGED
@@ -41,7 +41,7 @@ export declare class Process {
41
41
  private waitPromise?;
42
42
  constructor(processID: string, handle: CommandHandle, output: ProcessOutput, onExit?: ((exitCode: number) => Promise<void> | void) | undefined);
43
43
  kill(): Promise<void>;
44
- wait(timeout?: number): Promise<ProcessOutput>;
44
+ wait(timeoutMs?: number): Promise<ProcessOutput>;
45
45
  private waitOnce;
46
46
  sendStdin(data: string): Promise<void>;
47
47
  }