@watasu/sdk 0.1.66 → 0.1.67

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;
@@ -125,6 +164,14 @@ export declare class Commands {
125
164
  run(cmd: string, opts?: CommandStartOpts): Promise<CommandResult>;
126
165
  /** Reconnect to a live process stream by pid. */
127
166
  connect(pid: number | string, opts?: CommandStartOpts): Promise<CommandHandle>;
167
+ /** Reconnect to a live process stream by pid starting at a cursor. */
168
+ connectSince(pid: number | string, cursor?: number, opts?: CommandStartOpts): Promise<CommandHandle>;
169
+ /** Look up process status without attaching a WebSocket. */
170
+ process(pid: number | string, opts?: CommandRequestOpts): Promise<ProcessStatus>;
171
+ /** Read available process output since a cursor without blocking. */
172
+ readProcessOutput(pid: number | string, opts?: ReadProcessOutputOptions): Promise<ProcessOutputSnapshot>;
173
+ /** Stop a process, optionally signalling the full process group. */
174
+ stopProcess(pid: number | string, opts?: StopProcessOptions): Promise<ProcessStatus>;
128
175
  /** Start a command and return a live handle immediately. */
129
176
  start(cmd: string, opts?: CommandStartOpts): Promise<CommandHandle>;
130
177
  }
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. */
@@ -136,9 +137,10 @@ export class Commands {
136
137
  }
137
138
  /** Send SIGKILL to a process by pid. */
138
139
  async kill(pid, opts = {}) {
139
- await this.dataPlane.postJson(`/runtime/v1/process/${pid}/signal`, {
140
- ...opts,
141
- json: { signal: 'SIGKILL' },
140
+ await this.stopProcess(pid, {
141
+ signal: 'SIGKILL',
142
+ requestTimeoutMs: opts.requestTimeoutMs,
143
+ abortSignal: opts.signal,
142
144
  });
143
145
  return true;
144
146
  }
@@ -171,11 +173,41 @@ export class Commands {
171
173
  }
172
174
  /** Reconnect to a live process stream by pid. */
173
175
  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();
176
+ return this.connectSince(pid, 0, opts);
177
+ }
178
+ /** Reconnect to a live process stream by pid starting at a cursor. */
179
+ async connectSince(pid, cursor = 0, opts = {}) {
180
+ const encodedPid = encodeURIComponent(String(pid));
181
+ 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();
175
182
  const first = await nextStarted(socket);
176
183
  const actualPid = framePid(first) ?? pid;
177
184
  return new CommandHandle(actualPid, socket, () => this.kill(actualPid), socket, opts.onStdout, opts.onStderr, opts.onPty);
178
185
  }
186
+ /** Look up process status without attaching a WebSocket. */
187
+ async process(pid, opts = {}) {
188
+ const payload = await this.dataPlane.getJson(`/runtime/v1/process/${encodeURIComponent(String(pid))}`, opts);
189
+ return processStatus(payload);
190
+ }
191
+ /** Read available process output since a cursor without blocking. */
192
+ async readProcessOutput(pid, opts = {}) {
193
+ const payload = await this.dataPlane.getJson(withQuery(`/runtime/v1/process/${encodeURIComponent(String(pid))}/output`, {
194
+ since: opts.since,
195
+ limit_bytes: opts.limitBytes,
196
+ }), opts);
197
+ return processOutputSnapshot(payload);
198
+ }
199
+ /** Stop a process, optionally signalling the full process group. */
200
+ async stopProcess(pid, opts = {}) {
201
+ const payload = await this.dataPlane.deleteJson(withQuery(`/runtime/v1/process/${encodeURIComponent(String(pid))}`, {
202
+ signal: opts.signal,
203
+ kill_group: opts.killGroup ?? true,
204
+ grace_ms: opts.graceMs,
205
+ }), {
206
+ requestTimeoutMs: opts.requestTimeoutMs,
207
+ signal: opts.abortSignal,
208
+ });
209
+ return processStatus(payload);
210
+ }
179
211
  /** Start a command and return a live handle immediately. */
180
212
  async start(cmd, opts = {}) {
181
213
  const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, '/runtime/v1/process', opts.requestTimeoutMs ?? this.config.requestTimeoutMs, this.config.headers).connect();
@@ -243,8 +275,61 @@ function processInfo(value) {
243
275
  cwd: typeof process.cwd === 'string' ? process.cwd : undefined,
244
276
  };
245
277
  }
