@watasu/sdk 0.1.6 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -8
- 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 +12 -6
- package/dist/index.js +6 -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 +118 -19
- package/dist/sandbox.js +246 -42
- package/dist/template.d.ts +232 -0
- package/dist/template.js +624 -0
- package/dist/terminal.d.ts +41 -0
- package/dist/terminal.js +74 -0
- package/dist/transport.d.ts +1 -0
- package/dist/transport.js +3 -0
- package/package.json +1 -1
package/dist/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,19 @@
|
|
|
1
|
-
export { ApiError, AuthenticationError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
|
|
1
|
+
export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
|
|
2
2
|
export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
|
|
3
|
-
export { Sandbox, SnapshotPaginator } from './sandbox.js';
|
|
4
|
-
export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxCreateOpts, SandboxConnectOpts, SandboxInfo, SandboxMetrics, SandboxUrlOpts, SnapshotInfo, FileUrlInfo, } from './sandbox.js';
|
|
3
|
+
export { Sandbox, SandboxPaginator, SnapshotPaginator } from './sandbox.js';
|
|
4
|
+
export type { CreateSnapshotOpts, RestoreSnapshotOpts, SandboxCreateOpts, SandboxConnectOpts, SandboxInfo, SandboxListOpts, SandboxMetrics, McpServer, McpServerName, SandboxNetworkSelector, SandboxNetworkUpdate, SandboxNetworkUpdateOpts, SandboxUrlOpts, SnapshotInfo, FileUrlInfo, } from './sandbox.js';
|
|
5
5
|
export { CommandExitError, CommandHandle, Commands } from './commands.js';
|
|
6
6
|
export type { CommandResult, CommandStartOpts, ProcessInfo } from './commands.js';
|
|
7
|
-
export {
|
|
8
|
-
export type {
|
|
7
|
+
export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
|
|
8
|
+
export type { ProcessOpts } from './process.js';
|
|
9
|
+
export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
|
|
10
|
+
export type { EntryInfo, FilesystemEvent, FilesystemReadOpts, FilesystemRequestOpts, FilesystemWriteOpts, WatchOpts, WriteData, WriteEntry, WriteInfo, } from './filesystem.js';
|
|
9
11
|
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';
|
|
12
|
+
export type { GitAddOpts, GitAuthOpts, GitBranches, GitBranchOpts, GitCloneOpts, GitCommandResult, GitConfigOpts, GitConfigureUserOpts, GitCredentialOpts, GitCommitOpts, GitFileStatus, GitInitOpts, GitPullOpts, GitPushOpts, GitRemoteAddOpts, GitResetMode, GitResetOpts, GitRestoreOpts, GitRequestOpts, GitStatus, } from './git.js';
|
|
11
13
|
export { Pty } from './pty.js';
|
|
12
14
|
export type { PtyConnectOpts, PtyCreateOpts, PtySize } from './pty.js';
|
|
15
|
+
export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
|
|
16
|
+
export type { TerminalOpts } from './terminal.js';
|
|
13
17
|
export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
|
|
18
|
+
export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, waitForUrl, } from './template.js';
|
|
19
|
+
export type { BuildInfo, BuildOptions, BuildStatusReason, CopyItem, GetBuildStatusOptions, LogEntry, ReadyCommand, TemplateBuildStatus, TemplateBuildStatusResponse, TemplateBuilder, TemplateClass, TemplateFactory, TemplateFinal, TemplateFromImage, TemplateOptions, TemplateTag, TemplateTagInfo, } from './template.js';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
export { ApiError, AuthenticationError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
|
|
1
|
+
export { ApiError, AuthenticationError, ConflictError, FileNotFoundError, InvalidArgumentError, NotEnoughSpaceError, NotFoundError, NotImplementedError, RateLimitError, SandboxError, TimeoutError, } from './errors.js';
|
|
2
2
|
export { ConnectionConfig, KEEPALIVE_PING_INTERVAL_SEC } from './connectionConfig.js';
|
|
3
|
-
export { Sandbox, SnapshotPaginator } from './sandbox.js';
|
|
3
|
+
export { Sandbox, SandboxPaginator, SnapshotPaginator } from './sandbox.js';
|
|
4
4
|
export { CommandExitError, CommandHandle, Commands } from './commands.js';
|
|
5
|
-
export {
|
|
5
|
+
export { Process, ProcessManager, ProcessMessage, ProcessOutput } from './process.js';
|
|
6
|
+
export { FileType, Filesystem, FilesystemWatcher, WatchHandle } from './filesystem.js';
|
|
6
7
|
export { Git } from './git.js';
|
|
7
8
|
export { Pty } from './pty.js';
|
|
9
|
+
export { Terminal, TerminalManager, TerminalOutput } from './terminal.js';
|
|
8
10
|
export { ProcessSocket, base64DecodeBytes, base64DecodeText, base64Encode } from './processSocket.js';
|
|
11
|
+
export { ReadyCmd, Template, TemplateBase, waitForFile, waitForPort, waitForProcess, waitForTimeout, waitForURL, waitForUrl, } from './template.js';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { CommandHandle, CommandResult, Commands, CommandStartOpts } from './commands.js';
|
|
2
|
+
/** A message emitted by a sandbox process. */
|
|
3
|
+
export declare class ProcessMessage {
|
|
4
|
+
readonly line: string;
|
|
5
|
+
/** Unix epoch in nanoseconds. */
|
|
6
|
+
readonly timestamp: number;
|
|
7
|
+
readonly error: boolean;
|
|
8
|
+
constructor(line: string,
|
|
9
|
+
/** Unix epoch in nanoseconds. */
|
|
10
|
+
timestamp: number, error: boolean);
|
|
11
|
+
toString(): string;
|
|
12
|
+
}
|
|
13
|
+
/** Captured output from a sandbox process. */
|
|
14
|
+
export declare class ProcessOutput {
|
|
15
|
+
private messages;
|
|
16
|
+
private _finished;
|
|
17
|
+
private _error;
|
|
18
|
+
private _exitCode;
|
|
19
|
+
get error(): boolean;
|
|
20
|
+
get exitCode(): number | undefined;
|
|
21
|
+
get stdout(): string;
|
|
22
|
+
get stderr(): string;
|
|
23
|
+
addStdout(message: ProcessMessage): void;
|
|
24
|
+
addStderr(message: ProcessMessage): void;
|
|
25
|
+
setExitCode(exitCode: number): void;
|
|
26
|
+
replace(result: CommandResult): void;
|
|
27
|
+
}
|
|
28
|
+
export interface ProcessOpts extends Omit<CommandStartOpts, 'cmd' | 'args' | 'onStdout' | 'onStderr' | 'onExit'> {
|
|
29
|
+
cmd: string;
|
|
30
|
+
onStdout?: (out: ProcessMessage) => Promise<void> | void;
|
|
31
|
+
onStderr?: (out: ProcessMessage) => Promise<void> | void;
|
|
32
|
+
onExit?: ((exitCode: number) => Promise<void> | void) | (() => Promise<void> | void);
|
|
33
|
+
}
|
|
34
|
+
/** A running sandbox process. */
|
|
35
|
+
export declare class Process {
|
|
36
|
+
readonly processID: string;
|
|
37
|
+
private readonly handle;
|
|
38
|
+
readonly output: ProcessOutput;
|
|
39
|
+
private readonly onExit?;
|
|
40
|
+
readonly finished: Promise<ProcessOutput>;
|
|
41
|
+
private waitPromise?;
|
|
42
|
+
constructor(processID: string, handle: CommandHandle, output: ProcessOutput, onExit?: ((exitCode: number) => Promise<void> | void) | undefined);
|
|
43
|
+
kill(): Promise<void>;
|
|
44
|
+
wait(timeout?: number): Promise<ProcessOutput>;
|
|
45
|
+
private waitOnce;
|
|
46
|
+
sendStdin(data: string): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/** Manager for starting and interacting with sandbox processes. */
|
|
49
|
+
export declare class ProcessManager {
|
|
50
|
+
private readonly commands;
|
|
51
|
+
constructor(commands: Commands);
|
|
52
|
+
start(cmd: string): Promise<Process>;
|
|
53
|
+
start(opts: ProcessOpts): Promise<Process>;
|
|
54
|
+
startAndWait(cmd: string): Promise<ProcessOutput>;
|
|
55
|
+
startAndWait(opts: ProcessOpts): Promise<ProcessOutput>;
|
|
56
|
+
}
|
package/dist/process.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { CommandExitError } from './commands.js';
|
|
2
|
+
import { TimeoutError } from './errors.js';
|
|
3
|
+
/** A message emitted by a sandbox process. */
|
|
4
|
+
export class ProcessMessage {
|
|
5
|
+
line;
|
|
6
|
+
timestamp;
|
|
7
|
+
error;
|
|
8
|
+
constructor(line,
|
|
9
|
+
/** Unix epoch in nanoseconds. */
|
|
10
|
+
timestamp, error) {
|
|
11
|
+
this.line = line;
|
|
12
|
+
this.timestamp = timestamp;
|
|
13
|
+
this.error = error;
|
|
14
|
+
}
|
|
15
|
+
toString() {
|
|
16
|
+
return this.line;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Captured output from a sandbox process. */
|
|
20
|
+
export class ProcessOutput {
|
|
21
|
+
messages = [];
|
|
22
|
+
_finished = false;
|
|
23
|
+
_error = false;
|
|
24
|
+
_exitCode;
|
|
25
|
+
get error() { return this._error; }
|
|
26
|
+
get exitCode() { return this._exitCode; }
|
|
27
|
+
get stdout() { return this.messages.filter((message) => !message.error).map(String).join(''); }
|
|
28
|
+
get stderr() { return this.messages.filter((message) => message.error).map(String).join(''); }
|
|
29
|
+
addStdout(message) {
|
|
30
|
+
this.messages.push(message);
|
|
31
|
+
}
|
|
32
|
+
addStderr(message) {
|
|
33
|
+
this.messages.push(message);
|
|
34
|
+
this._error = true;
|
|
35
|
+
}
|
|
36
|
+
setExitCode(exitCode) {
|
|
37
|
+
this._exitCode = exitCode;
|
|
38
|
+
this._finished = true;
|
|
39
|
+
if (exitCode !== 0)
|
|
40
|
+
this._error = true;
|
|
41
|
+
}
|
|
42
|
+
replace(result) {
|
|
43
|
+
this.messages = [];
|
|
44
|
+
if (result.stdout)
|
|
45
|
+
this.addStdout(processMessage(result.stdout, false));
|
|
46
|
+
if (result.stderr)
|
|
47
|
+
this.addStderr(processMessage(result.stderr, true));
|
|
48
|
+
this.setExitCode(result.exitCode);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** A running sandbox process. */
|
|
52
|
+
export class Process {
|
|
53
|
+
processID;
|
|
54
|
+
handle;
|
|
55
|
+
output;
|
|
56
|
+
onExit;
|
|
57
|
+
finished;
|
|
58
|
+
waitPromise;
|
|
59
|
+
constructor(processID, handle, output, onExit) {
|
|
60
|
+
this.processID = processID;
|
|
61
|
+
this.handle = handle;
|
|
62
|
+
this.output = output;
|
|
63
|
+
this.onExit = onExit;
|
|
64
|
+
this.waitPromise = this.waitOnce();
|
|
65
|
+
this.finished = this.waitPromise;
|
|
66
|
+
}
|
|
67
|
+
async kill() {
|
|
68
|
+
await this.handle.kill();
|
|
69
|
+
}
|
|
70
|
+
async wait(timeout) {
|
|
71
|
+
if (!this.waitPromise)
|
|
72
|
+
this.waitPromise = this.waitOnce();
|
|
73
|
+
return waitFor(this.waitPromise, timeout);
|
|
74
|
+
}
|
|
75
|
+
async waitOnce() {
|
|
76
|
+
try {
|
|
77
|
+
this.output.replace(await this.handle.wait());
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error instanceof CommandExitError) {
|
|
81
|
+
this.output.replace(error);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await this.onExit?.(this.output.exitCode ?? 0);
|
|
88
|
+
return this.output;
|
|
89
|
+
}
|
|
90
|
+
async sendStdin(data) {
|
|
91
|
+
await this.handle.sendStdin(data);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function waitFor(promise, timeoutMs) {
|
|
95
|
+
if (timeoutMs === undefined || timeoutMs <= 0)
|
|
96
|
+
return promise;
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
const timer = setTimeout(() => reject(new TimeoutError()), timeoutMs);
|
|
99
|
+
promise.then(resolve, reject).finally(() => clearTimeout(timer));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** Manager for starting and interacting with sandbox processes. */
|
|
103
|
+
export class ProcessManager {
|
|
104
|
+
commands;
|
|
105
|
+
constructor(commands) {
|
|
106
|
+
this.commands = commands;
|
|
107
|
+
}
|
|
108
|
+
async start(cmdOrOpts) {
|
|
109
|
+
const opts = processOpts(cmdOrOpts);
|
|
110
|
+
const { cmd, onStdout, onStderr, onExit, ...commandOpts } = opts;
|
|
111
|
+
const output = new ProcessOutput();
|
|
112
|
+
const handle = await this.commands.start(cmd, {
|
|
113
|
+
...commandOpts,
|
|
114
|
+
onStdout: async (data) => {
|
|
115
|
+
const message = processMessage(data, false);
|
|
116
|
+
output.addStdout(message);
|
|
117
|
+
await onStdout?.(message);
|
|
118
|
+
},
|
|
119
|
+
onStderr: async (data) => {
|
|
120
|
+
const message = processMessage(data, true);
|
|
121
|
+
output.addStderr(message);
|
|
122
|
+
await onStderr?.(message);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return new Process(String(handle.pid), handle, output, onExit);
|
|
126
|
+
}
|
|
127
|
+
async startAndWait(cmdOrOpts) {
|
|
128
|
+
const process = await this.start(cmdOrOpts);
|
|
129
|
+
return process.wait(typeof cmdOrOpts === 'string' ? undefined : cmdOrOpts.timeout);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function processOpts(cmdOrOpts) {
|
|
133
|
+
return typeof cmdOrOpts === 'string' ? { cmd: cmdOrOpts } : cmdOrOpts;
|
|
134
|
+
}
|
|
135
|
+
function processMessage(line, error) {
|
|
136
|
+
return new ProcessMessage(line, Date.now() * 1_000_000, error);
|
|
137
|
+
}
|
package/dist/processSocket.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare class ProcessSocket implements AsyncIterable<ProcessFrame> {
|
|
|
14
14
|
connect(): Promise<this>;
|
|
15
15
|
sendJson(payload: ProcessFrame): void;
|
|
16
16
|
sendStdin(data: string | Uint8Array): void;
|
|
17
|
+
closeStdin(): void;
|
|
17
18
|
close(): void;
|
|
18
19
|
[Symbol.asyncIterator](): AsyncIterator<ProcessFrame>;
|
|
19
20
|
private next;
|
package/dist/processSocket.js
CHANGED
|
@@ -55,6 +55,9 @@ export class ProcessSocket {
|
|
|
55
55
|
const raw = typeof data === 'string' ? new TextEncoder().encode(data) : data;
|
|
56
56
|
this.sendJson({ type: 'stdin', data: base64Encode(raw) });
|
|
57
57
|
}
|
|
58
|
+
closeStdin() {
|
|
59
|
+
this.sendJson({ type: 'close_stdin' });
|
|
60
|
+
}
|
|
58
61
|
close() {
|
|
59
62
|
this.closed = true;
|
|
60
63
|
if (this.keepalive)
|
package/dist/pty.d.ts
CHANGED
|
@@ -5,7 +5,11 @@ export interface PtySize {
|
|
|
5
5
|
cols: number;
|
|
6
6
|
rows: number;
|
|
7
7
|
}
|
|
8
|
-
export interface PtyCreateOpts extends Omit<CommandStartOpts, 'background' | 'onStdout' | 'onStderr'
|
|
8
|
+
export interface PtyCreateOpts extends Omit<CommandStartOpts, 'background' | 'onStdout' | 'onStderr'> {
|
|
9
|
+
cols?: number;
|
|
10
|
+
rows?: number;
|
|
11
|
+
size?: PtySize;
|
|
12
|
+
cmd?: string;
|
|
9
13
|
onData?: (data: Uint8Array) => void | Promise<void>;
|
|
10
14
|
}
|
|
11
15
|
export interface PtyConnectOpts {
|
package/dist/pty.js
CHANGED
|
@@ -12,18 +12,20 @@ export class Pty {
|
|
|
12
12
|
/** Create an interactive shell PTY and return its live command handle. */
|
|
13
13
|
async create(opts) {
|
|
14
14
|
const socket = await new ProcessSocket(this.dataPlane.baseUrl, this.dataPlane.token, '/runtime/v1/process', opts.requestTimeoutMs ?? this.config.requestTimeoutMs).connect();
|
|
15
|
-
const envs = { TERM: 'xterm-256color', LANG: 'C.UTF-8', LC_ALL: 'C.UTF-8', ...(opts.envs ?? {}) };
|
|
15
|
+
const envs = { TERM: 'xterm-256color', LANG: 'C.UTF-8', LC_ALL: 'C.UTF-8', ...(opts.envVars ?? opts.envs ?? {}) };
|
|
16
|
+
const size = opts.size ?? { cols: opts.cols ?? 80, rows: opts.rows ?? 24 };
|
|
17
|
+
const args = opts.cmd === undefined ? ['-i', '-l'] : ['-l', '-c', opts.cmd];
|
|
16
18
|
socket.sendJson({
|
|
17
19
|
type: 'start',
|
|
18
20
|
cmd: '/bin/bash',
|
|
19
|
-
args
|
|
20
|
-
cwd: opts.cwd,
|
|
21
|
+
args,
|
|
22
|
+
cwd: opts.cwd ?? opts.rootDir,
|
|
21
23
|
user: opts.user,
|
|
22
24
|
environment: envs,
|
|
23
25
|
envs,
|
|
24
26
|
stdin: true,
|
|
25
|
-
pty: { cols:
|
|
26
|
-
timeout_ms: opts.timeoutMs ?? 60_000,
|
|
27
|
+
pty: { cols: size.cols, rows: size.rows },
|
|
28
|
+
timeout_ms: opts.timeoutMs ?? opts.timeout ?? 60_000,
|
|
27
29
|
});
|
|
28
30
|
const first = await nextStarted(socket);
|
|
29
31
|
const pid = framePid(first);
|
|
@@ -45,7 +47,7 @@ export class Pty {
|
|
|
45
47
|
await handle.sendStdin(data);
|
|
46
48
|
}
|
|
47
49
|
finally {
|
|
48
|
-
handle.disconnect();
|
|
50
|
+
await handle.disconnect();
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
/** Alias for `sendStdin`. */
|
|
@@ -59,7 +61,7 @@ export class Pty {
|
|
|
59
61
|
await handle.resize(size);
|
|
60
62
|
}
|
|
61
63
|
finally {
|
|
62
|
-
handle.disconnect();
|
|
64
|
+
await handle.disconnect();
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
/** Kill a running PTY. */
|