@watasu/sdk 0.1.40 → 0.1.50

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.
@@ -8,6 +8,13 @@ export declare enum FileType {
8
8
  /** Symbolic link. */
9
9
  SYMLINK = "symlink"
10
10
  }
11
+ export declare enum FilesystemEventType {
12
+ CHMOD = "chmod",
13
+ CREATE = "create",
14
+ REMOVE = "remove",
15
+ RENAME = "rename",
16
+ WRITE = "write"
17
+ }
11
18
  /** Metadata for one sandbox filesystem entry. */
12
19
  export interface EntryInfo {
13
20
  name: string;
@@ -27,7 +34,8 @@ export interface WriteEntry {
27
34
  data: WriteData;
28
35
  }
29
36
  export interface FilesystemEvent {
30
- type: 'create' | 'write' | 'modify' | 'remove' | 'delete' | 'rename' | string;
37
+ name: string;
38
+ type: FilesystemEventType | string;
31
39
  path: string;
32
40
  entry?: EntryInfo;
33
41
  raw: Record<string, unknown>;
@@ -35,7 +43,10 @@ export interface FilesystemEvent {
35
43
  export interface WatchOpts {
36
44
  recursive?: boolean;
37
45
  includeEntry?: boolean;
46
+ allowNetworkMounts?: boolean;
38
47
  requestTimeoutMs?: number;
48
+ signal?: AbortSignal;
49
+ user?: string;
39
50
  onExit?: (error?: Error) => void | Promise<void>;
40
51
  }
41
52
  export interface FilesystemRequestOpts {
@@ -48,6 +59,7 @@ export interface FilesystemReadOpts extends FilesystemRequestOpts {
48
59
  }
49
60
  export interface FilesystemWriteOpts extends FilesystemRequestOpts {
50
61
  gzip?: boolean;
62
+ useOctetStream?: boolean;
51
63
  metadata?: Record<string, string>;
52
64
  }
53
65
  /** Live filesystem watcher. Call `stop()` to close the local watch stream. */
@@ -1,3 +1,4 @@
1
+ import { gzipSync } from 'node:zlib';
1
2
  import { withQuery } from './transport.js';
2
3
  import { FileNotFoundError, InvalidArgumentError } from './errors.js';
3
4
  import { ProcessSocket, base64Encode } from './processSocket.js';
@@ -10,6 +11,14 @@ export var FileType;
10
11
  /** Symbolic link. */
11
12
  FileType["SYMLINK"] = "symlink";
12
13
  })(FileType || (FileType = {}));
14
+ export var FilesystemEventType;
15
+ (function (FilesystemEventType) {
16
+ FilesystemEventType["CHMOD"] = "chmod";
17
+ FilesystemEventType["CREATE"] = "create";
18
+ FilesystemEventType["REMOVE"] = "remove";
19
+ FilesystemEventType["RENAME"] = "rename";
20
+ FilesystemEventType["WRITE"] = "write";
21
+ })(FilesystemEventType || (FilesystemEventType = {}));
13
22
  /** Live filesystem watcher. Call `stop()` to close the local watch stream. */
14
23
  export class WatchHandle {
15
24
  socket;
@@ -62,7 +71,12 @@ export class FilesystemWatcher {
62
71
  if (this.handle)
63
72
  return;
64
73
  const nextOpts = { ...this.opts, ...opts };
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();
74
+ const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, withQuery('/runtime/v1/files/watch', {
75
+ path: this.path,
76
+ recursive: nextOpts.recursive ?? false,
77
+ include_entry: nextOpts.includeEntry,
78
+ allow_network_mounts: nextOpts.allowNetworkMounts,
79
+ }), nextOpts.requestTimeoutMs, this.dataPlane.headers).connect();
66
80
  this.handle = new WatchHandle(socket, socket, async (event) => {
67
81
  for (const listener of this.listeners)
68
82
  await listener(event);
@@ -109,8 +123,8 @@ export class Filesystem {
109
123
  if (Array.isArray(pathOrFiles)) {
110
124
  return this.writeFiles(pathOrFiles, dataOrOpts);
111
125
  }
112
- const body = await writeDataToBytes(dataOrOpts);
113
- const payload = await this.dataPlane.putJson(withQuery('/runtime/v1/files', { path: pathOrFiles, gzip: opts.gzip }), body, opts);
126
+ const body = maybeGzip(await writeDataToBytes(dataOrOpts), opts.gzip);
127
+ const payload = await this.dataPlane.putJson(withQuery('/runtime/v1/files', { path: pathOrFiles, gzip: opts.gzip }), body, requestOpts(opts, opts.gzip ? { 'content-encoding': 'gzip' } : undefined));
114
128
  return entryInfo(payload.file ?? payload);
115
129
  }
116
130
  /** Write raw bytes to a file. */
@@ -122,11 +136,12 @@ export class Filesystem {
122
136
  if (files.length === 0)
123
137
  return [];
124
138
  const payload = await this.dataPlane.postJson('/runtime/v1/files/write_files', {
125
- ...opts,
139
+ ...requestOpts(opts),
126
140
  json: {
127
141
  files: await Promise.all(files.map(async (file) => ({
128
142
  path: file.path,
129
- data_base64: base64Encode(await writeDataToBytes(file.data)),
143
+ data_base64: base64Encode(maybeGzip(await writeDataToBytes(file.data), opts.gzip)),
144
+ ...(opts.gzip ? { gzip: true } : {}),
130
145
  }))),
131
146
  },
132
147
  });
@@ -194,6 +209,19 @@ async function writeDataToBytes(data) {
194
209
  return readStreamToBytes(data);
195
210
  throw new InvalidArgumentError(`Unsupported file data type: ${Object.prototype.toString.call(data)}`);
196
211
  }
212
+ function requestOpts(opts, headers) {
213
+ const out = {};
214
+ if (opts.requestTimeoutMs !== undefined)
215
+ out.requestTimeoutMs = opts.requestTimeoutMs;
216
+ if (opts.signal !== undefined)
217
+ out.signal = opts.signal;
218
+ if (headers !== undefined)
219
+ out.headers = headers;
220
+ return out;
221
+ }
222
+ function maybeGzip(bytes, enabled) {
223
+ return enabled ? new Uint8Array(gzipSync(bytes)) : bytes;
224
+ }
197
225
  function toArrayBuffer(bytes) {
198
226
  const buffer = new ArrayBuffer(bytes.byteLength);
199
227
  new Uint8Array(buffer).set(bytes);
@@ -253,17 +281,32 @@ function recordOfStrings(value) {
253
281
  }
254
282
  function filesystemEvent(value) {
255
283
  const item = value && typeof value === 'object' ? value : {};
284
+ const path = String(item.path ?? '');
256
285
  return {
257
286
  type: normalizeEventType(String(item.type ?? 'modify')),
258
- path: String(item.path ?? ''),
287
+ name: String(item.name ?? relativeName(path)),
288
+ path,
259
289
  entry: item.file && typeof item.file === 'object' ? entryInfo(item.file) : undefined,
260
290
  raw: item,
261
291
  };
262
292
  }
263
293
  function normalizeEventType(value) {
264
294
  if (value === 'delete')
265
- return 'remove';
295
+ return FilesystemEventType.REMOVE;
266
296
  if (value === 'modify')
267
- return 'write';
297
+ return FilesystemEventType.WRITE;
298
+ if (value === 'chmod')
299
+ return FilesystemEventType.CHMOD;
300
+ if (value === 'create')
301
+ return FilesystemEventType.CREATE;
302
+ if (value === 'remove')
303
+ return FilesystemEventType.REMOVE;
304
+ if (value === 'rename')
305
+ return FilesystemEventType.RENAME;
306
+ if (value === 'write')
307
+ return FilesystemEventType.WRITE;
268
308
  return value;
269
309
  }
310
+ function relativeName(path) {
311
+ return path.split('/').filter(Boolean).pop() ?? '';
312
+ }
package/dist/git.d.ts CHANGED
@@ -50,13 +50,15 @@ export interface GitCredentialOpts extends GitRequestOpts {
50
50
  host?: string;
51
51
  protocol?: string;
52
52
  }
53
+ export type GitDangerouslyAuthenticateOpts = GitCredentialOpts;
53
54
  export interface GitConfigureUserOpts extends GitRequestOpts {
54
- scope?: 'global' | 'local';
55
+ scope?: GitConfigScope;
55
56
  path?: string;
56
57
  }
57
58
  export interface GitBranchOpts extends GitRequestOpts {
58
59
  force?: boolean;
59
60
  }
61
+ export type GitDeleteBranchOpts = GitBranchOpts;
60
62
  export interface GitAddOpts extends GitRequestOpts {
61
63
  files?: string[];
62
64
  all?: boolean;
@@ -83,9 +85,10 @@ export interface GitRemoteAddOpts extends GitRequestOpts {
83
85
  overwrite?: boolean;
84
86
  }
85
87
  export interface GitConfigOpts extends GitRequestOpts {
86
- scope?: 'global' | 'local';
88
+ scope?: GitConfigScope;
87
89
  path?: string;
88
90
  }
91
+ export type GitConfigScope = 'global' | 'local' | 'system';
89
92
  export interface GitBranches {
90
93
  path?: string;
91
94
  branches: string[];
package/dist/index.d.ts CHANGED
@@ -1,24 +1,36 @@
1
- export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
1
+ export { ApiError, AuthenticationError, BuildError, ConflictError, FileNotFoundError, FileUploadError, GitAuthError, GitUpstreamError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, SandboxNotFoundError, TemplateError, TimeoutError, VolumeError, } from './errors.js';
2
2
  export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
3
- export { Sandbox, SandboxPaginator, SnapshotPaginator } from './sandbox.js';
3
+ export type { ConnectionOpts, Username } from './connectionConfig.js';
4
+ export { ControlClient as ApiClient } from './transport.js';
5
+ export { ALL_TRAFFIC, Sandbox, SandboxPaginator, SnapshotPaginator, getSignature, } from './sandbox.js';
4
6
  export { Sandbox as CodeInterpreterSandbox } from './codeInterpreter.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';
7
+ export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxApiOpts, SandboxCreateOpts, SandboxConnectOpts, SandboxInfo, SandboxInfoLifecycle, SandboxLifecycle, SandboxListOpts, SandboxMetrics, SandboxMetricsOpts, McpServer, McpServerName, SandboxNetworkInfo, SandboxNetworkOpts, SandboxNetworkRule, SandboxNetworkRuleInfo, SandboxNetworkRules, SandboxNetworkSelector, SandboxNetworkSelectorContext, SandboxNetworkTransform, SandboxNetworkUpdate, SandboxNetworkUpdateOpts, SandboxOpts, SandboxState, SandboxUrlOpts, SignatureOpts, SnapshotInfo, SnapshotListOpts, FileUrlInfo, } from './sandbox.js';
6
8
  export type { CreateCodeContextOpts, RunCodeLanguage, RunCodeOpts, } from './codeInterpreter.js';
7
9
  export { Context as CodeInterpreterContext, Execution as CodeInterpreterExecution, ExecutionError as CodeInterpreterExecutionError, OutputMessage as CodeInterpreterOutputMessage, Result as CodeInterpreterResult, } from './codeInterpreter.js';
8
10
  export { CommandExitError, CommandHandle, Commands } from './commands.js';
9
- export type { CommandResult, CommandStartOpts, ProcessInfo } from './commands.js';
11
+ export type { CommandConnectOpts, CommandRequestOpts, CommandResult, CommandStartOpts, ProcessInfo, PtyOutput, Stderr, Stdout, } from './commands.js';
10
12
  export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
11
13
  export type { ProcessOpts } from './process.js';
12
- export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
14
+ export { FileType, Filesystem, FilesystemEventType, FilesystemWatcher, WatchHandle, } from './filesystem.js';
13
15
  export type { EntryInfo, FilesystemEvent, FilesystemReadOpts, FilesystemRequestOpts, FilesystemWriteOpts, WatchOpts, WriteData, WriteEntry, WriteInfo, } from './filesystem.js';
14
16
  export { Git } from './git.js';
15
- export type { GitAddOpts, GitAuthOpts, GitBranches, GitBranchOpts, GitCloneOpts, GitCommandResult, GitConfigOpts, GitConfigureUserOpts, GitCredentialOpts, GitCommitOpts, GitFileStatus, GitInitOpts, GitPullOpts, GitPushOpts, GitRemoteAddOpts, GitResetMode, GitResetOpts, GitRestoreOpts, GitRequestOpts, GitStatus, } from './git.js';
17
+ export type { GitAddOpts, GitAuthOpts, GitBranches, GitBranchOpts, GitCloneOpts, GitCommandResult, GitConfigScope, GitConfigOpts, GitConfigureUserOpts, GitCredentialOpts, GitCommitOpts, GitDangerouslyAuthenticateOpts, GitDeleteBranchOpts, GitFileStatus, GitInitOpts, GitPullOpts, GitPushOpts, GitRemoteAddOpts, GitResetMode, GitResetOpts, GitRestoreOpts, GitRequestOpts, GitStatus, } from './git.js';
16
18
  export { Pty } from './pty.js';
17
19
  export type { PtyConnectOpts, PtyCreateOpts, PtySize } from './pty.js';
18
20
  export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
19
21
  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';
22
+ export { Volume, VolumeConnectionConfig, VolumeFileType } from './volume.js';
23
+ export type { VolumeAndToken, VolumeApiParams, VolumeApiOpts, VolumeEntryStat, VolumeInfo, VolumeListFilesOpts, VolumeListOpts, VolumeMetadataOpts, VolumeMetadataOptions, VolumeReadFileOpts, VolumeReadFormat, VolumeWriteData, VolumeWriteFileOpts, VolumeWriteOptions, } from './volume.js';
22
24
  export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
23
- export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, } from './template.js';
24
- export type { BuildInfo, BuildOptions, BuildStatusReason, CopyItem, GetBuildStatusOptions, LogEntry, ReadyCommand, TemplateBuildStatus, TemplateBuildStatusResponse, TemplateBuilder, TemplateClass, TemplateFactory, TemplateFinal, TemplateFromImage, TemplateOptions, TemplateTag, TemplateTagInfo, } from './template.js';
25
+ export { ReadyCmd, Template, TemplateBase, LogEntry, LogEntryEnd, LogEntryStart, defaultBuildLogger, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, } from './template.js';
26
+ export type { BuildInfo, BuildOptions, BuildStatusReason, CopyItem, GetBuildStatusOptions, LogEntryLevel, Logger, ReadyCommand, TemplateBuildStatus, TemplateBuildStatusResponse, TemplateBuilder, TemplateClass, TemplateFactory, TemplateFinal, TemplateFromImage, TemplateOptions, TemplateTag, TemplateTagInfo, } from './template.js';
27
+ export interface components {
28
+ schemas: Record<string, unknown>;
29
+ responses: Record<string, unknown>;
30
+ parameters: Record<string, unknown>;
31
+ requestBodies: Record<string, unknown>;
32
+ headers: Record<string, unknown>;
33
+ pathItems: Record<string, unknown>;
34
+ }
35
+ export type paths = Record<string, unknown>;
36
+ export { Sandbox as default } from './sandbox.js';
package/dist/index.js CHANGED
@@ -1,14 +1,16 @@
1
- export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
1
+ export { ApiError, AuthenticationError, BuildError, ConflictError, FileNotFoundError, FileUploadError, GitAuthError, GitUpstreamError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, SandboxNotFoundError, TemplateError, TimeoutError, VolumeError, } from './errors.js';
2
2
  export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
3
- export { Sandbox, SandboxPaginator, SnapshotPaginator } from './sandbox.js';
3
+ export { ControlClient as ApiClient } from './transport.js';
4
+ export { ALL_TRAFFIC, Sandbox, SandboxPaginator, SnapshotPaginator, getSignature, } from './sandbox.js';
4
5
  export { Sandbox as CodeInterpreterSandbox } from './codeInterpreter.js';
5
6
  export { Context as CodeInterpreterContext, Execution as CodeInterpreterExecution, ExecutionError as CodeInterpreterExecutionError, OutputMessage as CodeInterpreterOutputMessage, Result as CodeInterpreterResult, } from './codeInterpreter.js';
6
7
  export { CommandExitError, CommandHandle, Commands } from './commands.js';
7
8
  export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
8
- export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
9
+ export { FileType, Filesystem, FilesystemEventType, FilesystemWatcher, WatchHandle, } from './filesystem.js';
9
10
  export { Git } from './git.js';
10
11
  export { Pty } from './pty.js';
11
12
  export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
12
- export { Volume } from './volume.js';
13
+ export { Volume, VolumeConnectionConfig, VolumeFileType } from './volume.js';
13
14
  export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
14
- export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, } from './template.js';
15
+ export { ReadyCmd, Template, TemplateBase, LogEntry, LogEntryEnd, LogEntryStart, defaultBuildLogger, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, } from './template.js';
16
+ export { Sandbox as default } from './sandbox.js';
package/dist/process.js CHANGED
@@ -40,11 +40,15 @@ export class ProcessOutput {
40
40
  this._error = true;
41
41
  }
42
42
  replace(result) {
43
+ const streamedStdout = this.stdout;
44
+ const streamedStderr = this.stderr;
43
45
  this.messages = [];
44
- if (result.stdout)
45
- this.addStdout(processMessage(result.stdout, false));
46
- if (result.stderr)
47
- this.addStderr(processMessage(result.stderr, true));
46
+ const stdout = result.stdout || streamedStdout;
47
+ const stderr = result.stderr || streamedStderr;
48
+ if (stdout)
49
+ this.addStdout(processMessage(stdout, false));
50
+ if (stderr)
51
+ this.addStderr(processMessage(stderr, true));
48
52
  this.setExitCode(result.exitCode);
49
53
  }
50
54
  }
@@ -9,19 +9,31 @@ export declare class ProcessSocket implements AsyncIterable<ProcessFrame> {
9
9
  private ws?;
10
10
  private queue;
11
11
  private waiters;
12
+ private ackWaiters;
12
13
  private closed;
13
14
  private keepalive?;
14
15
  constructor(baseUrl: string, token: string, path: string, requestTimeoutMs?: number, headers?: Record<string, string>);
15
16
  connect(): Promise<this>;
16
- sendJson(payload: ProcessFrame): void;
17
- sendStdin(data: string | Uint8Array): void;
18
- closeStdin(): void;
17
+ sendJson(payload: ProcessFrame): Promise<void>;
18
+ sendStdin(data: string | Uint8Array, opts?: {
19
+ requestTimeoutMs?: number;
20
+ signal?: AbortSignal;
21
+ }): Promise<void>;
22
+ closeStdin(opts?: {
23
+ requestTimeoutMs?: number;
24
+ signal?: AbortSignal;
25
+ }): Promise<void>;
19
26
  close(): void;
20
27
  [Symbol.asyncIterator](): AsyncIterator<ProcessFrame>;
21
28
  private next;
22
29
  private onMessage;
23
30
  private finish;
24
31
  private flushDone;
32
+ private waitForControlAck;
33
+ private resolveControlAck;
34
+ private rejectControlAcks;
35
+ private removeControlAck;
36
+ private removeSignalListener;
25
37
  }
26
38
  export declare function base64Encode(bytes: Uint8Array): string;
27
39
  export declare function base64DecodeText(value: unknown): string;
@@ -12,6 +12,7 @@ export class ProcessSocket {
12
12
  ws;
13
13
  queue = [];
14
14
  waiters = [];
15
+ ackWaiters = new Map();
15
16
  closed = false;
16
17
  keepalive;
17
18
  constructor(baseUrl, token, path, requestTimeoutMs = 60_000, headers = {}) {
@@ -47,18 +48,41 @@ export class ProcessSocket {
47
48
  }, KEEPALIVE_PING_INTERVAL_SEC * 1000);
48
49
  return this;
49
50
  }
50
- sendJson(payload) {
51
+ async sendJson(payload) {
51
52
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
52
53
  throw new SandboxError('process websocket is not connected');
53
54
  }
54
- this.ws.send(JSON.stringify(payload));
55
+ await new Promise((resolve, reject) => {
56
+ this.ws.send(JSON.stringify(payload), (error) => {
57
+ if (error)
58
+ reject(error);
59
+ else
60
+ resolve();
61
+ });
62
+ });
55
63
  }
56
- sendStdin(data) {
64
+ async sendStdin(data, opts = {}) {
57
65
  const raw = typeof data === 'string' ? new TextEncoder().encode(data) : data;
58
- this.sendJson({ type: 'stdin', data: base64Encode(raw) });
66
+ const ack = this.waitForControlAck('stdin_ack', opts);
67
+ try {
68
+ await this.sendJson({ type: 'stdin', data: base64Encode(raw) });
69
+ await ack.promise;
70
+ }
71
+ catch (error) {
72
+ ack.cancel();
73
+ throw error;
74
+ }
59
75
  }
60
- closeStdin() {
61
- this.sendJson({ type: 'close_stdin' });
76
+ async closeStdin(opts = {}) {
77
+ const ack = this.waitForControlAck('close_stdin_ack', opts);
78
+ try {
79
+ await this.sendJson({ type: 'close_stdin' });
80
+ await ack.promise;
81
+ }
82
+ catch (error) {
83
+ ack.cancel();
84
+ throw error;
85
+ }
62
86
  }
63
87
  close() {
64
88
  this.closed = true;
@@ -86,6 +110,10 @@ export class ProcessSocket {
86
110
  const frame = JSON.parse(text);
87
111
  if (frame.type === 'pong' || frame.type === 'ready')
88
112
  return;
113
+ if (frame.type === 'stdin_ack' || frame.type === 'close_stdin_ack') {
114
+ this.resolveControlAck(String(frame.type));
115
+ return;
116
+ }
89
117
  if (frame.type === 'error') {
90
118
  this.finish(new SandboxError(String(frame.message ?? frame.code ?? 'process error')));
91
119
  return;
@@ -112,6 +140,7 @@ export class ProcessSocket {
112
140
  else
113
141
  this.queue.push(frame);
114
142
  }
143
+ this.rejectControlAcks(error ?? new SandboxError('process websocket closed before acknowledgement'));
115
144
  this.flushDone();
116
145
  }
117
146
  flushDone() {
@@ -119,6 +148,79 @@ export class ProcessSocket {
119
148
  waiter({ done: true, value: undefined });
120
149
  }
121
150
  }
151
+ waitForControlAck(type, opts = {}) {
152
+ if (opts.signal?.aborted) {
153
+ return {
154
+ promise: Promise.reject(new SandboxError('process control acknowledgement aborted')),
155
+ cancel: () => { },
156
+ };
157
+ }
158
+ let entry;
159
+ const promise = new Promise((resolve, reject) => {
160
+ const rejectWithAbort = () => {
161
+ this.removeControlAck(type, entry);
162
+ clearTimeout(entry.timer);
163
+ reject(new SandboxError('process control acknowledgement aborted'));
164
+ };
165
+ const timer = setTimeout(() => {
166
+ this.removeControlAck(type, entry);
167
+ this.removeSignalListener(entry);
168
+ reject(new TimeoutError());
169
+ }, opts.requestTimeoutMs ?? this.requestTimeoutMs);
170
+ entry = { resolve, reject, timer, signal: opts.signal };
171
+ if (opts.signal) {
172
+ entry.abort = rejectWithAbort;
173
+ opts.signal.addEventListener('abort', rejectWithAbort, { once: true });
174
+ }
175
+ const waiters = this.ackWaiters.get(type) ?? [];
176
+ waiters.push(entry);
177
+ this.ackWaiters.set(type, waiters);
178
+ });
179
+ return {
180
+ promise,
181
+ cancel: () => {
182
+ this.removeControlAck(type, entry);
183
+ clearTimeout(entry.timer);
184
+ if (entry.abort)
185
+ opts.signal?.removeEventListener('abort', entry.abort);
186
+ },
187
+ };
188
+ }
189
+ resolveControlAck(type) {
190
+ const entry = this.ackWaiters.get(type)?.shift();
191
+ if (!entry)
192
+ return;
193
+ clearTimeout(entry.timer);
194
+ if (entry.abort)
195
+ this.removeSignalListener(entry);
196
+ entry.resolve();
197
+ }
198
+ rejectControlAcks(error) {
199
+ for (const waiters of this.ackWaiters.values()) {
200
+ for (const entry of waiters.splice(0)) {
201
+ clearTimeout(entry.timer);
202
+ if (entry.abort)
203
+ this.removeSignalListener(entry);
204
+ entry.reject(error);
205
+ }
206
+ }
207
+ this.ackWaiters.clear();
208
+ }
209
+ removeControlAck(type, entry) {
210
+ const waiters = this.ackWaiters.get(type);
211
+ if (!waiters)
212
+ return;
213
+ const index = waiters.indexOf(entry);
214
+ if (index !== -1)
215
+ waiters.splice(index, 1);
216
+ if (waiters.length === 0)
217
+ this.ackWaiters.delete(type);
218
+ }
219
+ removeSignalListener(entry) {
220
+ if (entry.abort)
221
+ entry.signal?.removeEventListener('abort', entry.abort);
222
+ entry.abort = undefined;
223
+ }
122
224
  }
123
225
  export function base64Encode(bytes) {
124
226
  return Buffer.from(bytes).toString('base64');
package/dist/pty.js CHANGED
@@ -15,7 +15,7 @@ export class Pty {
15
15
  const envs = { TERM: 'xterm-256color', LANG: 'C.UTF-8', LC_ALL: 'C.UTF-8', ...(opts.envs ?? {}) };
16
16
  const size = opts.size ?? { cols: opts.cols ?? 80, rows: opts.rows ?? 24 };
17
17
  const args = opts.cmd === undefined ? ['-i', '-l'] : ['-l', '-c', opts.cmd];
18
- socket.sendJson({
18
+ await socket.sendJson({
19
19
  type: 'start',
20
20
  cmd: '/bin/bash',
21
21
  args,
package/dist/sandbox.d.ts CHANGED
@@ -7,6 +7,7 @@ import { Pty } from './pty.js';
7
7
  import { ProcessManager } from './process.js';
8
8
  import { TerminalManager } from './terminal.js';
9
9
  import type { Volume } from './volume.js';
10
+ export declare const ALL_TRAFFIC = "0.0.0.0/0";
10
11
  export interface SandboxCreateOpts extends ConnectionOpts {
11
12
  /** Template slug to create. Defaults to "base". */
12
13
  template?: string;
@@ -31,7 +32,18 @@ export interface SandboxLifecycle {
31
32
  onTimeout: 'kill' | 'pause';
32
33
  autoResume?: boolean;
33
34
  }
34
- export type SandboxNetworkSelector = string | string[];
35
+ export type SandboxNetworkTransform = {
36
+ headers?: Record<string, string>;
37
+ };
38
+ export type SandboxNetworkRule = {
39
+ transform?: SandboxNetworkTransform;
40
+ };
41
+ export type SandboxNetworkRules = Record<string, SandboxNetworkRule[]> | Map<string, SandboxNetworkRule[]>;
42
+ export interface SandboxNetworkSelectorContext {
43
+ allTraffic: string;
44
+ rules: Map<string, SandboxNetworkRule[]>;
45
+ }
46
+ export type SandboxNetworkSelector = string | string[] | ((ctx: SandboxNetworkSelectorContext) => string[]);
35
47
  export interface SandboxNetworkUpdate {
36
48
  allowOut?: SandboxNetworkSelector;
37
49
  denyOut?: SandboxNetworkSelector;
@@ -41,9 +53,15 @@ export interface SandboxNetworkUpdate {
41
53
  egressProfile?: string;
42
54
  egressProfiles?: string[];
43
55
  networkClass?: string;
44
- rules?: unknown;
56
+ rules?: SandboxNetworkRules;
45
57
  maskRequestHost?: string;
46
58
  }
59
+ export type SandboxNetworkOpts = SandboxNetworkUpdate;
60
+ export type SandboxNetworkInfo = SandboxNetworkUpdate;
61
+ export type SandboxNetworkRuleInfo = Record<string, unknown>;
62
+ export type SandboxOpts = SandboxCreateOpts;
63
+ export type SandboxApiOpts = ConnectionOpts;
64
+ export type SandboxState = string;
47
65
  export interface SandboxNetworkUpdateOpts extends ConnectionOpts {
48
66
  }
49
67
  export interface SandboxConnectOpts extends ConnectionOpts {
@@ -142,6 +160,17 @@ export interface RestoreSnapshotOpts extends ConnectionOpts {
142
160
  snapshotId?: string | number;
143
161
  timeoutMs?: number;
144
162
  }
163
+ export interface SignatureOpts {
164
+ path: string;
165
+ operation: 'read' | 'write';
166
+ user?: string;
167
+ expirationInSeconds?: number;
168
+ envdAccessToken?: string;
169
+ }
170
+ export declare function getSignature({ path, operation, user, expirationInSeconds, envdAccessToken, }: SignatureOpts): Promise<{
171
+ signature: string;
172
+ expiration: number | null;
173
+ }>;
145
174
  /** Paginator for listing sandbox snapshots. */
146
175
  export declare class SnapshotPaginator {
147
176
  private readonly opts;
@@ -195,7 +224,7 @@ export declare class Sandbox {
195
224
  sandbox?: Record<string, unknown>;
196
225
  envs?: Record<string, string>;
197
226
  });
198
- /** Sandbox id alias used by SDK-compatible code. */
227
+ /** Unique sandbox identifier. */
199
228
  get id(): string;
200
229
  static create(opts?: SandboxCreateOpts): Promise<Sandbox>;
201
230
  static create(template: string, opts?: SandboxCreateOpts): Promise<Sandbox>;
@@ -232,6 +261,8 @@ export declare class Sandbox {
232
261
  setTimeout(timeoutMs: number, opts?: SandboxRequestOpts): Promise<void>;
233
262
  /** Fetch control-plane metadata for a sandbox by id. */
234
263
  static getInfo(sandboxId: string, opts?: ConnectionOpts): Promise<SandboxInfo>;
264
+ /** Fetch full control-plane metadata for a sandbox by id. */
265
+ static getFullInfo(sandboxId: string, opts?: ConnectionOpts): Promise<SandboxInfo>;
235
266
  /** Fetch the latest control-plane metadata for this sandbox. */
236
267
  getInfo(opts?: SandboxRequestOpts): Promise<SandboxInfo>;
237
268
  /** Fetch latest sandbox metrics. */
@@ -240,8 +271,6 @@ export declare class Sandbox {
240
271
  createSnapshot(opts?: CreateSnapshotOpts): Promise<SnapshotInfo>;
241
272
  /** Delete a snapshot by id. */
242
273
  deleteSnapshot(snapshotId: string, opts?: ConnectionOpts): Promise<boolean>;
243
- /** Watasu-native alias for `createSnapshot`. */
244
- checkpoint(opts?: CreateSnapshotOpts): Promise<SnapshotInfo>;
245
274
  /** List checkpoints for this sandbox using snapshot naming. */
246
275
  listSnapshots(opts?: Omit<SnapshotListOpts, 'sandboxId'>): SnapshotPaginator;
247
276
  /** Restore a checkpoint into a new sandbox and return its control-plane info. */