@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.
@@ -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';
@@ -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 readonly socket;
104
+ private socket;
62
105
  private readonly handleKill;
63
- private readonly events;
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
- 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);
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
- constructor(pid, socket, handleKill, events, onStdout, onStderr, onPty, onExit) {
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
- try {
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
- throw new SandboxError(String(frame.message ?? frame.code ?? 'process error'));
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
- finally {
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.dataPlane.postJson(`/runtime/v1/process/${pid}/signal`, {
140
- ...opts,
141
- json: { signal: 'SIGKILL' },
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
- 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
- const first = await nextStarted(socket);
176
- const actualPid = framePid(first) ?? pid;
177
- return new CommandHandle(actualPid, socket, () => this.kill(actualPid), socket, opts.onStdout, opts.onStderr, opts.onPty);
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
- return new CommandHandle(pid, socket, () => this.kill(pid), withFirst(first, socket), opts.onStdout, opts.onStderr, opts.onPty, opts.onExit);
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
- return new CommandHandle(pid, socket, () => this.kill(pid), withFirst(first, socket), undefined, undefined, opts.onData ?? opts.onPty);
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 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();
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);
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.postJson(`/runtime/v1/process/${pid}/signal`, {
70
- json: { signal: 'SIGKILL' },
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@watasu/sdk",
3
- "version": "0.1.66",
3
+ "version": "0.1.68",
4
4
  "type": "module",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "description": "TypeScript SDK for Watasu",