@watasu/sdk 0.1.6 → 0.1.25
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 +122 -8
- package/dist/codeInterpreter.d.ts +100 -0
- package/dist/codeInterpreter.js +241 -0
- package/dist/commands.d.ts +26 -4
- package/dist/commands.js +54 -15
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +8 -0
- package/dist/filesystem.d.ts +53 -13
- package/dist/filesystem.js +128 -12
- package/dist/git.d.ts +27 -0
- package/dist/git.js +43 -2
- package/dist/index.d.ts +15 -6
- package/dist/index.js +8 -3
- package/dist/process.d.ts +56 -0
- package/dist/process.js +137 -0
- package/dist/processSocket.d.ts +1 -0
- package/dist/processSocket.js +3 -0
- package/dist/pty.d.ts +5 -1
- package/dist/pty.js +9 -7
- package/dist/sandbox.d.ts +121 -20
- package/dist/sandbox.js +254 -43
- 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 +5 -1
package/dist/commands.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProcessSocket, base64DecodeBytes, base64DecodeText } from './processSocket.js';
|
|
2
|
-
import { SandboxError } from './errors.js';
|
|
2
|
+
import { SandboxError, TimeoutError } from './errors.js';
|
|
3
3
|
/** Error thrown by `CommandHandle.wait()` when a process exits non-zero. */
|
|
4
4
|
export class CommandExitError extends SandboxError {
|
|
5
5
|
result;
|
|
@@ -22,11 +22,12 @@ export class CommandHandle {
|
|
|
22
22
|
onStdout;
|
|
23
23
|
onStderr;
|
|
24
24
|
onPty;
|
|
25
|
+
onExit;
|
|
25
26
|
_stdout = '';
|
|
26
27
|
_stderr = '';
|
|
27
28
|
result;
|
|
28
29
|
pending;
|
|
29
|
-
constructor(pid, socket, handleKill, events, onStdout, onStderr, onPty) {
|
|
30
|
+
constructor(pid, socket, handleKill, events, onStdout, onStderr, onPty, onExit) {
|
|
30
31
|
this.pid = pid;
|
|
31
32
|
this.socket = socket;
|
|
32
33
|
this.handleKill = handleKill;
|
|
@@ -34,6 +35,7 @@ export class CommandHandle {
|
|
|
34
35
|
this.onStdout = onStdout;
|
|
35
36
|
this.onStderr = onStderr;
|
|
36
37
|
this.onPty = onPty;
|
|
38
|
+
this.onExit = onExit;
|
|
37
39
|
this.pending = this.handleEvents();
|
|
38
40
|
}
|
|
39
41
|
get stdout() { return this._stdout; }
|
|
@@ -41,8 +43,8 @@ export class CommandHandle {
|
|
|
41
43
|
get exitCode() { return this.result?.exitCode; }
|
|
42
44
|
get error() { return this.result?.error; }
|
|
43
45
|
/** Wait until the process exits and return captured output. */
|
|
44
|
-
async wait() {
|
|
45
|
-
await this.pending;
|
|
46
|
+
async wait(timeoutMs) {
|
|
47
|
+
await waitFor(this.pending, timeoutMs);
|
|
46
48
|
if (!this.result)
|
|
47
49
|
throw new SandboxError('Command ended without an exit event');
|
|
48
50
|
if (this.result.exitCode !== 0)
|
|
@@ -57,12 +59,16 @@ export class CommandHandle {
|
|
|
57
59
|
async sendStdin(data) {
|
|
58
60
|
this.socket.sendStdin(data);
|
|
59
61
|
}
|
|
62
|
+
/** Close the stdin stream and signal EOF to the process. */
|
|
63
|
+
async closeStdin() {
|
|
64
|
+
this.socket.closeStdin();
|
|
65
|
+
}
|
|
60
66
|
/** Resize the attached PTY stream when this handle was created as a PTY. */
|
|
61
67
|
async resize(size) {
|
|
62
68
|
this.socket.sendJson({ type: 'resize', cols: size.cols, rows: size.rows });
|
|
63
69
|
}
|
|
64
70
|
/** Detach the local stream without killing the process. */
|
|
65
|
-
disconnect() {
|
|
71
|
+
async disconnect() {
|
|
66
72
|
this.socket.close();
|
|
67
73
|
}
|
|
68
74
|
async handleEvents() {
|
|
@@ -88,12 +94,14 @@ export class CommandHandle {
|
|
|
88
94
|
await this.onPty?.(bytes);
|
|
89
95
|
}
|
|
90
96
|
else if (type === 'exit') {
|
|
97
|
+
const exitCode = Number(frame.exit_code ?? frame.exitCode ?? 0);
|
|
91
98
|
this.result = {
|
|
92
|
-
exitCode
|
|
99
|
+
exitCode,
|
|
93
100
|
error: typeof frame.error === 'string' ? frame.error : undefined,
|
|
94
101
|
stdout: this._stdout,
|
|
95
102
|
stderr: this._stderr,
|
|
96
103
|
};
|
|
104
|
+
await this.onExit?.(exitCode);
|
|
97
105
|
return;
|
|
98
106
|
}
|
|
99
107
|
else if (type === 'error') {
|
|
@@ -116,6 +124,10 @@ export class Commands {
|
|
|
116
124
|
this.config = config;
|
|
117
125
|
this.sandboxEnvs = sandboxEnvs;
|
|
118
126
|
}
|
|
127
|
+
/** Whether this runtime supports stdin EOF frames. */
|
|
128
|
+
get supportsStdinClose() {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
119
131
|
/** List processes currently known by the sandbox runtime. */
|
|
120
132
|
async list(opts = {}) {
|
|
121
133
|
const payload = await this.dataPlane.getJson('/runtime/v1/process', opts);
|
|
@@ -137,7 +149,17 @@ export class Commands {
|
|
|
137
149
|
await handle.sendStdin(data);
|
|
138
150
|
}
|
|
139
151
|
finally {
|
|
140
|
-
handle.disconnect();
|
|
152
|
+
await handle.disconnect();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Attach to a process and close stdin, signalling EOF. */
|
|
156
|
+
async closeStdin(pid, opts = {}) {
|
|
157
|
+
const handle = await this.connect(pid, opts);
|
|
158
|
+
try {
|
|
159
|
+
await handle.closeStdin();
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
await handle.disconnect();
|
|
141
163
|
}
|
|
142
164
|
}
|
|
143
165
|
/** Run a shell command over the WebSocket process runtime. */
|
|
@@ -145,7 +167,7 @@ export class Commands {
|
|
|
145
167
|
const handle = await this.start(cmd, opts);
|
|
146
168
|
if (opts.background)
|
|
147
169
|
return handle;
|
|
148
|
-
return handle.wait();
|
|
170
|
+
return handle.wait(opts.timeoutMs ?? opts.timeout);
|
|
149
171
|
}
|
|
150
172
|
/** Reconnect to a live process stream by pid. */
|
|
151
173
|
async connect(pid, opts = {}) {
|
|
@@ -154,27 +176,44 @@ export class Commands {
|
|
|
154
176
|
const actualPid = framePid(first) ?? pid;
|
|
155
177
|
return new CommandHandle(actualPid, socket, () => this.kill(actualPid), socket, opts.onStdout, opts.onStderr, opts.onPty);
|
|
156
178
|
}
|
|
157
|
-
|
|
179
|
+
/** Start a command and return a live handle immediately. */
|
|
180
|
+
async start(cmd, opts = {}) {
|
|
158
181
|
const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, '/runtime/v1/process', opts.requestTimeoutMs ?? this.config.requestTimeoutMs).connect();
|
|
159
|
-
const environment = { ...this.sandboxEnvs, ...(opts.envs ?? {}) };
|
|
182
|
+
const environment = { ...this.sandboxEnvs, ...(opts.envVars ?? opts.envs ?? {}) };
|
|
183
|
+
const processConfig = processStartConfig(cmd, opts);
|
|
160
184
|
socket.sendJson({
|
|
161
185
|
type: 'start',
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
id: opts.processID,
|
|
187
|
+
cmd: processConfig.cmd,
|
|
188
|
+
args: processConfig.args,
|
|
189
|
+
cwd: opts.cwd ?? opts.rootDir,
|
|
165
190
|
user: opts.user,
|
|
166
191
|
environment,
|
|
167
192
|
envs: environment,
|
|
168
193
|
stdin: opts.stdin ?? false,
|
|
169
|
-
timeout_ms: opts.timeoutMs ?? 60_000,
|
|
194
|
+
timeout_ms: opts.timeoutMs ?? opts.timeout ?? 60_000,
|
|
170
195
|
});
|
|
171
196
|
const first = await nextStarted(socket);
|
|
172
197
|
const pid = framePid(first);
|
|
173
198
|
if (pid === undefined)
|
|
174
199
|
throw new SandboxError('process started frame did not include pid');
|
|
175
|
-
return new CommandHandle(pid, socket, () => this.kill(pid), withFirst(first, socket), opts.onStdout, opts.onStderr, opts.onPty);
|
|
200
|
+
return new CommandHandle(pid, socket, () => this.kill(pid), withFirst(first, socket), opts.onStdout, opts.onStderr, opts.onPty, opts.onExit);
|
|
176
201
|
}
|
|
177
202
|
}
|
|
203
|
+
function processStartConfig(cmd, opts) {
|
|
204
|
+
if (opts.args !== undefined || opts.cmd !== undefined) {
|
|
205
|
+
return { cmd: opts.cmd ?? cmd, args: opts.args ?? [] };
|
|
206
|
+
}
|
|
207
|
+
return { cmd: '/bin/bash', args: ['-l', '-c', cmd] };
|
|
208
|
+
}
|
|
209
|
+
function waitFor(promise, timeoutMs) {
|
|
210
|
+
if (timeoutMs === undefined || timeoutMs <= 0)
|
|
211
|
+
return promise;
|
|
212
|
+
return new Promise((resolve, reject) => {
|
|
213
|
+
const timer = setTimeout(() => reject(new TimeoutError()), timeoutMs);
|
|
214
|
+
promise.then(resolve, reject).finally(() => clearTimeout(timer));
|
|
215
|
+
});
|
|
216
|
+
}
|
|
178
217
|
async function nextStarted(events) {
|
|
179
218
|
for await (const frame of events) {
|
|
180
219
|
if (frame.type === 'started')
|
package/dist/errors.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ export declare class AuthenticationError extends SandboxError {
|
|
|
7
7
|
export declare class NotFoundError extends SandboxError {
|
|
8
8
|
constructor(message?: string);
|
|
9
9
|
}
|
|
10
|
+
export declare class ConflictError extends SandboxError {
|
|
11
|
+
constructor(message?: string);
|
|
12
|
+
}
|
|
10
13
|
export declare class TimeoutError extends SandboxError {
|
|
11
14
|
constructor(message?: string);
|
|
12
15
|
}
|
package/dist/errors.js
CHANGED
|
@@ -16,6 +16,12 @@ export class NotFoundError extends SandboxError {
|
|
|
16
16
|
this.name = 'NotFoundError';
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
export class ConflictError extends SandboxError {
|
|
20
|
+
constructor(message = 'Conflict') {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'ConflictError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
19
25
|
export class TimeoutError extends SandboxError {
|
|
20
26
|
constructor(message = 'Request timed out') {
|
|
21
27
|
super(message);
|
|
@@ -76,6 +82,8 @@ export function errorFromResponse(status, payload) {
|
|
|
76
82
|
return new AuthenticationError(message);
|
|
77
83
|
if (status === 404)
|
|
78
84
|
return new NotFoundError(message);
|
|
85
|
+
if (status === 409)
|
|
86
|
+
return new ConflictError(message);
|
|
79
87
|
if (status === 408 || status === 504)
|
|
80
88
|
return new TimeoutError(message);
|
|
81
89
|
if (status === 422 || status === 400)
|
package/dist/filesystem.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ export interface EntryInfo {
|
|
|
21
21
|
metadata?: Record<string, string>;
|
|
22
22
|
}
|
|
23
23
|
export type WriteInfo = EntryInfo;
|
|
24
|
+
export type WriteData = string | Uint8Array | ArrayBuffer | Blob | ReadableStream<Uint8Array>;
|
|
25
|
+
export interface WriteEntry {
|
|
26
|
+
path: string;
|
|
27
|
+
data: WriteData;
|
|
28
|
+
}
|
|
24
29
|
export interface FilesystemEvent {
|
|
25
30
|
type: 'create' | 'write' | 'modify' | 'remove' | 'delete' | 'rename' | string;
|
|
26
31
|
path: string;
|
|
@@ -33,6 +38,17 @@ export interface WatchOpts {
|
|
|
33
38
|
requestTimeoutMs?: number;
|
|
34
39
|
onExit?: (error?: Error) => void | Promise<void>;
|
|
35
40
|
}
|
|
41
|
+
export interface FilesystemRequestOpts {
|
|
42
|
+
requestTimeoutMs?: number;
|
|
43
|
+
user?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface FilesystemReadOpts extends FilesystemRequestOpts {
|
|
46
|
+
gzip?: boolean;
|
|
47
|
+
}
|
|
48
|
+
export interface FilesystemWriteOpts extends FilesystemRequestOpts {
|
|
49
|
+
gzip?: boolean;
|
|
50
|
+
metadata?: Record<string, string>;
|
|
51
|
+
}
|
|
36
52
|
/** Live filesystem watcher. Call `stop()` to close the local watch stream. */
|
|
37
53
|
export declare class WatchHandle {
|
|
38
54
|
private readonly socket;
|
|
@@ -46,22 +62,45 @@ export declare class WatchHandle {
|
|
|
46
62
|
wait(): Promise<void>;
|
|
47
63
|
private pump;
|
|
48
64
|
}
|
|
65
|
+
/** Lazy filesystem watcher. Add listeners, then call `start()`. */
|
|
66
|
+
export declare class FilesystemWatcher {
|
|
67
|
+
private readonly dataPlane;
|
|
68
|
+
private readonly path;
|
|
69
|
+
private readonly opts;
|
|
70
|
+
private handle?;
|
|
71
|
+
private listeners;
|
|
72
|
+
constructor(dataPlane: DataPlaneClient, path: string, opts?: WatchOpts);
|
|
73
|
+
start(opts?: WatchOpts): Promise<void>;
|
|
74
|
+
stop(): Promise<void>;
|
|
75
|
+
addEventListener(listener: (event: FilesystemEvent) => void | Promise<void>): () => boolean;
|
|
76
|
+
wait(): Promise<void>;
|
|
77
|
+
}
|
|
49
78
|
/** Filesystem helper for a sandbox data-plane session. */
|
|
50
79
|
export declare class Filesystem {
|
|
51
80
|
private readonly dataPlane;
|
|
52
81
|
constructor(dataPlane: DataPlaneClient);
|
|
53
|
-
/** Read
|
|
54
|
-
read(path: string, opts?: {
|
|
55
|
-
format?: 'text'
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
/** Read file content as text, bytes, a `Blob`, or a `ReadableStream`. */
|
|
83
|
+
read(path: string, opts?: FilesystemReadOpts & {
|
|
84
|
+
format?: 'text';
|
|
85
|
+
}): Promise<string>;
|
|
86
|
+
read(path: string, opts: FilesystemReadOpts & {
|
|
87
|
+
format: 'bytes';
|
|
88
|
+
}): Promise<Uint8Array>;
|
|
89
|
+
read(path: string, opts: FilesystemReadOpts & {
|
|
90
|
+
format: 'blob';
|
|
91
|
+
}): Promise<Blob>;
|
|
92
|
+
read(path: string, opts: FilesystemReadOpts & {
|
|
93
|
+
format: 'stream';
|
|
94
|
+
}): Promise<ReadableStream<Uint8Array>>;
|
|
95
|
+
/** Read a file as raw bytes. */
|
|
96
|
+
readBytes(path: string, opts?: FilesystemReadOpts): Promise<Uint8Array>;
|
|
97
|
+
/** Write UTF-8 text, bytes, browser data objects, or a batch of file entries. */
|
|
98
|
+
write(path: string, data: WriteData, opts?: FilesystemWriteOpts): Promise<WriteInfo>;
|
|
99
|
+
write(files: WriteEntry[], opts?: FilesystemWriteOpts): Promise<WriteInfo[]>;
|
|
100
|
+
/** Write raw bytes to a file. */
|
|
101
|
+
writeBytes(path: string, data: Uint8Array | ArrayBuffer, opts?: FilesystemWriteOpts): Promise<WriteInfo>;
|
|
102
|
+
/** Write several files in one runtime API call. */
|
|
103
|
+
writeFiles(files: WriteEntry[], opts?: FilesystemWriteOpts): Promise<WriteInfo[]>;
|
|
65
104
|
/** List directory entries below `path`. */
|
|
66
105
|
list(path: string, opts?: {
|
|
67
106
|
requestTimeoutMs?: number;
|
|
@@ -88,5 +127,6 @@ export declare class Filesystem {
|
|
|
88
127
|
requestTimeoutMs?: number;
|
|
89
128
|
}): Promise<boolean>;
|
|
90
129
|
/** Start watching a directory for filesystem events. */
|
|
91
|
-
watchDir(path: string
|
|
130
|
+
watchDir(path: string): FilesystemWatcher;
|
|
131
|
+
watchDir(path: string, onEvent: (event: FilesystemEvent) => void | Promise<void>, opts?: WatchOpts): Promise<FilesystemWatcher>;
|
|
92
132
|
}
|
package/dist/filesystem.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { withQuery } from './transport.js';
|
|
2
|
-
import { FileNotFoundError } from './errors.js';
|
|
3
|
-
import { ProcessSocket } from './processSocket.js';
|
|
2
|
+
import { FileNotFoundError, InvalidArgumentError } from './errors.js';
|
|
3
|
+
import { ProcessSocket, base64Encode } from './processSocket.js';
|
|
4
4
|
export var FileType;
|
|
5
5
|
(function (FileType) {
|
|
6
6
|
/** Regular file. */
|
|
@@ -50,27 +50,93 @@ export class WatchHandle {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
/** Lazy filesystem watcher. Add listeners, then call `start()`. */
|
|
54
|
+
export class FilesystemWatcher {
|
|
55
|
+
dataPlane;
|
|
56
|
+
path;
|
|
57
|
+
opts;
|
|
58
|
+
handle;
|
|
59
|
+
listeners = [];
|
|
60
|
+
constructor(dataPlane, path, opts = {}) {
|
|
61
|
+
this.dataPlane = dataPlane;
|
|
62
|
+
this.path = path;
|
|
63
|
+
this.opts = opts;
|
|
64
|
+
}
|
|
65
|
+
async start(opts = {}) {
|
|
66
|
+
if (this.handle)
|
|
67
|
+
return;
|
|
68
|
+
const nextOpts = { ...this.opts, ...opts };
|
|
69
|
+
const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, withQuery('/runtime/v1/files/watch', { path: this.path, recursive: nextOpts.recursive ?? false, include_entry: nextOpts.includeEntry }), nextOpts.requestTimeoutMs).connect();
|
|
70
|
+
this.handle = new WatchHandle(socket, socket, async (event) => {
|
|
71
|
+
for (const listener of this.listeners)
|
|
72
|
+
await listener(event);
|
|
73
|
+
}, nextOpts.onExit);
|
|
74
|
+
}
|
|
75
|
+
async stop() {
|
|
76
|
+
this.handle?.stop();
|
|
77
|
+
}
|
|
78
|
+
addEventListener(listener) {
|
|
79
|
+
this.listeners.push(listener);
|
|
80
|
+
return () => {
|
|
81
|
+
const index = this.listeners.indexOf(listener);
|
|
82
|
+
if (index === -1)
|
|
83
|
+
return false;
|
|
84
|
+
this.listeners.splice(index, 1);
|
|
85
|
+
return true;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
wait() {
|
|
89
|
+
return this.handle?.wait() ?? Promise.resolve();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
53
92
|
/** Filesystem helper for a sandbox data-plane session. */
|
|
54
93
|
export class Filesystem {
|
|
55
94
|
dataPlane;
|
|
56
95
|
constructor(dataPlane) {
|
|
57
96
|
this.dataPlane = dataPlane;
|
|
58
97
|
}
|
|
59
|
-
/** Read a file as UTF-8 text, bytes, or a one-chunk async byte stream. */
|
|
60
98
|
async read(path, opts = {}) {
|
|
61
99
|
const bytes = await this.dataPlane.getBytes(withQuery('/runtime/v1/files', { path, gzip: opts.gzip }), opts);
|
|
62
100
|
if (opts.format === 'bytes')
|
|
63
101
|
return bytes;
|
|
102
|
+
if (opts.format === 'blob')
|
|
103
|
+
return new Blob([toArrayBuffer(bytes)]);
|
|
64
104
|
if (opts.format === 'stream')
|
|
65
|
-
return (
|
|
105
|
+
return new ReadableStream({ start(controller) { controller.enqueue(bytes); controller.close(); } });
|
|
66
106
|
return new TextDecoder().decode(bytes);
|
|
67
107
|
}
|
|
68
|
-
/**
|
|
69
|
-
async
|
|
70
|
-
|
|
71
|
-
|
|
108
|
+
/** Read a file as raw bytes. */
|
|
109
|
+
async readBytes(path, opts = {}) {
|
|
110
|
+
return this.read(path, { ...opts, format: 'bytes' });
|
|
111
|
+
}
|
|
112
|
+
async write(pathOrFiles, dataOrOpts, opts = {}) {
|
|
113
|
+
if (Array.isArray(pathOrFiles)) {
|
|
114
|
+
return this.writeFiles(pathOrFiles, dataOrOpts);
|
|
115
|
+
}
|
|
116
|
+
const body = await writeDataToBytes(dataOrOpts);
|
|
117
|
+
const payload = await this.dataPlane.putJson(withQuery('/runtime/v1/files', { path: pathOrFiles, gzip: opts.gzip }), body, opts);
|
|
72
118
|
return entryInfo(payload.file ?? payload);
|
|
73
119
|
}
|
|
120
|
+
/** Write raw bytes to a file. */
|
|
121
|
+
async writeBytes(path, data, opts = {}) {
|
|
122
|
+
return this.write(path, data, opts);
|
|
123
|
+
}
|
|
124
|
+
/** Write several files in one runtime API call. */
|
|
125
|
+
async writeFiles(files, opts = {}) {
|
|
126
|
+
if (files.length === 0)
|
|
127
|
+
return [];
|
|
128
|
+
const payload = await this.dataPlane.postJson('/runtime/v1/files/write_files', {
|
|
129
|
+
...opts,
|
|
130
|
+
json: {
|
|
131
|
+
files: await Promise.all(files.map(async (file) => ({
|
|
132
|
+
path: file.path,
|
|
133
|
+
data_base64: base64Encode(await writeDataToBytes(file.data)),
|
|
134
|
+
}))),
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
const written = Array.isArray(payload.files) ? payload.files : [];
|
|
138
|
+
return written.map(entryInfo);
|
|
139
|
+
}
|
|
74
140
|
/** List directory entries below `path`. */
|
|
75
141
|
async list(path, opts = {}) {
|
|
76
142
|
const payload = await this.dataPlane.getJson(withQuery('/runtime/v1/directories', { path, depth: opts.depth }), opts);
|
|
@@ -111,11 +177,61 @@ export class Filesystem {
|
|
|
111
177
|
await this.dataPlane.postJson(withQuery('/runtime/v1/directories', { path }), opts);
|
|
112
178
|
return true;
|
|
113
179
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
180
|
+
watchDir(path, onEvent, opts = {}) {
|
|
181
|
+
const watcher = new FilesystemWatcher(this.dataPlane, path, opts);
|
|
182
|
+
if (!onEvent)
|
|
183
|
+
return watcher;
|
|
184
|
+
watcher.addEventListener(onEvent);
|
|
185
|
+
return watcher.start().then(() => watcher);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function writeDataToBytes(data) {
|
|
189
|
+
if (typeof data === 'string')
|
|
190
|
+
return new TextEncoder().encode(data);
|
|
191
|
+
if (data instanceof Uint8Array)
|
|
192
|
+
return data;
|
|
193
|
+
if (data instanceof ArrayBuffer)
|
|
194
|
+
return new Uint8Array(data);
|
|
195
|
+
if (typeof Blob !== 'undefined' && data instanceof Blob)
|
|
196
|
+
return new Uint8Array(await data.arrayBuffer());
|
|
197
|
+
if (isReadableStream(data))
|
|
198
|
+
return readStreamToBytes(data);
|
|
199
|
+
throw new InvalidArgumentError(`Unsupported file data type: ${Object.prototype.toString.call(data)}`);
|
|
200
|
+
}
|
|
201
|
+
function toArrayBuffer(bytes) {
|
|
202
|
+
const buffer = new ArrayBuffer(bytes.byteLength);
|
|
203
|
+
new Uint8Array(buffer).set(bytes);
|
|
204
|
+
return buffer;
|
|
205
|
+
}
|
|
206
|
+
function isReadableStream(value) {
|
|
207
|
+
return Boolean(value && typeof value === 'object' && typeof value.getReader === 'function');
|
|
208
|
+
}
|
|
209
|
+
async function readStreamToBytes(stream) {
|
|
210
|
+
const reader = stream.getReader();
|
|
211
|
+
const chunks = [];
|
|
212
|
+
let total = 0;
|
|
213
|
+
try {
|
|
214
|
+
for (;;) {
|
|
215
|
+
const { done, value } = await reader.read();
|
|
216
|
+
if (done)
|
|
217
|
+
break;
|
|
218
|
+
if (!(value instanceof Uint8Array)) {
|
|
219
|
+
throw new InvalidArgumentError('ReadableStream file data must yield Uint8Array chunks');
|
|
220
|
+
}
|
|
221
|
+
chunks.push(value);
|
|
222
|
+
total += value.byteLength;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
reader.releaseLock();
|
|
227
|
+
}
|
|
228
|
+
const bytes = new Uint8Array(total);
|
|
229
|
+
let offset = 0;
|
|
230
|
+
for (const chunk of chunks) {
|
|
231
|
+
bytes.set(chunk, offset);
|
|
232
|
+
offset += chunk.byteLength;
|
|
118
233
|
}
|
|
234
|
+
return bytes;
|
|
119
235
|
}
|
|
120
236
|
function entryInfo(value) {
|
|
121
237
|
const item = value && typeof value === 'object' ? value : {};
|
package/dist/git.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface GitAuthOpts {
|
|
|
18
18
|
username?: string;
|
|
19
19
|
password?: string;
|
|
20
20
|
envs?: Record<string, string>;
|
|
21
|
+
user?: string;
|
|
22
|
+
cwd?: string;
|
|
21
23
|
timeout?: number;
|
|
22
24
|
timeoutMs?: number;
|
|
23
25
|
requestTimeoutMs?: number;
|
|
@@ -32,6 +34,10 @@ export interface GitCloneOpts extends GitAuthOpts {
|
|
|
32
34
|
}
|
|
33
35
|
export interface GitRequestOpts extends GitAuthOpts {
|
|
34
36
|
}
|
|
37
|
+
export interface GitInitOpts extends GitRequestOpts {
|
|
38
|
+
bare?: boolean;
|
|
39
|
+
initialBranch?: string;
|
|
40
|
+
}
|
|
35
41
|
export interface GitPullOpts extends GitAuthOpts {
|
|
36
42
|
branch?: string;
|
|
37
43
|
remote?: string;
|
|
@@ -54,12 +60,25 @@ export interface GitBranchOpts extends GitRequestOpts {
|
|
|
54
60
|
}
|
|
55
61
|
export interface GitAddOpts extends GitRequestOpts {
|
|
56
62
|
files?: string[];
|
|
63
|
+
all?: boolean;
|
|
57
64
|
}
|
|
58
65
|
export interface GitCommitOpts extends GitRequestOpts {
|
|
59
66
|
authorName?: string;
|
|
60
67
|
authorEmail?: string;
|
|
61
68
|
allowEmpty?: boolean;
|
|
62
69
|
}
|
|
70
|
+
export type GitResetMode = 'soft' | 'mixed' | 'hard' | 'merge' | 'keep';
|
|
71
|
+
export interface GitResetOpts extends GitRequestOpts {
|
|
72
|
+
mode?: GitResetMode;
|
|
73
|
+
target?: string;
|
|
74
|
+
paths?: string[];
|
|
75
|
+
}
|
|
76
|
+
export interface GitRestoreOpts extends GitRequestOpts {
|
|
77
|
+
paths: string[];
|
|
78
|
+
staged?: boolean;
|
|
79
|
+
worktree?: boolean;
|
|
80
|
+
source?: string;
|
|
81
|
+
}
|
|
63
82
|
export interface GitRemoteAddOpts extends GitRequestOpts {
|
|
64
83
|
fetch?: boolean;
|
|
65
84
|
overwrite?: boolean;
|
|
@@ -114,6 +133,8 @@ export declare class Git {
|
|
|
114
133
|
}): Promise<GitCommandResult>;
|
|
115
134
|
/** Configure Git author identity globally or for one repository. */
|
|
116
135
|
configureUser(name: string, email: string, opts?: GitConfigureUserOpts): Promise<GitCommandResult>;
|
|
136
|
+
/** Initialize a Git repository. */
|
|
137
|
+
init(path: string, opts?: GitInitOpts): Promise<GitCommandResult>;
|
|
117
138
|
/** Return parsed repository status for `path`. */
|
|
118
139
|
status(path: string, opts?: GitRequestOpts): Promise<GitStatus>;
|
|
119
140
|
/** Return branches and the current branch for `path`. */
|
|
@@ -126,6 +147,10 @@ export declare class Git {
|
|
|
126
147
|
add(path: string, opts?: GitAddOpts): Promise<GitCommandResult>;
|
|
127
148
|
/** Commit staged files. */
|
|
128
149
|
commit(path: string, message: string, opts?: GitCommitOpts): Promise<GitCommandResult>;
|
|
150
|
+
/** Reset the current HEAD to a specified state. */
|
|
151
|
+
reset(path: string, opts?: GitResetOpts): Promise<GitCommandResult>;
|
|
152
|
+
/** Restore working tree files or unstage changes. */
|
|
153
|
+
restore(path: string, opts: GitRestoreOpts): Promise<GitCommandResult>;
|
|
129
154
|
/** Pull the current branch with a fast-forward-only merge. */
|
|
130
155
|
pull(path: string, opts?: GitPullOpts): Promise<GitCommandResult>;
|
|
131
156
|
/** Push the current branch or a selected branch. */
|
|
@@ -136,6 +161,8 @@ export declare class Git {
|
|
|
136
161
|
checkoutBranch(path: string, branch: string, opts?: GitRequestOpts): Promise<GitCommandResult>;
|
|
137
162
|
/** Add a remote. */
|
|
138
163
|
remoteAdd(path: string, name: string, url: string, opts?: GitRemoteAddOpts): Promise<GitCommandResult>;
|
|
164
|
+
/** Return a remote URL, or undefined when the remote does not exist. */
|
|
165
|
+
remoteGet(path: string, name: string, opts?: GitRequestOpts): Promise<string | undefined>;
|
|
139
166
|
/** Set a Git config value. */
|
|
140
167
|
setConfig(key: string, value: string, opts?: GitConfigOpts): Promise<GitCommandResult>;
|
|
141
168
|
/** Read a Git config value. */
|
package/dist/git.js
CHANGED
|
@@ -33,6 +33,15 @@ export class Git {
|
|
|
33
33
|
path: opts.path,
|
|
34
34
|
}, opts);
|
|
35
35
|
}
|
|
36
|
+
/** Initialize a Git repository. */
|
|
37
|
+
async init(path, opts = {}) {
|
|
38
|
+
return this.run('/runtime/v1/git/init', {
|
|
39
|
+
path,
|
|
40
|
+
bare: opts.bare,
|
|
41
|
+
initial_branch: opts.initialBranch,
|
|
42
|
+
...gitOpts(opts),
|
|
43
|
+
}, opts);
|
|
44
|
+
}
|
|
36
45
|
/** Return parsed repository status for `path`. */
|
|
37
46
|
async status(path, opts = {}) {
|
|
38
47
|
const result = await this.run('/runtime/v1/git/status', { path, ...gitOpts(opts) }, opts);
|
|
@@ -58,7 +67,7 @@ export class Git {
|
|
|
58
67
|
}
|
|
59
68
|
/** Stage files. Defaults to all files. */
|
|
60
69
|
async add(path, opts = {}) {
|
|
61
|
-
return this.run('/runtime/v1/git/add', { path, files: opts.files, ...gitOpts(opts) }, opts);
|
|
70
|
+
return this.run('/runtime/v1/git/add', { path, files: opts.files, all: opts.all, ...gitOpts(opts) }, opts);
|
|
62
71
|
}
|
|
63
72
|
/** Commit staged files. */
|
|
64
73
|
async commit(path, message, opts = {}) {
|
|
@@ -71,6 +80,27 @@ export class Git {
|
|
|
71
80
|
...gitOpts(opts),
|
|
72
81
|
}, opts);
|
|
73
82
|
}
|
|
83
|
+
/** Reset the current HEAD to a specified state. */
|
|
84
|
+
async reset(path, opts = {}) {
|
|
85
|
+
return this.run('/runtime/v1/git/reset', {
|
|
86
|
+
path,
|
|
87
|
+
mode: opts.mode,
|
|
88
|
+
target: opts.target,
|
|
89
|
+
paths: opts.paths,
|
|
90
|
+
...gitOpts(opts),
|
|
91
|
+
}, opts);
|
|
92
|
+
}
|
|
93
|
+
/** Restore working tree files or unstage changes. */
|
|
94
|
+
async restore(path, opts) {
|
|
95
|
+
return this.run('/runtime/v1/git/restore', {
|
|
96
|
+
path,
|
|
97
|
+
paths: opts.paths,
|
|
98
|
+
staged: opts.staged,
|
|
99
|
+
worktree: opts.worktree,
|
|
100
|
+
source: opts.source,
|
|
101
|
+
...gitOpts(opts),
|
|
102
|
+
}, opts);
|
|
103
|
+
}
|
|
74
104
|
/** Pull the current branch with a fast-forward-only merge. */
|
|
75
105
|
async pull(path, opts = {}) {
|
|
76
106
|
return this.run('/runtime/v1/git/pull', { path, ...gitOpts(opts), ...pick(opts, ['remote', 'branch', 'username', 'password']) }, opts);
|
|
@@ -81,7 +111,7 @@ export class Git {
|
|
|
81
111
|
path,
|
|
82
112
|
...gitOpts(opts),
|
|
83
113
|
...pick(opts, ['remote', 'branch', 'username', 'password']),
|
|
84
|
-
set_upstream: opts.setUpstream,
|
|
114
|
+
set_upstream: opts.setUpstream ?? true,
|
|
85
115
|
}, opts);
|
|
86
116
|
}
|
|
87
117
|
/** Check out an arbitrary ref in a repository. */
|
|
@@ -103,6 +133,15 @@ export class Git {
|
|
|
103
133
|
...gitOpts(opts),
|
|
104
134
|
}, opts);
|
|
105
135
|
}
|
|
136
|
+
/** Return a remote URL, or undefined when the remote does not exist. */
|
|
137
|
+
async remoteGet(path, name, opts = {}) {
|
|
138
|
+
const result = await this.run('/runtime/v1/git/remote_get', {
|
|
139
|
+
path,
|
|
140
|
+
name,
|
|
141
|
+
...gitOpts(opts),
|
|
142
|
+
}, opts);
|
|
143
|
+
return result.value ?? result.url;
|
|
144
|
+
}
|
|
106
145
|
/** Set a Git config value. */
|
|
107
146
|
async setConfig(key, value, opts = {}) {
|
|
108
147
|
return this.run('/runtime/v1/git/set_config', {
|
|
@@ -131,6 +170,8 @@ export class Git {
|
|
|
131
170
|
function gitOpts(opts) {
|
|
132
171
|
return {
|
|
133
172
|
env_vars: opts.envs,
|
|
173
|
+
user: opts.user,
|
|
174
|
+
cwd: opts.cwd,
|
|
134
175
|
timeout_seconds: opts.timeout ?? (opts.timeoutMs === undefined ? undefined : Math.ceil(opts.timeoutMs / 1000)),
|
|
135
176
|
};
|
|
136
177
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
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
|
|
3
|
+
export { Sandbox, SandboxPaginator, SnapshotPaginator } from './sandbox.js';
|
|
4
|
+
export { Sandbox as CodeInterpreterSandbox } from './codeInterpreter.js';
|
|
5
|
+
export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxCreateOpts, SandboxConnectOpts, SandboxInfo, SandboxListOpts, SandboxMetrics, McpServer, McpServerName, SandboxNetworkSelector, SandboxNetworkUpdate, SandboxNetworkUpdateOpts, SandboxUrlOpts, SnapshotInfo, FileUrlInfo, } from './sandbox.js';
|
|
6
|
+
export type { CreateCodeContextOpts, RunCodeLanguage, RunCodeOpts, } from './codeInterpreter.js';
|
|
7
|
+
export { Context as CodeInterpreterContext, Execution as CodeInterpreterExecution, ExecutionError as CodeInterpreterExecutionError, OutputMessage as CodeInterpreterOutputMessage, Result as CodeInterpreterResult, } from './codeInterpreter.js';
|
|
5
8
|
export { CommandExitError, CommandHandle, Commands } from './commands.js';
|
|
6
9
|
export type { CommandResult, CommandStartOpts, ProcessInfo } from './commands.js';
|
|
7
|
-
export {
|
|
8
|
-
export type {
|
|
10
|
+
export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
|
|
11
|
+
export type { ProcessOpts } from './process.js';
|
|
12
|
+
export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
|
|
13
|
+
export type { EntryInfo, FilesystemEvent, FilesystemReadOpts, FilesystemRequestOpts, FilesystemWriteOpts, WatchOpts, WriteData, WriteEntry, WriteInfo, } from './filesystem.js';
|
|
9
14
|
export { Git } from './git.js';
|
|
10
|
-
export type { GitAddOpts, GitAuthOpts, GitBranches, GitBranchOpts, GitCloneOpts, GitCommandResult, GitConfigOpts, GitConfigureUserOpts, GitCredentialOpts, GitCommitOpts, GitFileStatus, GitPullOpts, GitPushOpts, GitRemoteAddOpts, GitRequestOpts, GitStatus, } from './git.js';
|
|
15
|
+
export type { GitAddOpts, GitAuthOpts, GitBranches, GitBranchOpts, GitCloneOpts, GitCommandResult, GitConfigOpts, GitConfigureUserOpts, GitCredentialOpts, GitCommitOpts, GitFileStatus, GitInitOpts, GitPullOpts, GitPushOpts, GitRemoteAddOpts, GitResetMode, GitResetOpts, GitRestoreOpts, GitRequestOpts, GitStatus, } from './git.js';
|
|
11
16
|
export { Pty } from './pty.js';
|
|
12
17
|
export type { PtyConnectOpts, PtyCreateOpts, PtySize } from './pty.js';
|
|
18
|
+
export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
|
|
19
|
+
export type { TerminalOpts } from './terminal.js';
|
|
13
20
|
export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
|
|
21
|
+
export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, waitForUrl, } from './template.js';
|
|
22
|
+
export type { BuildInfo, BuildOptions, BuildStatusReason, CopyItem, GetBuildStatusOptions, LogEntry, ReadyCommand, TemplateBuildStatus, TemplateBuildStatusResponse, TemplateBuilder, TemplateClass, TemplateFactory, TemplateFinal, TemplateFromImage, TemplateOptions, TemplateTag, TemplateTagInfo, } from './template.js';
|