@watasu/sdk 0.1.5 → 0.1.24
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 +145 -2
- package/dist/commands.d.ts +33 -4
- package/dist/commands.js +68 -17
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +8 -0
- package/dist/filesystem.d.ts +80 -13
- package/dist/filesystem.js +184 -9
- package/dist/git.d.ts +171 -0
- package/dist/git.js +277 -0
- package/dist/index.d.ts +16 -6
- package/dist/index.js +9 -4
- package/dist/process.d.ts +56 -0
- package/dist/process.js +137 -0
- package/dist/processSocket.d.ts +2 -0
- package/dist/processSocket.js +9 -1
- package/dist/pty.d.ts +37 -0
- package/dist/pty.js +90 -0
- package/dist/sandbox.d.ts +145 -25
- package/dist/sandbox.js +312 -45
- package/dist/template.d.ts +232 -0
- package/dist/template.js +624 -0
- package/dist/terminal.d.ts +41 -0
- package/dist/terminal.js +74 -0
- package/dist/transport.d.ts +1 -0
- package/dist/transport.js +3 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
export { ApiError, AuthenticationError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
|
|
1
|
+
export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
|
|
2
2
|
export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
|
|
3
|
-
export { Sandbox, SnapshotPaginator } from './sandbox.js';
|
|
4
|
-
export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxCreateOpts, SandboxConnectOpts, SandboxInfo, SandboxMetrics, SnapshotInfo, } from './sandbox.js';
|
|
3
|
+
export { Sandbox, SandboxPaginator, SnapshotPaginator } from './sandbox.js';
|
|
4
|
+
export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxCreateOpts, SandboxConnectOpts, SandboxInfo, SandboxListOpts, SandboxMetrics, McpServer, McpServerName, SandboxNetworkSelector, SandboxNetworkUpdate, SandboxNetworkUpdateOpts, SandboxUrlOpts, SnapshotInfo, FileUrlInfo, } from './sandbox.js';
|
|
5
5
|
export { CommandExitError, CommandHandle, Commands } from './commands.js';
|
|
6
6
|
export type { CommandResult, CommandStartOpts, ProcessInfo } from './commands.js';
|
|
7
|
-
export {
|
|
8
|
-
export type {
|
|
9
|
-
export {
|
|
7
|
+
export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
|
|
8
|
+
export type { ProcessOpts } from './process.js';
|
|
9
|
+
export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
|
|
10
|
+
export type { EntryInfo, FilesystemEvent, FilesystemReadOpts, FilesystemRequestOpts, FilesystemWriteOpts, WatchOpts, WriteData, WriteEntry, WriteInfo, } from './filesystem.js';
|
|
11
|
+
export { Git } from './git.js';
|
|
12
|
+
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';
|
|
13
|
+
export { Pty } from './pty.js';
|
|
14
|
+
export type { PtyConnectOpts, PtyCreateOpts, PtySize } from './pty.js';
|
|
15
|
+
export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
|
|
16
|
+
export type { TerminalOpts } from './terminal.js';
|
|
17
|
+
export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
|
|
18
|
+
export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, waitForUrl, } from './template.js';
|
|
19
|
+
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
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
export { ApiError, AuthenticationError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
|
|
1
|
+
export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
|
|
2
2
|
export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
|
|
3
|
-
export { Sandbox, SnapshotPaginator } from './sandbox.js';
|
|
3
|
+
export { Sandbox, SandboxPaginator, SnapshotPaginator } from './sandbox.js';
|
|
4
4
|
export { CommandExitError, CommandHandle, Commands } from './commands.js';
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
5
|
+
export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
|
|
6
|
+
export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
|
|
7
|
+
export { Git } from './git.js';
|
|
8
|
+
export { Pty } from './pty.js';
|
|
9
|
+
export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
|
|
10
|
+
export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
|
|
11
|
+
export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, waitForUrl, } from './template.js';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { CommandHandle, CommandResult, Commands, CommandStartOpts } from './commands.js';
|
|
2
|
+
/** A message emitted by a sandbox process. */
|
|
3
|
+
export declare class ProcessMessage {
|
|
4
|
+
readonly line: string;
|
|
5
|
+
/** Unix epoch in nanoseconds. */
|
|
6
|
+
readonly timestamp: number;
|
|
7
|
+
readonly error: boolean;
|
|
8
|
+
constructor(line: string,
|
|
9
|
+
/** Unix epoch in nanoseconds. */
|
|
10
|
+
timestamp: number, error: boolean);
|
|
11
|
+
toString(): string;
|
|
12
|
+
}
|
|
13
|
+
/** Captured output from a sandbox process. */
|
|
14
|
+
export declare class ProcessOutput {
|
|
15
|
+
private messages;
|
|
16
|
+
private _finished;
|
|
17
|
+
private _error;
|
|
18
|
+
private _exitCode;
|
|
19
|
+
get error(): boolean;
|
|
20
|
+
get exitCode(): number | undefined;
|
|
21
|
+
get stdout(): string;
|
|
22
|
+
get stderr(): string;
|
|
23
|
+
addStdout(message: ProcessMessage): void;
|
|
24
|
+
addStderr(message: ProcessMessage): void;
|
|
25
|
+
setExitCode(exitCode: number): void;
|
|
26
|
+
replace(result: CommandResult): void;
|
|
27
|
+
}
|
|
28
|
+
export interface ProcessOpts extends Omit<CommandStartOpts, 'cmd' | 'args' | 'onStdout' | 'onStderr' | 'onExit'> {
|
|
29
|
+
cmd: string;
|
|
30
|
+
onStdout?: (out: ProcessMessage) => Promise<void> | void;
|
|
31
|
+
onStderr?: (out: ProcessMessage) => Promise<void> | void;
|
|
32
|
+
onExit?: ((exitCode: number) => Promise<void> | void) | (() => Promise<void> | void);
|
|
33
|
+
}
|
|
34
|
+
/** A running sandbox process. */
|
|
35
|
+
export declare class Process {
|
|
36
|
+
readonly processID: string;
|
|
37
|
+
private readonly handle;
|
|
38
|
+
readonly output: ProcessOutput;
|
|
39
|
+
private readonly onExit?;
|
|
40
|
+
readonly finished: Promise<ProcessOutput>;
|
|
41
|
+
private waitPromise?;
|
|
42
|
+
constructor(processID: string, handle: CommandHandle, output: ProcessOutput, onExit?: ((exitCode: number) => Promise<void> | void) | undefined);
|
|
43
|
+
kill(): Promise<void>;
|
|
44
|
+
wait(timeout?: number): Promise<ProcessOutput>;
|
|
45
|
+
private waitOnce;
|
|
46
|
+
sendStdin(data: string): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/** Manager for starting and interacting with sandbox processes. */
|
|
49
|
+
export declare class ProcessManager {
|
|
50
|
+
private readonly commands;
|
|
51
|
+
constructor(commands: Commands);
|
|
52
|
+
start(cmd: string): Promise<Process>;
|
|
53
|
+
start(opts: ProcessOpts): Promise<Process>;
|
|
54
|
+
startAndWait(cmd: string): Promise<ProcessOutput>;
|
|
55
|
+
startAndWait(opts: ProcessOpts): Promise<ProcessOutput>;
|
|
56
|
+
}
|
package/dist/process.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { CommandExitError } from './commands.js';
|
|
2
|
+
import { TimeoutError } from './errors.js';
|
|
3
|
+
/** A message emitted by a sandbox process. */
|
|
4
|
+
export class ProcessMessage {
|
|
5
|
+
line;
|
|
6
|
+
timestamp;
|
|
7
|
+
error;
|
|
8
|
+
constructor(line,
|
|
9
|
+
/** Unix epoch in nanoseconds. */
|
|
10
|
+
timestamp, error) {
|
|
11
|
+
this.line = line;
|
|
12
|
+
this.timestamp = timestamp;
|
|
13
|
+
this.error = error;
|
|
14
|
+
}
|
|
15
|
+
toString() {
|
|
16
|
+
return this.line;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Captured output from a sandbox process. */
|
|
20
|
+
export class ProcessOutput {
|
|
21
|
+
messages = [];
|
|
22
|
+
_finished = false;
|
|
23
|
+
_error = false;
|
|
24
|
+
_exitCode;
|
|
25
|
+
get error() { return this._error; }
|
|
26
|
+
get exitCode() { return this._exitCode; }
|
|
27
|
+
get stdout() { return this.messages.filter((message) => !message.error).map(String).join(''); }
|
|
28
|
+
get stderr() { return this.messages.filter((message) => message.error).map(String).join(''); }
|
|
29
|
+
addStdout(message) {
|
|
30
|
+
this.messages.push(message);
|
|
31
|
+
}
|
|
32
|
+
addStderr(message) {
|
|
33
|
+
this.messages.push(message);
|
|
34
|
+
this._error = true;
|
|
35
|
+
}
|
|
36
|
+
setExitCode(exitCode) {
|
|
37
|
+
this._exitCode = exitCode;
|
|
38
|
+
this._finished = true;
|
|
39
|
+
if (exitCode !== 0)
|
|
40
|
+
this._error = true;
|
|
41
|
+
}
|
|
42
|
+
replace(result) {
|
|
43
|
+
this.messages = [];
|
|
44
|
+
if (result.stdout)
|
|
45
|
+
this.addStdout(processMessage(result.stdout, false));
|
|
46
|
+
if (result.stderr)
|
|
47
|
+
this.addStderr(processMessage(result.stderr, true));
|
|
48
|
+
this.setExitCode(result.exitCode);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** A running sandbox process. */
|
|
52
|
+
export class Process {
|
|
53
|
+
processID;
|
|
54
|
+
handle;
|
|
55
|
+
output;
|
|
56
|
+
onExit;
|
|
57
|
+
finished;
|
|
58
|
+
waitPromise;
|
|
59
|
+
constructor(processID, handle, output, onExit) {
|
|
60
|
+
this.processID = processID;
|
|
61
|
+
this.handle = handle;
|
|
62
|
+
this.output = output;
|
|
63
|
+
this.onExit = onExit;
|
|
64
|
+
this.waitPromise = this.waitOnce();
|
|
65
|
+
this.finished = this.waitPromise;
|
|
66
|
+
}
|
|
67
|
+
async kill() {
|
|
68
|
+
await this.handle.kill();
|
|
69
|
+
}
|
|
70
|
+
async wait(timeout) {
|
|
71
|
+
if (!this.waitPromise)
|
|
72
|
+
this.waitPromise = this.waitOnce();
|
|
73
|
+
return waitFor(this.waitPromise, timeout);
|
|
74
|
+
}
|
|
75
|
+
async waitOnce() {
|
|
76
|
+
try {
|
|
77
|
+
this.output.replace(await this.handle.wait());
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error instanceof CommandExitError) {
|
|
81
|
+
this.output.replace(error);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await this.onExit?.(this.output.exitCode ?? 0);
|
|
88
|
+
return this.output;
|
|
89
|
+
}
|
|
90
|
+
async sendStdin(data) {
|
|
91
|
+
await this.handle.sendStdin(data);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function waitFor(promise, timeoutMs) {
|
|
95
|
+
if (timeoutMs === undefined || timeoutMs <= 0)
|
|
96
|
+
return promise;
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
const timer = setTimeout(() => reject(new TimeoutError()), timeoutMs);
|
|
99
|
+
promise.then(resolve, reject).finally(() => clearTimeout(timer));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** Manager for starting and interacting with sandbox processes. */
|
|
103
|
+
export class ProcessManager {
|
|
104
|
+
commands;
|
|
105
|
+
constructor(commands) {
|
|
106
|
+
this.commands = commands;
|
|
107
|
+
}
|
|
108
|
+
async start(cmdOrOpts) {
|
|
109
|
+
const opts = processOpts(cmdOrOpts);
|
|
110
|
+
const { cmd, onStdout, onStderr, onExit, ...commandOpts } = opts;
|
|
111
|
+
const output = new ProcessOutput();
|
|
112
|
+
const handle = await this.commands.start(cmd, {
|
|
113
|
+
...commandOpts,
|
|
114
|
+
onStdout: async (data) => {
|
|
115
|
+
const message = processMessage(data, false);
|
|
116
|
+
output.addStdout(message);
|
|
117
|
+
await onStdout?.(message);
|
|
118
|
+
},
|
|
119
|
+
onStderr: async (data) => {
|
|
120
|
+
const message = processMessage(data, true);
|
|
121
|
+
output.addStderr(message);
|
|
122
|
+
await onStderr?.(message);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return new Process(String(handle.pid), handle, output, onExit);
|
|
126
|
+
}
|
|
127
|
+
async startAndWait(cmdOrOpts) {
|
|
128
|
+
const process = await this.start(cmdOrOpts);
|
|
129
|
+
return process.wait(typeof cmdOrOpts === 'string' ? undefined : cmdOrOpts.timeout);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function processOpts(cmdOrOpts) {
|
|
133
|
+
return typeof cmdOrOpts === 'string' ? { cmd: cmdOrOpts } : cmdOrOpts;
|
|
134
|
+
}
|
|
135
|
+
function processMessage(line, error) {
|
|
136
|
+
return new ProcessMessage(line, Date.now() * 1_000_000, error);
|
|
137
|
+
}
|
package/dist/processSocket.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare class ProcessSocket implements AsyncIterable<ProcessFrame> {
|
|
|
14
14
|
connect(): Promise<this>;
|
|
15
15
|
sendJson(payload: ProcessFrame): void;
|
|
16
16
|
sendStdin(data: string | Uint8Array): void;
|
|
17
|
+
closeStdin(): void;
|
|
17
18
|
close(): void;
|
|
18
19
|
[Symbol.asyncIterator](): AsyncIterator<ProcessFrame>;
|
|
19
20
|
private next;
|
|
@@ -23,3 +24,4 @@ export declare class ProcessSocket implements AsyncIterable<ProcessFrame> {
|
|
|
23
24
|
}
|
|
24
25
|
export declare function base64Encode(bytes: Uint8Array): string;
|
|
25
26
|
export declare function base64DecodeText(value: unknown): string;
|
|
27
|
+
export declare function base64DecodeBytes(value: unknown): Uint8Array;
|
package/dist/processSocket.js
CHANGED
|
@@ -55,6 +55,9 @@ export class ProcessSocket {
|
|
|
55
55
|
const raw = typeof data === 'string' ? new TextEncoder().encode(data) : data;
|
|
56
56
|
this.sendJson({ type: 'stdin', data: base64Encode(raw) });
|
|
57
57
|
}
|
|
58
|
+
closeStdin() {
|
|
59
|
+
this.sendJson({ type: 'close_stdin' });
|
|
60
|
+
}
|
|
58
61
|
close() {
|
|
59
62
|
this.closed = true;
|
|
60
63
|
if (this.keepalive)
|
|
@@ -122,12 +125,17 @@ export function base64DecodeText(value) {
|
|
|
122
125
|
if (typeof value !== 'string')
|
|
123
126
|
return String(value ?? '');
|
|
124
127
|
try {
|
|
125
|
-
return Buffer.from(value
|
|
128
|
+
return Buffer.from(base64DecodeBytes(value)).toString('utf8');
|
|
126
129
|
}
|
|
127
130
|
catch {
|
|
128
131
|
return value;
|
|
129
132
|
}
|
|
130
133
|
}
|
|
134
|
+
export function base64DecodeBytes(value) {
|
|
135
|
+
if (typeof value !== 'string')
|
|
136
|
+
return new TextEncoder().encode(String(value ?? ''));
|
|
137
|
+
return new Uint8Array(Buffer.from(value, 'base64'));
|
|
138
|
+
}
|
|
131
139
|
function rawDataToText(message) {
|
|
132
140
|
if (typeof message === 'string')
|
|
133
141
|
return message;
|
package/dist/pty.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CommandHandle, CommandStartOpts } from './commands.js';
|
|
2
|
+
import { ConnectionConfig } from './connectionConfig.js';
|
|
3
|
+
import { DataPlaneClient } from './transport.js';
|
|
4
|
+
export interface PtySize {
|
|
5
|
+
cols: number;
|
|
6
|
+
rows: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PtyCreateOpts extends Omit<CommandStartOpts, 'background' | 'onStdout' | 'onStderr'> {
|
|
9
|
+
cols?: number;
|
|
10
|
+
rows?: number;
|
|
11
|
+
size?: PtySize;
|
|
12
|
+
cmd?: string;
|
|
13
|
+
onData?: (data: Uint8Array) => void | Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export interface PtyConnectOpts {
|
|
16
|
+
onData?: (data: Uint8Array) => void | Promise<void>;
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
requestTimeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
/** PTY helper backed by the sandbox process WebSocket runtime. */
|
|
21
|
+
export declare class Pty {
|
|
22
|
+
private readonly dataPlane;
|
|
23
|
+
private readonly config;
|
|
24
|
+
constructor(dataPlane: DataPlaneClient, config: ConnectionConfig);
|
|
25
|
+
/** Create an interactive shell PTY and return its live command handle. */
|
|
26
|
+
create(opts: PtyCreateOpts): Promise<CommandHandle>;
|
|
27
|
+
/** Connect to a running PTY by pid. */
|
|
28
|
+
connect(pid: number | string, opts?: PtyConnectOpts): Promise<CommandHandle>;
|
|
29
|
+
/** Send input bytes or text to a PTY. */
|
|
30
|
+
sendStdin(pid: number | string, data: string | Uint8Array, opts?: PtyConnectOpts): Promise<void>;
|
|
31
|
+
/** Alias for `sendStdin`. */
|
|
32
|
+
sendInput(pid: number | string, data: string | Uint8Array, opts?: PtyConnectOpts): Promise<void>;
|
|
33
|
+
/** Resize a running PTY. */
|
|
34
|
+
resize(pid: number | string, size: PtySize, opts?: PtyConnectOpts): Promise<void>;
|
|
35
|
+
/** Kill a running PTY. */
|
|
36
|
+
kill(pid: number | string): Promise<boolean>;
|
|
37
|
+
}
|
package/dist/pty.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { CommandHandle } from './commands.js';
|
|
2
|
+
import { ProcessSocket } from './processSocket.js';
|
|
3
|
+
import { SandboxError } from './errors.js';
|
|
4
|
+
/** PTY helper backed by the sandbox process WebSocket runtime. */
|
|
5
|
+
export class Pty {
|
|
6
|
+
dataPlane;
|
|
7
|
+
config;
|
|
8
|
+
constructor(dataPlane, config) {
|
|
9
|
+
this.dataPlane = dataPlane;
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
/** Create an interactive shell PTY and return its live command handle. */
|
|
13
|
+
async create(opts) {
|
|
14
|
+
const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, '/runtime/v1/process', opts.requestTimeoutMs ?? this.config.requestTimeoutMs).connect();
|
|
15
|
+
const envs = { TERM: 'xterm-256color', LANG: 'C.UTF-8', LC_ALL: 'C.UTF-8', ...(opts.envVars ?? opts.envs ?? {}) };
|
|
16
|
+
const size = opts.size ?? { cols: opts.cols ?? 80, rows: opts.rows ?? 24 };
|
|
17
|
+
const args = opts.cmd === undefined ? ['-i', '-l'] : ['-l', '-c', opts.cmd];
|
|
18
|
+
socket.sendJson({
|
|
19
|
+
type: 'start',
|
|
20
|
+
cmd: '/bin/bash',
|
|
21
|
+
args,
|
|
22
|
+
cwd: opts.cwd ?? opts.rootDir,
|
|
23
|
+
user: opts.user,
|
|
24
|
+
environment: envs,
|
|
25
|
+
envs,
|
|
26
|
+
stdin: true,
|
|
27
|
+
pty: { cols: size.cols, rows: size.rows },
|
|
28
|
+
timeout_ms: opts.timeoutMs ?? opts.timeout ?? 60_000,
|
|
29
|
+
});
|
|
30
|
+
const first = await nextStarted(socket);
|
|
31
|
+
const pid = framePid(first);
|
|
32
|
+
if (pid === undefined)
|
|
33
|
+
throw new SandboxError('PTY started frame did not include pid');
|
|
34
|
+
return new CommandHandle(pid, socket, () => this.kill(pid), withFirst(first, socket), undefined, undefined, opts.onData ?? opts.onPty);
|
|
35
|
+
}
|
|
36
|
+
/** Connect to a running PTY by pid. */
|
|
37
|
+
async connect(pid, opts = {}) {
|
|
38
|
+
const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, `/runtime/v1/process/${pid}/connect?since=0`, opts.requestTimeoutMs ?? this.config.requestTimeoutMs).connect();
|
|
39
|
+
const first = await nextStarted(socket);
|
|
40
|
+
const actualPid = framePid(first) ?? pid;
|
|
41
|
+
return new CommandHandle(actualPid, socket, () => this.kill(actualPid), withFirst(first, socket), undefined, undefined, opts.onData);
|
|
42
|
+
}
|
|
43
|
+
/** Send input bytes or text to a PTY. */
|
|
44
|
+
async sendStdin(pid, data, opts = {}) {
|
|
45
|
+
const handle = await this.connect(pid, opts);
|
|
46
|
+
try {
|
|
47
|
+
await handle.sendStdin(data);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await handle.disconnect();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Alias for `sendStdin`. */
|
|
54
|
+
async sendInput(pid, data, opts = {}) {
|
|
55
|
+
return this.sendStdin(pid, data, opts);
|
|
56
|
+
}
|
|
57
|
+
/** Resize a running PTY. */
|
|
58
|
+
async resize(pid, size, opts = {}) {
|
|
59
|
+
const handle = await this.connect(pid, opts);
|
|
60
|
+
try {
|
|
61
|
+
await handle.resize(size);
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await handle.disconnect();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Kill a running PTY. */
|
|
68
|
+
async kill(pid) {
|
|
69
|
+
await this.dataPlane.postJson(`/runtime/v1/process/${pid}/signal`, {
|
|
70
|
+
json: { signal: 'SIGKILL' },
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function nextStarted(events) {
|
|
76
|
+
for await (const frame of events) {
|
|
77
|
+
if (frame.type === 'started')
|
|
78
|
+
return frame;
|
|
79
|
+
}
|
|
80
|
+
throw new SandboxError('PTY ended before started frame');
|
|
81
|
+
}
|
|
82
|
+
async function* withFirst(first, rest) {
|
|
83
|
+
yield first;
|
|
84
|
+
yield* rest;
|
|
85
|
+
}
|
|
86
|
+
function framePid(frame) {
|
|
87
|
+
const process = frame.process && typeof frame.process === 'object' ? frame.process : {};
|
|
88
|
+
const pid = frame.pid ?? process.pid ?? process.id;
|
|
89
|
+
return typeof pid === 'number' || typeof pid === 'string' ? pid : undefined;
|
|
90
|
+
}
|
package/dist/sandbox.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { Commands } from './commands.js';
|
|
|
2
2
|
import { ConnectionConfig, ConnectionOpts } from './connectionConfig.js';
|
|
3
3
|
import { ControlClient } from './transport.js';
|
|
4
4
|
import { Filesystem } from './filesystem.js';
|
|
5
|
+
import { Git } from './git.js';
|
|
6
|
+
import { Pty } from './pty.js';
|
|
7
|
+
import { ProcessManager } from './process.js';
|
|
8
|
+
import { TerminalManager } from './terminal.js';
|
|
5
9
|
export interface SandboxCreateOpts extends ConnectionOpts {
|
|
6
10
|
/** Template slug to create. Defaults to "base". */
|
|
7
11
|
template?: string;
|
|
@@ -11,14 +15,44 @@ export interface SandboxCreateOpts extends ConnectionOpts {
|
|
|
11
15
|
envs?: Record<string, string>;
|
|
12
16
|
secure?: boolean;
|
|
13
17
|
allowInternetAccess?: boolean;
|
|
18
|
+
network?: SandboxNetworkUpdate;
|
|
14
19
|
team?: string;
|
|
15
|
-
mcp
|
|
20
|
+
/** MCP gateway configuration to launch inside an `mcp-gateway` sandbox. */
|
|
21
|
+
mcp?: McpServer;
|
|
16
22
|
volumeMounts?: unknown;
|
|
17
23
|
}
|
|
24
|
+
export type SandboxNetworkSelector = string | string[];
|
|
25
|
+
export interface SandboxNetworkUpdate {
|
|
26
|
+
allowOut?: SandboxNetworkSelector;
|
|
27
|
+
denyOut?: SandboxNetworkSelector;
|
|
28
|
+
allowInternetAccess?: boolean;
|
|
29
|
+
allowPackageRegistryAccess?: boolean;
|
|
30
|
+
allowPublicTraffic?: boolean;
|
|
31
|
+
egressProfile?: string;
|
|
32
|
+
egressProfiles?: string[];
|
|
33
|
+
networkClass?: string;
|
|
34
|
+
rules?: unknown;
|
|
35
|
+
maskRequestHost?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface SandboxNetworkUpdateOpts extends ConnectionOpts {
|
|
38
|
+
}
|
|
18
39
|
export interface SandboxConnectOpts extends ConnectionOpts {
|
|
19
40
|
/** Optional new sandbox lifetime in milliseconds. */
|
|
20
41
|
timeoutMs?: number;
|
|
21
42
|
}
|
|
43
|
+
export interface SandboxListOpts extends ConnectionOpts {
|
|
44
|
+
/** Filters applied by the Watasu API. */
|
|
45
|
+
query?: {
|
|
46
|
+
metadata?: Record<string, string>;
|
|
47
|
+
state?: Array<'running' | 'paused' | string>;
|
|
48
|
+
};
|
|
49
|
+
/** Maximum number of sandboxes to return per page. */
|
|
50
|
+
limit?: number;
|
|
51
|
+
/** Pagination cursor returned by a previous page. */
|
|
52
|
+
nextToken?: string;
|
|
53
|
+
/** Team slug to list within. */
|
|
54
|
+
team?: string;
|
|
55
|
+
}
|
|
22
56
|
export interface SandboxInfo {
|
|
23
57
|
sandboxId: string;
|
|
24
58
|
templateId?: string;
|
|
@@ -47,39 +81,84 @@ export interface SnapshotInfo {
|
|
|
47
81
|
expiresAt?: string;
|
|
48
82
|
raw: Record<string, unknown>;
|
|
49
83
|
}
|
|
84
|
+
export interface FileUrlInfo {
|
|
85
|
+
method: string;
|
|
86
|
+
path: string;
|
|
87
|
+
url: string;
|
|
88
|
+
expiresAt?: string;
|
|
89
|
+
raw: Record<string, unknown>;
|
|
90
|
+
}
|
|
91
|
+
/** MCP gateway configuration accepted by `Sandbox.create({ mcp })`. */
|
|
92
|
+
export type McpServer = Record<string, unknown>;
|
|
93
|
+
/** Name accepted by `Template.addMcpServer`. Watasu keeps this open-ended. */
|
|
94
|
+
export type McpServerName = string;
|
|
95
|
+
export interface SandboxUrlOpts extends ConnectionOpts {
|
|
96
|
+
user?: string;
|
|
97
|
+
useSignatureExpiration?: number;
|
|
98
|
+
expiresInSeconds?: number;
|
|
99
|
+
}
|
|
50
100
|
export interface CreateSnapshotOpts extends ConnectionOpts {
|
|
51
101
|
name?: string;
|
|
52
102
|
metadata?: Record<string, string>;
|
|
53
103
|
expiresAt?: string;
|
|
54
104
|
quiesceMode?: string;
|
|
55
105
|
}
|
|
106
|
+
export interface SnapshotListOpts extends ConnectionOpts {
|
|
107
|
+
/** Filter snapshots by source sandbox id. */
|
|
108
|
+
sandboxId?: string;
|
|
109
|
+
/** Maximum number of snapshots to return per page. */
|
|
110
|
+
limit?: number;
|
|
111
|
+
/** Pagination cursor returned by a previous page. */
|
|
112
|
+
nextToken?: string;
|
|
113
|
+
}
|
|
56
114
|
export interface RestoreSnapshotOpts extends ConnectionOpts {
|
|
57
115
|
checkpointId?: string | number;
|
|
58
116
|
snapshotId?: string | number;
|
|
59
117
|
timeout?: number;
|
|
60
118
|
timeoutMs?: number;
|
|
61
119
|
}
|
|
120
|
+
/** Paginator for listing sandbox snapshots. */
|
|
62
121
|
export declare class SnapshotPaginator {
|
|
63
|
-
private readonly
|
|
64
|
-
|
|
122
|
+
private readonly opts;
|
|
123
|
+
hasNext: boolean;
|
|
124
|
+
nextToken: string | undefined;
|
|
125
|
+
constructor(opts?: SnapshotListOpts);
|
|
126
|
+
/** Fetch the next page of snapshot metadata. */
|
|
127
|
+
nextItems(opts?: ConnectionOpts): Promise<SnapshotInfo[]>;
|
|
128
|
+
/** Drain all remaining pages into one list. */
|
|
129
|
+
listItems(opts?: ConnectionOpts): Promise<SnapshotInfo[]>;
|
|
130
|
+
}
|
|
131
|
+
/** Paginator for listing sandboxes. */
|
|
132
|
+
export declare class SandboxPaginator {
|
|
133
|
+
private readonly opts;
|
|
65
134
|
hasNext: boolean;
|
|
66
135
|
nextToken: string | undefined;
|
|
67
|
-
constructor(
|
|
68
|
-
|
|
136
|
+
constructor(opts?: SandboxListOpts);
|
|
137
|
+
/** Fetch the next page of sandbox metadata. */
|
|
138
|
+
nextItems(opts?: ConnectionOpts): Promise<SandboxInfo[]>;
|
|
139
|
+
/** Drain all remaining pages into one list. */
|
|
140
|
+
listItems(opts?: ConnectionOpts): Promise<SandboxInfo[]>;
|
|
69
141
|
}
|
|
70
142
|
/** Running Watasu sandbox with ready `files` and `commands` helpers. */
|
|
71
143
|
export declare class Sandbox {
|
|
72
144
|
/** Default template slug used when create is called without a template. */
|
|
73
145
|
static readonly defaultTemplate = "base";
|
|
146
|
+
/** Default template slug used by MCP creation once Watasu supports it. */
|
|
147
|
+
static readonly defaultMcpTemplate = "mcp-gateway";
|
|
148
|
+
/** Default sandbox lifetime in milliseconds. */
|
|
149
|
+
static readonly defaultSandboxTimeoutMs = 300000;
|
|
74
150
|
files: Filesystem;
|
|
151
|
+
filesystem: Filesystem;
|
|
75
152
|
commands: Commands;
|
|
153
|
+
process: ProcessManager;
|
|
154
|
+
pty: Pty;
|
|
155
|
+
terminal: TerminalManager;
|
|
156
|
+
git: Git;
|
|
157
|
+
cwd: string | undefined;
|
|
158
|
+
envVars: Record<string, string>;
|
|
76
159
|
readonly sandboxId: string;
|
|
77
|
-
readonly
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
readonly git: {
|
|
81
|
-
clone: () => never;
|
|
82
|
-
};
|
|
160
|
+
private readonly mcpPort;
|
|
161
|
+
private mcpToken;
|
|
83
162
|
private readonly config;
|
|
84
163
|
private readonly control;
|
|
85
164
|
private readonly envs;
|
|
@@ -93,24 +172,40 @@ export declare class Sandbox {
|
|
|
93
172
|
sandbox?: Record<string, unknown>;
|
|
94
173
|
envs?: Record<string, string>;
|
|
95
174
|
});
|
|
175
|
+
/** Sandbox id alias used by SDK-compatible code. */
|
|
176
|
+
get id(): string;
|
|
96
177
|
static create(opts?: SandboxCreateOpts): Promise<Sandbox>;
|
|
97
178
|
static create(template: string, opts?: SandboxCreateOpts): Promise<Sandbox>;
|
|
98
179
|
/** Connect to an existing sandbox and return it with a fresh data-plane session. */
|
|
99
180
|
static connect(sandboxId: string, opts?: SandboxConnectOpts): Promise<Sandbox>;
|
|
181
|
+
/** Alias for `connect`. */
|
|
182
|
+
static reconnect(sandboxId: string, opts?: SandboxConnectOpts): Promise<Sandbox>;
|
|
183
|
+
static reconnect(opts: SandboxConnectOpts & {
|
|
184
|
+
sandboxID: string;
|
|
185
|
+
}): Promise<Sandbox>;
|
|
100
186
|
/** Refresh this sandbox's data-plane session in place. */
|
|
101
187
|
connect(opts?: SandboxConnectOpts): Promise<this>;
|
|
188
|
+
/** Resume a paused sandbox by id. */
|
|
189
|
+
static resume(sandboxId: string, opts?: SandboxConnectOpts): Promise<boolean>;
|
|
190
|
+
/** Pause a sandbox by id. Returns false when it was already paused. */
|
|
191
|
+
static betaPause(sandboxId: string, opts?: ConnectionOpts): Promise<boolean>;
|
|
192
|
+
/** Alias for `betaPause`. */
|
|
193
|
+
static pause(sandboxId: string, opts?: ConnectionOpts): Promise<boolean>;
|
|
102
194
|
/** Destroy a sandbox by id. */
|
|
103
|
-
static kill(sandboxId: string, opts?: ConnectionOpts): Promise<boolean>;
|
|
195
|
+
static kill(sandboxId: string, opts?: ConnectionOpts | string): Promise<boolean>;
|
|
104
196
|
/** Fetch sandbox metrics by id. */
|
|
105
197
|
static getMetrics(sandboxId: string, opts?: ConnectionOpts): Promise<SandboxMetrics[]>;
|
|
198
|
+
/** Atomically replace a sandbox's network egress policy by id. */
|
|
199
|
+
static updateNetwork(sandboxId: string, network: SandboxNetworkUpdate, opts?: SandboxNetworkUpdateOpts): Promise<void>;
|
|
200
|
+
private static putNetwork;
|
|
106
201
|
/** Deprecated alias for `getInfo`. */
|
|
107
202
|
static getFullInfo(sandboxId: string, opts?: ConnectionOpts): Promise<SandboxInfo>;
|
|
108
203
|
/** Create a Watasu checkpoint using snapshot naming. */
|
|
109
204
|
static createSnapshot(sandboxId: string, opts?: CreateSnapshotOpts): Promise<SnapshotInfo>;
|
|
110
|
-
/** List
|
|
111
|
-
static listSnapshots(
|
|
112
|
-
/**
|
|
113
|
-
static deleteSnapshot(
|
|
205
|
+
/** List snapshots visible to the configured API key. */
|
|
206
|
+
static listSnapshots(opts?: SnapshotListOpts): SnapshotPaginator;
|
|
207
|
+
/** Delete a snapshot by id. Returns `false` when the snapshot does not exist. */
|
|
208
|
+
static deleteSnapshot(snapshotId: string, opts?: ConnectionOpts): Promise<boolean>;
|
|
114
209
|
/** Destroy this sandbox. */
|
|
115
210
|
kill(): Promise<boolean>;
|
|
116
211
|
/** Check if this sandbox is in a runtime-active lifecycle state. */
|
|
@@ -119,6 +214,8 @@ export declare class Sandbox {
|
|
|
119
214
|
static setTimeout(sandboxId: string, timeoutMs: number, opts?: ConnectionOpts): Promise<void>;
|
|
120
215
|
/** Set this sandbox's lifetime. */
|
|
121
216
|
setTimeout(timeoutMs: number): Promise<void>;
|
|
217
|
+
/** Keep the sandbox alive for `duration` milliseconds. */
|
|
218
|
+
keepAlive(duration: number): Promise<void>;
|
|
122
219
|
/** Fetch control-plane metadata for a sandbox by id. */
|
|
123
220
|
static getInfo(sandboxId: string, opts?: ConnectionOpts): Promise<SandboxInfo>;
|
|
124
221
|
/** Fetch the latest control-plane metadata for this sandbox. */
|
|
@@ -127,21 +224,44 @@ export declare class Sandbox {
|
|
|
127
224
|
getMetrics(opts?: ConnectionOpts): Promise<SandboxMetrics[]>;
|
|
128
225
|
/** Create a Watasu checkpoint using snapshot naming. */
|
|
129
226
|
createSnapshot(opts?: CreateSnapshotOpts): Promise<SnapshotInfo>;
|
|
227
|
+
/** Delete a snapshot by id. */
|
|
228
|
+
deleteSnapshot(snapshotId: string, opts?: ConnectionOpts): Promise<boolean>;
|
|
130
229
|
/** Watasu-native alias for `createSnapshot`. */
|
|
131
230
|
checkpoint(opts?: CreateSnapshotOpts): Promise<SnapshotInfo>;
|
|
132
231
|
/** List checkpoints for this sandbox using snapshot naming. */
|
|
133
|
-
listSnapshots(opts?:
|
|
232
|
+
listSnapshots(opts?: Omit<SnapshotListOpts, 'sandboxId'>): SnapshotPaginator;
|
|
134
233
|
/** Restore a checkpoint into a new sandbox and return its control-plane info. */
|
|
135
234
|
restore(opts?: RestoreSnapshotOpts | string | number): Promise<SandboxInfo>;
|
|
136
|
-
/**
|
|
137
|
-
static list(opts?:
|
|
138
|
-
team?: string;
|
|
139
|
-
}): Promise<SandboxInfo[]>;
|
|
235
|
+
/** Return a paginator for sandboxes visible to the configured API key. */
|
|
236
|
+
static list(opts?: SandboxListOpts | string): SandboxPaginator;
|
|
140
237
|
/** Return the public hostname for an exposed sandbox port. */
|
|
141
238
|
getHost(port: number): string;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
239
|
+
/** Return the public hostname for the sandbox or an exposed sandbox port. */
|
|
240
|
+
getHostname(port?: number): string;
|
|
241
|
+
/** Return the conventional MCP URL for this sandbox. */
|
|
242
|
+
getMcpUrl(): string;
|
|
243
|
+
/** Return the MCP gateway token when the sandbox contains one. */
|
|
244
|
+
getMcpToken(): Promise<string | undefined>;
|
|
245
|
+
/** Return a protocol string for a secure or insecure sandbox URL. */
|
|
246
|
+
getProtocol(baseProtocol?: string, secure?: boolean): string;
|
|
247
|
+
/** Close the local SDK attachment. This does not destroy the sandbox. */
|
|
248
|
+
close(): Promise<void>;
|
|
249
|
+
/** Get a signed URL that accepts a POST upload for a sandbox file path. */
|
|
250
|
+
uploadUrl(path?: string, opts?: SandboxUrlOpts): Promise<string>;
|
|
251
|
+
/** Get a signed URL that accepts a GET download for a sandbox file path. */
|
|
252
|
+
downloadUrl(path: string, opts?: SandboxUrlOpts): Promise<string>;
|
|
253
|
+
/** Get signed upload URL metadata for a sandbox file path. */
|
|
254
|
+
uploadUrlInfo(path?: string, opts?: SandboxUrlOpts): Promise<FileUrlInfo>;
|
|
255
|
+
/** Get signed download URL metadata for a sandbox file path. */
|
|
256
|
+
downloadUrlInfo(path: string, opts?: SandboxUrlOpts): Promise<FileUrlInfo>;
|
|
257
|
+
/** Atomically replace this sandbox's network egress policy. */
|
|
258
|
+
updateNetwork(network: SandboxNetworkUpdate, opts?: SandboxNetworkUpdateOpts): Promise<void>;
|
|
259
|
+
/** Pause this sandbox. Returns false when it was already paused. */
|
|
260
|
+
betaPause(opts?: ConnectionOpts): Promise<boolean>;
|
|
261
|
+
/** Alias for `betaPause`. */
|
|
262
|
+
pause(opts?: ConnectionOpts): Promise<boolean>;
|
|
263
|
+
/** Resume this sandbox and refresh its data-plane session. */
|
|
264
|
+
resume(opts?: SandboxConnectOpts): Promise<boolean>;
|
|
265
|
+
private fileUrl;
|
|
146
266
|
private configOptions;
|
|
147
267
|
}
|