@watasu/sdk 0.1.66 → 0.1.68
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/dist/codeInterpreter.d.ts +1 -1
- package/dist/commands.d.ts +61 -3
- package/dist/commands.js +160 -12
- package/dist/index.d.ts +1 -1
- package/dist/pty.d.ts +1 -0
- package/dist/pty.js +19 -7
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ export { ControlClient as ApiClient } from './transport.js';
|
|
|
6
6
|
export { ALL_TRAFFIC, SandboxPaginator, SnapshotPaginator, getSignature, } from './sandbox.js';
|
|
7
7
|
export type { CreateSnapshotOpts, FileUrlInfo, McpServer, McpServerName, RestoreSnapshotOpts, SandboxApiOpts, SandboxConnectOpts, SandboxCreateOpts, SandboxInfo, SandboxInfoLifecycle, SandboxLifecycle, SandboxListOpts, SandboxMetrics, SandboxMetricsOpts, SandboxNetworkInfo, SandboxNetworkOpts, SandboxNetworkRule, SandboxNetworkRuleInfo, SandboxNetworkRules, SandboxNetworkSelector, SandboxNetworkSelectorContext, SandboxNetworkTransform, SandboxNetworkUpdate, SandboxNetworkUpdateOpts, SandboxOpts, SandboxState, SandboxUrlOpts, SignatureOpts, SnapshotInfo, SnapshotListOpts, } from './sandbox.js';
|
|
8
8
|
export { CommandExitError, CommandHandle, Commands } from './commands.js';
|
|
9
|
-
export type { CommandConnectOpts, CommandRequestOpts, CommandResult, CommandStartOpts, ProcessInfo, PtyOutput, Stderr, Stdout, } from './commands.js';
|
|
9
|
+
export type { CommandConnectOpts, CommandRequestOpts, CommandResult, CommandStartOpts, ProcessInfo, ProcessOutputEvent, ProcessOutputSnapshot, ProcessStatus, PtyOutput, ReadProcessOutputOptions, Stderr, StopProcessOptions, Stdout, } from './commands.js';
|
|
10
10
|
export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
|
|
11
11
|
export type { ProcessOpts } from './process.js';
|
|
12
12
|
export { FileType, Filesystem, FilesystemEventType, FilesystemWatcher, WatchHandle, } from './filesystem.js';
|
package/dist/commands.d.ts
CHANGED
|
@@ -26,6 +26,45 @@ export interface ProcessInfo {
|
|
|
26
26
|
envs: Record<string, string>;
|
|
27
27
|
cwd?: string;
|
|
28
28
|
}
|
|
29
|
+
export interface ProcessStatus {
|
|
30
|
+
pid: number | string;
|
|
31
|
+
id?: number | string;
|
|
32
|
+
osPid?: number;
|
|
33
|
+
command?: string;
|
|
34
|
+
args: string[];
|
|
35
|
+
cwd?: string;
|
|
36
|
+
user?: string;
|
|
37
|
+
pty?: boolean;
|
|
38
|
+
status: string;
|
|
39
|
+
startedAt?: string;
|
|
40
|
+
finishedAt?: string;
|
|
41
|
+
exitCode?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface ProcessOutputEvent {
|
|
44
|
+
cursor: number;
|
|
45
|
+
type: 'stdout' | 'stderr' | 'pty' | string;
|
|
46
|
+
data: Uint8Array;
|
|
47
|
+
}
|
|
48
|
+
export interface ProcessOutputSnapshot {
|
|
49
|
+
pid: number | string;
|
|
50
|
+
status: string;
|
|
51
|
+
exitCode?: number;
|
|
52
|
+
finishedAt?: string;
|
|
53
|
+
nextCursor: number;
|
|
54
|
+
truncatedBeforeCursor: boolean;
|
|
55
|
+
events: ProcessOutputEvent[];
|
|
56
|
+
}
|
|
57
|
+
export interface ReadProcessOutputOptions extends CommandRequestOpts {
|
|
58
|
+
since?: number;
|
|
59
|
+
limitBytes?: number;
|
|
60
|
+
}
|
|
61
|
+
export interface StopProcessOptions {
|
|
62
|
+
signal?: string;
|
|
63
|
+
killGroup?: boolean;
|
|
64
|
+
graceMs?: number;
|
|
65
|
+
requestTimeoutMs?: number;
|
|
66
|
+
abortSignal?: AbortSignal;
|
|
67
|
+
}
|
|
29
68
|
export interface CommandStartOpts {
|
|
30
69
|
/** Return a `CommandHandle` immediately instead of waiting for exit. */
|
|
31
70
|
background?: boolean;
|
|
@@ -55,21 +94,28 @@ export interface CommandConnectOpts extends CommandRequestOpts {
|
|
|
55
94
|
export type Stdout = string;
|
|
56
95
|
export type Stderr = string;
|
|
57
96
|
export type PtyOutput = Uint8Array;
|
|
97
|
+
type ProcessReconnect = (cursor: number) => Promise<{
|
|
98
|
+
socket: ProcessSocket;
|
|
99
|
+
events: AsyncIterable<ProcessFrame>;
|
|
100
|
+
}>;
|
|
58
101
|
/** Live handle for one sandbox process stream. */
|
|
59
102
|
export declare class CommandHandle implements Partial<CommandResult> {
|
|
60
103
|
readonly pid: number | string;
|
|
61
|
-
private
|
|
104
|
+
private socket;
|
|
62
105
|
private readonly handleKill;
|
|
63
|
-
private
|
|
106
|
+
private events;
|
|
64
107
|
private readonly onStdout?;
|
|
65
108
|
private readonly onStderr?;
|
|
66
109
|
private readonly onPty?;
|
|
67
110
|
private readonly onExit?;
|
|
111
|
+
private readonly reconnect?;
|
|
68
112
|
private _stdout;
|
|
69
113
|
private _stderr;
|
|
70
114
|
private result?;
|
|
71
115
|
private readonly pending;
|
|
72
|
-
|
|
116
|
+
private nextCursor;
|
|
117
|
+
private disconnected;
|
|
118
|
+
constructor(pid: number | string, socket: ProcessSocket, handleKill: () => Promise<boolean>, events: AsyncIterable<ProcessFrame>, onStdout?: ((data: string) => void | Promise<void>) | undefined, onStderr?: ((data: string) => void | Promise<void>) | undefined, onPty?: ((data: Uint8Array) => void | Promise<void>) | undefined, onExit?: ((exitCode: number) => void | Promise<void>) | undefined, reconnect?: ProcessReconnect | undefined);
|
|
73
119
|
get stdout(): string;
|
|
74
120
|
get stderr(): string;
|
|
75
121
|
get exitCode(): number | undefined;
|
|
@@ -90,6 +136,8 @@ export declare class CommandHandle implements Partial<CommandResult> {
|
|
|
90
136
|
/** Detach the local stream without killing the process. */
|
|
91
137
|
disconnect(): Promise<void>;
|
|
92
138
|
private handleEvents;
|
|
139
|
+
private advanceCursor;
|
|
140
|
+
private reconnectStream;
|
|
93
141
|
}
|
|
94
142
|
/** Command runner for a sandbox data-plane session. */
|
|
95
143
|
export declare class Commands {
|
|
@@ -125,6 +173,16 @@ export declare class Commands {
|
|
|
125
173
|
run(cmd: string, opts?: CommandStartOpts): Promise<CommandResult>;
|
|
126
174
|
/** Reconnect to a live process stream by pid. */
|
|
127
175
|
connect(pid: number | string, opts?: CommandStartOpts): Promise<CommandHandle>;
|
|
176
|
+
/** Reconnect to a live process stream by pid starting at a cursor. */
|
|
177
|
+
connectSince(pid: number | string, cursor?: number, opts?: CommandStartOpts): Promise<CommandHandle>;
|
|
178
|
+
/** Look up process status without attaching a WebSocket. */
|
|
179
|
+
process(pid: number | string, opts?: CommandRequestOpts): Promise<ProcessStatus>;
|
|
180
|
+
/** Read available process output since a cursor without blocking. */
|
|
181
|
+
readProcessOutput(pid: number | string, opts?: ReadProcessOutputOptions): Promise<ProcessOutputSnapshot>;
|
|
182
|
+
/** Stop a process, optionally signalling the full process group. */
|
|
183
|
+
stopProcess(pid: number | string, opts?: StopProcessOptions): Promise<ProcessStatus>;
|
|
128
184
|
/** Start a command and return a live handle immediately. */
|
|
129
185
|
start(cmd: string, opts?: CommandStartOpts): Promise<CommandHandle>;
|
|
186
|
+
private openProcessStream;
|
|
130
187
|
}
|
|
188
|
+
export {};
|
package/dist/commands.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withQuery } from './transport.js';
|
|
1
2
|
import { ProcessSocket, base64DecodeBytes, base64DecodeText } from './processSocket.js';
|
|
2
3
|
import { SandboxError, TimeoutError } from './errors.js';
|
|
3
4
|
/** Error thrown by `CommandHandle.wait()` when a process exits non-zero. */
|
|
@@ -13,6 +14,9 @@ export class CommandExitError extends SandboxError {
|
|
|
13
14
|
get stdout() { return this.result.stdout; }
|
|
14
15
|
get stderr() { return this.result.stderr; }
|
|
15
16
|
}
|
|
17
|
+
const STREAM_RECONNECT_ATTEMPTS = 12;
|
|
18
|
+
const STREAM_RECONNECT_BASE_DELAY_MS = 250;
|
|
19
|
+
const STREAM_RECONNECT_MAX_DELAY_MS = 2_000;
|
|
16
20
|
/** Live handle for one sandbox process stream. */
|
|
17
21
|
export class CommandHandle {
|
|
18
22
|
pid;
|
|
@@ -23,11 +27,14 @@ export class CommandHandle {
|
|
|
23
27
|
onStderr;
|
|
24
28
|
onPty;
|
|
25
29
|
onExit;
|
|
30
|
+
reconnect;
|
|
26
31
|
_stdout = '';
|
|
27
32
|
_stderr = '';
|
|
28
33
|
result;
|
|
29
34
|
pending;
|
|
30
|
-
|
|
35
|
+
nextCursor = 0;
|
|
36
|
+
disconnected = false;
|
|
37
|
+
constructor(pid, socket, handleKill, events, onStdout, onStderr, onPty, onExit, reconnect) {
|
|
31
38
|
this.pid = pid;
|
|
32
39
|
this.socket = socket;
|
|
33
40
|
this.handleKill = handleKill;
|
|
@@ -36,6 +43,7 @@ export class CommandHandle {
|
|
|
36
43
|
this.onStderr = onStderr;
|
|
37
44
|
this.onPty = onPty;
|
|
38
45
|
this.onExit = onExit;
|
|
46
|
+
this.reconnect = reconnect;
|
|
39
47
|
this.pending = this.handleEvents();
|
|
40
48
|
}
|
|
41
49
|
get stdout() { return this._stdout; }
|
|
@@ -69,11 +77,14 @@ export class CommandHandle {
|
|
|
69
77
|
}
|
|
70
78
|
/** Detach the local stream without killing the process. */
|
|
71
79
|
async disconnect() {
|
|
80
|
+
this.disconnected = true;
|
|
72
81
|
this.socket.close();
|
|
73
82
|
}
|
|
74
83
|
async handleEvents() {
|
|
75
|
-
|
|
84
|
+
while (!this.disconnected && !this.result) {
|
|
85
|
+
let streamError;
|
|
76
86
|
for await (const frame of this.events) {
|
|
87
|
+
this.advanceCursor(frame);
|
|
77
88
|
const type = frame.type;
|
|
78
89
|
if (type === 'started' || type === 'ready' || type === 'pong')
|
|
79
90
|
continue;
|
|
@@ -102,16 +113,51 @@ export class CommandHandle {
|
|
|
102
113
|
stderr: this._stderr,
|
|
103
114
|
};
|
|
104
115
|
await this.onExit?.(exitCode);
|
|
116
|
+
this.socket.close();
|
|
105
117
|
return;
|
|
106
118
|
}
|
|
107
119
|
else if (type === 'error') {
|
|
108
|
-
|
|
120
|
+
streamError = new SandboxError(String(frame.message ?? frame.code ?? 'process error'));
|
|
121
|
+
if (!isReconnectableStreamError(streamError))
|
|
122
|
+
throw streamError;
|
|
123
|
+
break;
|
|
109
124
|
}
|
|
110
125
|
}
|
|
126
|
+
if (this.result || this.disconnected)
|
|
127
|
+
return;
|
|
128
|
+
if (!this.reconnect) {
|
|
129
|
+
this.socket.close();
|
|
130
|
+
if (streamError)
|
|
131
|
+
throw streamError;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
await this.reconnectStream();
|
|
111
135
|
}
|
|
112
|
-
|
|
136
|
+
}
|
|
137
|
+
advanceCursor(frame) {
|
|
138
|
+
const cursor = numberValue(frame.cursor);
|
|
139
|
+
if (cursor !== undefined)
|
|
140
|
+
this.nextCursor = Math.max(this.nextCursor, cursor + 1);
|
|
141
|
+
}
|
|
142
|
+
async reconnectStream() {
|
|
143
|
+
let lastError;
|
|
144
|
+
for (let attempt = 0; attempt < STREAM_RECONNECT_ATTEMPTS && !this.disconnected; attempt += 1) {
|
|
113
145
|
this.socket.close();
|
|
146
|
+
if (attempt > 0)
|
|
147
|
+
await sleep(reconnectDelayMs(attempt));
|
|
148
|
+
try {
|
|
149
|
+
const next = await this.reconnect(this.nextCursor);
|
|
150
|
+
this.socket = next.socket;
|
|
151
|
+
this.events = next.events;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
lastError = error;
|
|
156
|
+
}
|
|
114
157
|
}
|
|
158
|
+
if (lastError instanceof Error)
|
|
159
|
+
throw lastError;
|
|
160
|
+
throw new SandboxError('process websocket closed before exit and could not reconnect');
|
|
115
161
|
}
|
|
116
162
|
}
|
|
117
163
|
/** Command runner for a sandbox data-plane session. */
|
|
@@ -136,9 +182,10 @@ export class Commands {
|
|
|
136
182
|
}
|
|
137
183
|
/** Send SIGKILL to a process by pid. */
|
|
138
184
|
async kill(pid, opts = {}) {
|
|
139
|
-
await this.
|
|
140
|
-
|
|
141
|
-
|
|
185
|
+
await this.stopProcess(pid, {
|
|
186
|
+
signal: 'SIGKILL',
|
|
187
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
188
|
+
abortSignal: opts.signal,
|
|
142
189
|
});
|
|
143
190
|
return true;
|
|
144
191
|
}
|
|
@@ -171,10 +218,38 @@ export class Commands {
|
|
|
171
218
|
}
|
|
172
219
|
/** Reconnect to a live process stream by pid. */
|
|
173
220
|
async connect(pid, opts = {}) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
221
|
+
return this.connectSince(pid, 0, opts);
|
|
222
|
+
}
|
|
223
|
+
/** Reconnect to a live process stream by pid starting at a cursor. */
|
|
224
|
+
async connectSince(pid, cursor = 0, opts = {}) {
|
|
225
|
+
const stream = await this.openProcessStream(pid, cursor, opts);
|
|
226
|
+
const reconnect = async (nextCursor) => this.openProcessStream(stream.actualPid, nextCursor, opts);
|
|
227
|
+
return new CommandHandle(stream.actualPid, stream.socket, () => this.kill(stream.actualPid), stream.events, opts.onStdout, opts.onStderr, opts.onPty, undefined, reconnect);
|
|
228
|
+
}
|
|
229
|
+
/** Look up process status without attaching a WebSocket. */
|
|
230
|
+
async process(pid, opts = {}) {
|
|
231
|
+
const payload = await this.dataPlane.getJson(`/runtime/v1/process/${encodeURIComponent(String(pid))}`, opts);
|
|
232
|
+
return processStatus(payload);
|
|
233
|
+
}
|
|
234
|
+
/** Read available process output since a cursor without blocking. */
|
|
235
|
+
async readProcessOutput(pid, opts = {}) {
|
|
236
|
+
const payload = await this.dataPlane.getJson(withQuery(`/runtime/v1/process/${encodeURIComponent(String(pid))}/output`, {
|
|
237
|
+
since: opts.since,
|
|
238
|
+
limit_bytes: opts.limitBytes,
|
|
239
|
+
}), opts);
|
|
240
|
+
return processOutputSnapshot(payload);
|
|
241
|
+
}
|
|
242
|
+
/** Stop a process, optionally signalling the full process group. */
|
|
243
|
+
async stopProcess(pid, opts = {}) {
|
|
244
|
+
const payload = await this.dataPlane.deleteJson(withQuery(`/runtime/v1/process/${encodeURIComponent(String(pid))}`, {
|
|
245
|
+
signal: opts.signal,
|
|
246
|
+
kill_group: opts.killGroup ?? true,
|
|
247
|
+
grace_ms: opts.graceMs,
|
|
248
|
+
}), {
|
|
249
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
250
|
+
signal: opts.abortSignal,
|
|
251
|
+
});
|
|
252
|
+
return processStatus(payload);
|
|
178
253
|
}
|
|
179
254
|
/** Start a command and return a live handle immediately. */
|
|
180
255
|
async start(cmd, opts = {}) {
|
|
@@ -197,7 +272,18 @@ export class Commands {
|
|
|
197
272
|
const pid = framePid(first);
|
|
198
273
|
if (pid === undefined)
|
|
199
274
|
throw new SandboxError('process started frame did not include pid');
|
|
200
|
-
|
|
275
|
+
const reconnect = async (nextCursor) => this.openProcessStream(pid, nextCursor, opts);
|
|
276
|
+
return new CommandHandle(pid, socket, () => this.kill(pid), withFirst(first, socket), opts.onStdout, opts.onStderr, opts.onPty, opts.onExit, reconnect);
|
|
277
|
+
}
|
|
278
|
+
async openProcessStream(pid, cursor, opts = {}) {
|
|
279
|
+
const encodedPid = encodeURIComponent(String(pid));
|
|
280
|
+
const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, withQuery(`/runtime/v1/process/${encodedPid}/connect`, { since: cursor }), opts.requestTimeoutMs ?? this.config.requestTimeoutMs, this.config.headers).connect();
|
|
281
|
+
const first = await nextStarted(socket);
|
|
282
|
+
return {
|
|
283
|
+
actualPid: framePid(first) ?? pid,
|
|
284
|
+
socket,
|
|
285
|
+
events: socket,
|
|
286
|
+
};
|
|
201
287
|
}
|
|
202
288
|
}
|
|
203
289
|
function processStartConfig(cmd, opts) {
|
|
@@ -214,6 +300,15 @@ function waitFor(promise, timeoutMs) {
|
|
|
214
300
|
promise.then(resolve, reject).finally(() => clearTimeout(timer));
|
|
215
301
|
});
|
|
216
302
|
}
|
|
303
|
+
function sleep(ms) {
|
|
304
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
305
|
+
}
|
|
306
|
+
function reconnectDelayMs(attempt) {
|
|
307
|
+
return Math.min(STREAM_RECONNECT_MAX_DELAY_MS, STREAM_RECONNECT_BASE_DELAY_MS * 2 ** Math.max(0, attempt - 1));
|
|
308
|
+
}
|
|
309
|
+
function isReconnectableStreamError(error) {
|
|
310
|
+
return error instanceof Error && /websocket|closed/i.test(error.message);
|
|
311
|
+
}
|
|
217
312
|
async function nextStarted(events) {
|
|
218
313
|
for await (const frame of events) {
|
|
219
314
|
if (frame.type === 'started')
|
|
@@ -243,8 +338,61 @@ function processInfo(value) {
|
|
|
243
338
|
cwd: typeof process.cwd === 'string' ? process.cwd : undefined,
|
|
244
339
|
};
|
|
245
340
|
}
|
|
341
|
+
function processStatus(value) {
|
|
342
|
+
const item = value && typeof value === 'object' ? value : {};
|
|
343
|
+
const process = item.process && typeof item.process === 'object' ? item.process : item;
|
|
344
|
+
const pid = scalar(process.pid ?? process.id) ?? '';
|
|
345
|
+
return {
|
|
346
|
+
pid,
|
|
347
|
+
id: scalar(process.id),
|
|
348
|
+
osPid: numberValue(process.os_pid ?? process.osPid),
|
|
349
|
+
command: stringValue(process.command ?? process.cmd),
|
|
350
|
+
args: arrayOfStrings(process.args ?? process.arguments),
|
|
351
|
+
cwd: stringValue(process.cwd ?? process.working_directory),
|
|
352
|
+
user: stringValue(process.user),
|
|
353
|
+
pty: typeof process.pty === 'boolean' ? process.pty : undefined,
|
|
354
|
+
status: stringValue(process.status) ?? '',
|
|
355
|
+
startedAt: stringValue(process.started_at ?? process.startedAt),
|
|
356
|
+
finishedAt: stringValue(process.finished_at ?? process.finishedAt),
|
|
357
|
+
exitCode: numberValue(process.exit_code ?? process.exitCode),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function processOutputSnapshot(value) {
|
|
361
|
+
const payload = value && typeof value === 'object' ? value : {};
|
|
362
|
+
return {
|
|
363
|
+
pid: scalar(payload.pid ?? payload.id) ?? '',
|
|
364
|
+
status: stringValue(payload.status) ?? '',
|
|
365
|
+
exitCode: numberValue(payload.exit_code ?? payload.exitCode),
|
|
366
|
+
finishedAt: stringValue(payload.finished_at ?? payload.finishedAt),
|
|
367
|
+
nextCursor: numberValue(payload.next_cursor ?? payload.nextCursor) ?? 0,
|
|
368
|
+
truncatedBeforeCursor: payload.truncated_before_cursor === true || payload.truncatedBeforeCursor === true,
|
|
369
|
+
events: Array.isArray(payload.events) ? payload.events.map(processOutputEvent) : [],
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function processOutputEvent(value) {
|
|
373
|
+
const event = value && typeof value === 'object' ? value : {};
|
|
374
|
+
return {
|
|
375
|
+
cursor: numberValue(event.cursor) ?? 0,
|
|
376
|
+
type: stringValue(event.type) ?? '',
|
|
377
|
+
data: base64DecodeBytes(stringValue(event.data) ?? ''),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
246
380
|
function recordOfStrings(value) {
|
|
247
381
|
if (!value || typeof value !== 'object')
|
|
248
382
|
return {};
|
|
249
383
|
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, String(item)]));
|
|
250
384
|
}
|
|
385
|
+
function scalar(value) {
|
|
386
|
+
return typeof value === 'number' || typeof value === 'string' ? value : undefined;
|
|
387
|
+
}
|
|
388
|
+
function stringValue(value) {
|
|
389
|
+
return typeof value === 'string' ? value : undefined;
|
|
390
|
+
}
|
|
391
|
+
function numberValue(value) {
|
|
392
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
393
|
+
return value;
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
function arrayOfStrings(value) {
|
|
397
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
398
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxApiOpts, SandboxCr
|
|
|
8
8
|
export type { CreateCodeContextOpts, RunCodeLanguage, RunCodeOpts, } from './codeInterpreter.js';
|
|
9
9
|
export { Context as CodeInterpreterContext, Execution as CodeInterpreterExecution, ExecutionError as CodeInterpreterExecutionError, OutputMessage as CodeInterpreterOutputMessage, Result as CodeInterpreterResult, } from './codeInterpreter.js';
|
|
10
10
|
export { CommandExitError, CommandHandle, Commands } from './commands.js';
|
|
11
|
-
export type { CommandConnectOpts, CommandRequestOpts, CommandResult, CommandStartOpts, ProcessInfo, PtyOutput, Stderr, Stdout, } from './commands.js';
|
|
11
|
+
export type { CommandConnectOpts, CommandRequestOpts, CommandResult, CommandStartOpts, ProcessInfo, ProcessOutputEvent, ProcessOutputSnapshot, ProcessStatus, PtyOutput, ReadProcessOutputOptions, Stderr, StopProcessOptions, Stdout, } from './commands.js';
|
|
12
12
|
export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
|
|
13
13
|
export type { ProcessOpts } from './process.js';
|
|
14
14
|
export { FileType, Filesystem, FilesystemEventType, FilesystemWatcher, WatchHandle, } from './filesystem.js';
|
package/dist/pty.d.ts
CHANGED
|
@@ -39,4 +39,5 @@ export declare class Pty {
|
|
|
39
39
|
resize(pid: number | string, size: PtySize, opts?: PtyConnectOpts): Promise<void>;
|
|
40
40
|
/** Kill a running PTY. */
|
|
41
41
|
kill(pid: number | string, opts?: Pick<ConnectionOpts, 'requestTimeoutMs' | 'signal'>): Promise<boolean>;
|
|
42
|
+
private openPtyStream;
|
|
42
43
|
}
|
package/dist/pty.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CommandHandle } from './commands.js';
|
|
2
2
|
import { ProcessSocket } from './processSocket.js';
|
|
3
|
+
import { withQuery } from './transport.js';
|
|
3
4
|
import { SandboxError } from './errors.js';
|
|
4
5
|
/** PTY helper backed by the sandbox process WebSocket runtime. */
|
|
5
6
|
export class Pty {
|
|
@@ -31,14 +32,14 @@ export class Pty {
|
|
|
31
32
|
const pid = framePid(first);
|
|
32
33
|
if (pid === undefined)
|
|
33
34
|
throw new SandboxError('PTY started frame did not include pid');
|
|
34
|
-
|
|
35
|
+
const reconnect = async (cursor) => this.openPtyStream(pid, cursor, opts);
|
|
36
|
+
return new CommandHandle(pid, socket, () => this.kill(pid), withFirst(first, socket), undefined, undefined, opts.onData ?? opts.onPty, undefined, reconnect);
|
|
35
37
|
}
|
|
36
38
|
/** Connect to a running PTY by pid. */
|
|
37
39
|
async connect(pid, opts = {}) {
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
return new CommandHandle(actualPid, socket, () => this.kill(actualPid), withFirst(first, socket), undefined, undefined, opts.onData);
|
|
40
|
+
const stream = await this.openPtyStream(pid, 0, opts);
|
|
41
|
+
const reconnect = async (cursor) => this.openPtyStream(stream.actualPid, cursor, opts);
|
|
42
|
+
return new CommandHandle(stream.actualPid, stream.socket, () => this.kill(stream.actualPid), stream.events, undefined, undefined, opts.onData, undefined, reconnect);
|
|
42
43
|
}
|
|
43
44
|
/** Send input bytes or text to a PTY. */
|
|
44
45
|
async sendStdin(pid, data, opts = {}) {
|
|
@@ -66,13 +67,24 @@ export class Pty {
|
|
|
66
67
|
}
|
|
67
68
|
/** Kill a running PTY. */
|
|
68
69
|
async kill(pid, opts = {}) {
|
|
69
|
-
await this.dataPlane.
|
|
70
|
-
|
|
70
|
+
await this.dataPlane.deleteJson(withQuery(`/runtime/v1/process/${encodeURIComponent(String(pid))}`, {
|
|
71
|
+
signal: 'SIGKILL',
|
|
72
|
+
kill_group: true,
|
|
73
|
+
}), {
|
|
71
74
|
requestTimeoutMs: opts.requestTimeoutMs,
|
|
72
75
|
signal: opts.signal,
|
|
73
76
|
});
|
|
74
77
|
return true;
|
|
75
78
|
}
|
|
79
|
+
async openPtyStream(pid, cursor, opts = {}) {
|
|
80
|
+
const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, withQuery(`/runtime/v1/process/${encodeURIComponent(String(pid))}/connect`, { since: cursor }), opts.requestTimeoutMs ?? this.config.requestTimeoutMs, this.config.headers).connect();
|
|
81
|
+
const first = await nextStarted(socket);
|
|
82
|
+
return {
|
|
83
|
+
actualPid: framePid(first) ?? pid,
|
|
84
|
+
socket,
|
|
85
|
+
events: withFirst(first, socket),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
76
88
|
}
|
|
77
89
|
async function nextStarted(events) {
|
|
78
90
|
for await (const frame of events) {
|