278
+ function processStatus(value) {
279
+ const item = value && typeof value === 'object' ? value : {};
280
+ const process = item.process && typeof item.process === 'object' ? item.process : item;
281
+ const pid = scalar(process.pid ?? process.id) ?? '';
282
+ return {
283
+ pid,
284
+ id: scalar(process.id),
285
+ osPid: numberValue(process.os_pid ?? process.osPid),
286
+ command: stringValue(process.command ?? process.cmd),
287
+ args: arrayOfStrings(process.args ?? process.arguments),
288
+ cwd: stringValue(process.cwd ?? process.working_directory),
289
+ user: stringValue(process.user),
290
+ pty: typeof process.pty === 'boolean' ? process.pty : undefined,
291
+ status: stringValue(process.status) ?? '',
292
+ startedAt: stringValue(process.started_at ?? process.startedAt),
293
+ finishedAt: stringValue(process.finished_at ?? process.finishedAt),
294
+ exitCode: numberValue(process.exit_code ?? process.exitCode),
295
+ };
296
+ }
297
+ function processOutputSnapshot(value) {
298
+ const payload = value && typeof value === 'object' ? value : {};
299
+ return {
300
+ pid: scalar(payload.pid ?? payload.id) ?? '',
301
+ status: stringValue(payload.status) ?? '',
302
+ exitCode: numberValue(payload.exit_code ?? payload.exitCode),
303
+ finishedAt: stringValue(payload.finished_at ?? payload.finishedAt),
304
+ nextCursor: numberValue(payload.next_cursor ?? payload.nextCursor) ?? 0,
305
+ truncatedBeforeCursor: payload.truncated_before_cursor === true || payload.truncatedBeforeCursor === true,
306
+ events: Array.isArray(payload.events) ? payload.events.map(processOutputEvent) : [],
307
+ };
308
+ }
309
+ function processOutputEvent(value) {
310
+ const event = value && typeof value === 'object' ? value : {};
311
+ return {
312
+ cursor: numberValue(event.cursor) ?? 0,
313
+ type: stringValue(event.type) ?? '',
314
+ data: base64DecodeBytes(stringValue(event.data) ?? ''),
315
+ };
316
+ }
246
317
  function recordOfStrings(value) {
247
318
  if (!value || typeof value !== 'object')
248
319
  return {};
249
320
  return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, String(item)]));
250
321
  }
322
+ function scalar(value) {
323
+ return typeof value === 'number' || typeof value === 'string' ? value : undefined;
324
+ }
325
+ function stringValue(value) {
326
+ return typeof value === 'string' ? value : undefined;
327
+ }
328
+ function numberValue(value) {
329
+ if (typeof value === 'number' && Number.isFinite(value))
330
+ return value;
331
+ return undefined;
332
+ }
333
+ function arrayOfStrings(value) {
334
+ return Array.isArray(value) ? value.map(String) : [];
335
+ }
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.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 {
@@ -35,7 +36,7 @@ export class Pty {
35
36
  }
36
37
  /** Connect to a running PTY by pid. */
37
38
  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 socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, withQuery(`/runtime/v1/process/${encodeURIComponent(String(pid))}/connect`, { since: 0 }), opts.requestTimeoutMs ?? this.config.requestTimeoutMs, this.config.headers).connect();
39
40
  const first = await nextStarted(socket);
40
41
  const actualPid = framePid(first) ?? pid;
41
42
  return new CommandHandle(actualPid, socket, () => this.kill(actualPid), withFirst(first, socket), undefined, undefined, opts.onData);
@@ -66,8 +67,10 @@ 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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@watasu/sdk",
3
- "version": "0.1.66",
3
+ "version": "0.1.67",
4
4
  "type": "module",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "description": "TypeScript SDK for Watasu",