@lizard-build/sdk 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +90 -2
- package/dist/index.mjs +90 -2
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -25,7 +25,9 @@ interface ProcessOpts {
|
|
|
25
25
|
envs?: Record<string, string>;
|
|
26
26
|
user?: string;
|
|
27
27
|
workdir?: string;
|
|
28
|
+
/** Called with each stdout line as it is produced, rather than at the end. */
|
|
28
29
|
onStdout?: (data: string) => void;
|
|
30
|
+
/** Called with each stderr line as it is produced, rather than at the end. */
|
|
29
31
|
onStderr?: (data: string) => void;
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
@@ -62,6 +64,12 @@ declare class Process {
|
|
|
62
64
|
* ```
|
|
63
65
|
*/
|
|
64
66
|
exec(cmd: string, opts?: ProcessOpts): Promise<ProcessResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Read an SSE exec stream, handing each line to the caller as it arrives while
|
|
69
|
+
* still accumulating the full result. Events are `{stream, line}` for output and
|
|
70
|
+
* `{exitCode}` at the end.
|
|
71
|
+
*/
|
|
72
|
+
private consumeStream;
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
interface FsOpts {
|
|
@@ -70,8 +78,12 @@ interface FsOpts {
|
|
|
70
78
|
interface FileInfo {
|
|
71
79
|
name: string;
|
|
72
80
|
path: string;
|
|
73
|
-
type: 'file' | 'dir';
|
|
81
|
+
type: 'file' | 'dir' | 'symlink';
|
|
74
82
|
size: number;
|
|
83
|
+
/** Permission bits as a string, e.g. `-rw-r--r--`. Absent on older sandboxes. */
|
|
84
|
+
mode?: string;
|
|
85
|
+
/** Last modification time, unix milliseconds. Absent on older sandboxes. */
|
|
86
|
+
modTime?: number;
|
|
75
87
|
}
|
|
76
88
|
/**
|
|
77
89
|
* Read and write files inside a Lizard sandbox microVM.
|
|
@@ -126,6 +138,35 @@ declare class Fs {
|
|
|
126
138
|
* Create a directory (and any missing parents) inside the microVM.
|
|
127
139
|
*/
|
|
128
140
|
makeDir(path: string, opts?: FsOpts): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Metadata for a single path — size, type, permissions, modification time.
|
|
143
|
+
*
|
|
144
|
+
* Saves listing a parent directory and filtering it just to answer "does this
|
|
145
|
+
* exist, and how big is it".
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* const info = await sandbox.fs.stat('/app/out.bin')
|
|
150
|
+
* console.log(info.size, info.type)
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* Throws `NotFoundError` if the path does not exist. Sandboxes created before
|
|
154
|
+
* this shipped run a guest agent without it and throw `LizardError` (501) —
|
|
155
|
+
* recreate the sandbox to use it.
|
|
156
|
+
*/
|
|
157
|
+
stat(path: string, opts?: FsOpts): Promise<FileInfo>;
|
|
158
|
+
/**
|
|
159
|
+
* Move or rename a path. Creates the destination's parent directories.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* await sandbox.fs.move('/tmp/build.log', '/app/logs/build.log')
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* Sandboxes created before this shipped run a guest agent without it and throw
|
|
167
|
+
* `LizardError` (501) — recreate the sandbox to use it.
|
|
168
|
+
*/
|
|
169
|
+
move(from: string, to: string): Promise<void>;
|
|
129
170
|
private execInternal;
|
|
130
171
|
}
|
|
131
172
|
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,9 @@ interface ProcessOpts {
|
|
|
25
25
|
envs?: Record<string, string>;
|
|
26
26
|
user?: string;
|
|
27
27
|
workdir?: string;
|
|
28
|
+
/** Called with each stdout line as it is produced, rather than at the end. */
|
|
28
29
|
onStdout?: (data: string) => void;
|
|
30
|
+
/** Called with each stderr line as it is produced, rather than at the end. */
|
|
29
31
|
onStderr?: (data: string) => void;
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
@@ -62,6 +64,12 @@ declare class Process {
|
|
|
62
64
|
* ```
|
|
63
65
|
*/
|
|
64
66
|
exec(cmd: string, opts?: ProcessOpts): Promise<ProcessResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Read an SSE exec stream, handing each line to the caller as it arrives while
|
|
69
|
+
* still accumulating the full result. Events are `{stream, line}` for output and
|
|
70
|
+
* `{exitCode}` at the end.
|
|
71
|
+
*/
|
|
72
|
+
private consumeStream;
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
interface FsOpts {
|
|
@@ -70,8 +78,12 @@ interface FsOpts {
|
|
|
70
78
|
interface FileInfo {
|
|
71
79
|
name: string;
|
|
72
80
|
path: string;
|
|
73
|
-
type: 'file' | 'dir';
|
|
81
|
+
type: 'file' | 'dir' | 'symlink';
|
|
74
82
|
size: number;
|
|
83
|
+
/** Permission bits as a string, e.g. `-rw-r--r--`. Absent on older sandboxes. */
|
|
84
|
+
mode?: string;
|
|
85
|
+
/** Last modification time, unix milliseconds. Absent on older sandboxes. */
|
|
86
|
+
modTime?: number;
|
|
75
87
|
}
|
|
76
88
|
/**
|
|
77
89
|
* Read and write files inside a Lizard sandbox microVM.
|
|
@@ -126,6 +138,35 @@ declare class Fs {
|
|
|
126
138
|
* Create a directory (and any missing parents) inside the microVM.
|
|
127
139
|
*/
|
|
128
140
|
makeDir(path: string, opts?: FsOpts): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Metadata for a single path — size, type, permissions, modification time.
|
|
143
|
+
*
|
|
144
|
+
* Saves listing a parent directory and filtering it just to answer "does this
|
|
145
|
+
* exist, and how big is it".
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* const info = await sandbox.fs.stat('/app/out.bin')
|
|
150
|
+
* console.log(info.size, info.type)
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* Throws `NotFoundError` if the path does not exist. Sandboxes created before
|
|
154
|
+
* this shipped run a guest agent without it and throw `LizardError` (501) —
|
|
155
|
+
* recreate the sandbox to use it.
|
|
156
|
+
*/
|
|
157
|
+
stat(path: string, opts?: FsOpts): Promise<FileInfo>;
|
|
158
|
+
/**
|
|
159
|
+
* Move or rename a path. Creates the destination's parent directories.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* await sandbox.fs.move('/tmp/build.log', '/app/logs/build.log')
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* Sandboxes created before this shipped run a guest agent without it and throw
|
|
167
|
+
* `LizardError` (501) — recreate the sandbox to use it.
|
|
168
|
+
*/
|
|
169
|
+
move(from: string, to: string): Promise<void>;
|
|
129
170
|
private execInternal;
|
|
130
171
|
}
|
|
131
172
|
|
package/dist/index.js
CHANGED
|
@@ -163,9 +163,10 @@ var Process = class {
|
|
|
163
163
|
* ```
|
|
164
164
|
*/
|
|
165
165
|
async exec(cmd, opts) {
|
|
166
|
+
const streaming = Boolean(opts?.onStdout || opts?.onStderr);
|
|
166
167
|
const res = await fetch(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/exec`, {
|
|
167
168
|
method: "POST",
|
|
168
|
-
headers: this.config.headers,
|
|
169
|
+
headers: streaming ? { ...this.config.headers, Accept: "text/event-stream" } : this.config.headers,
|
|
169
170
|
body: JSON.stringify({
|
|
170
171
|
cmd,
|
|
171
172
|
envs: opts?.envs,
|
|
@@ -176,7 +177,51 @@ var Process = class {
|
|
|
176
177
|
signal: AbortSignal.timeout(opts?.timeoutMs ?? 6e4)
|
|
177
178
|
});
|
|
178
179
|
if (!res.ok) await handleApiError(res);
|
|
179
|
-
return res.json();
|
|
180
|
+
if (!streaming) return res.json();
|
|
181
|
+
return this.consumeStream(res, opts);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Read an SSE exec stream, handing each line to the caller as it arrives while
|
|
185
|
+
* still accumulating the full result. Events are `{stream, line}` for output and
|
|
186
|
+
* `{exitCode}` at the end.
|
|
187
|
+
*/
|
|
188
|
+
async consumeStream(res, opts) {
|
|
189
|
+
const reader = res.body?.getReader();
|
|
190
|
+
if (!reader) return { stdout: "", stderr: "", exitCode: 0 };
|
|
191
|
+
const decoder = new TextDecoder();
|
|
192
|
+
let buf = "";
|
|
193
|
+
let stdout = "";
|
|
194
|
+
let stderr = "";
|
|
195
|
+
let exitCode = 0;
|
|
196
|
+
const handle = (raw) => {
|
|
197
|
+
if (!raw.startsWith("data:")) return;
|
|
198
|
+
let ev;
|
|
199
|
+
try {
|
|
200
|
+
ev = JSON.parse(raw.slice(5).trim());
|
|
201
|
+
} catch {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (ev.line !== void 0) {
|
|
205
|
+
if (ev.stream === "stderr") {
|
|
206
|
+
stderr += ev.line + "\n";
|
|
207
|
+
opts.onStderr?.(ev.line);
|
|
208
|
+
} else {
|
|
209
|
+
stdout += ev.line + "\n";
|
|
210
|
+
opts.onStdout?.(ev.line);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (ev.exitCode !== void 0) exitCode = ev.exitCode;
|
|
214
|
+
};
|
|
215
|
+
for (; ; ) {
|
|
216
|
+
const { done, value } = await reader.read();
|
|
217
|
+
if (done) break;
|
|
218
|
+
buf += decoder.decode(value, { stream: true });
|
|
219
|
+
const lines = buf.split("\n");
|
|
220
|
+
buf = lines.pop() ?? "";
|
|
221
|
+
for (const l of lines) handle(l);
|
|
222
|
+
}
|
|
223
|
+
if (buf) handle(buf);
|
|
224
|
+
return { stdout: stdout.trimEnd(), stderr: stderr.trimEnd(), exitCode };
|
|
180
225
|
}
|
|
181
226
|
};
|
|
182
227
|
|
|
@@ -261,6 +306,49 @@ var Fs = class {
|
|
|
261
306
|
async makeDir(path, opts) {
|
|
262
307
|
await this.execInternal(`mkdir -p ${JSON.stringify(path)}`, opts?.user);
|
|
263
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Metadata for a single path — size, type, permissions, modification time.
|
|
311
|
+
*
|
|
312
|
+
* Saves listing a parent directory and filtering it just to answer "does this
|
|
313
|
+
* exist, and how big is it".
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* const info = await sandbox.fs.stat('/app/out.bin')
|
|
318
|
+
* console.log(info.size, info.type)
|
|
319
|
+
* ```
|
|
320
|
+
*
|
|
321
|
+
* Throws `NotFoundError` if the path does not exist. Sandboxes created before
|
|
322
|
+
* this shipped run a guest agent without it and throw `LizardError` (501) —
|
|
323
|
+
* recreate the sandbox to use it.
|
|
324
|
+
*/
|
|
325
|
+
async stat(path, opts) {
|
|
326
|
+
const url = new URL(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/files/stat`);
|
|
327
|
+
url.searchParams.set("path", path);
|
|
328
|
+
if (opts?.user) url.searchParams.set("user", opts.user);
|
|
329
|
+
const res = await fetch(url.toString(), { headers: this.config.headers });
|
|
330
|
+
if (!res.ok) await handleApiError(res);
|
|
331
|
+
return res.json();
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Move or rename a path. Creates the destination's parent directories.
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* ```ts
|
|
338
|
+
* await sandbox.fs.move('/tmp/build.log', '/app/logs/build.log')
|
|
339
|
+
* ```
|
|
340
|
+
*
|
|
341
|
+
* Sandboxes created before this shipped run a guest agent without it and throw
|
|
342
|
+
* `LizardError` (501) — recreate the sandbox to use it.
|
|
343
|
+
*/
|
|
344
|
+
async move(from, to) {
|
|
345
|
+
const res = await fetch(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/files/move`, {
|
|
346
|
+
method: "POST",
|
|
347
|
+
headers: this.config.headers,
|
|
348
|
+
body: JSON.stringify({ from, to })
|
|
349
|
+
});
|
|
350
|
+
if (!res.ok) await handleApiError(res);
|
|
351
|
+
}
|
|
264
352
|
async execInternal(cmd, user) {
|
|
265
353
|
const res = await fetch(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/exec`, {
|
|
266
354
|
method: "POST",
|
package/dist/index.mjs
CHANGED
|
@@ -127,9 +127,10 @@ var Process = class {
|
|
|
127
127
|
* ```
|
|
128
128
|
*/
|
|
129
129
|
async exec(cmd, opts) {
|
|
130
|
+
const streaming = Boolean(opts?.onStdout || opts?.onStderr);
|
|
130
131
|
const res = await fetch(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/exec`, {
|
|
131
132
|
method: "POST",
|
|
132
|
-
headers: this.config.headers,
|
|
133
|
+
headers: streaming ? { ...this.config.headers, Accept: "text/event-stream" } : this.config.headers,
|
|
133
134
|
body: JSON.stringify({
|
|
134
135
|
cmd,
|
|
135
136
|
envs: opts?.envs,
|
|
@@ -140,7 +141,51 @@ var Process = class {
|
|
|
140
141
|
signal: AbortSignal.timeout(opts?.timeoutMs ?? 6e4)
|
|
141
142
|
});
|
|
142
143
|
if (!res.ok) await handleApiError(res);
|
|
143
|
-
return res.json();
|
|
144
|
+
if (!streaming) return res.json();
|
|
145
|
+
return this.consumeStream(res, opts);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Read an SSE exec stream, handing each line to the caller as it arrives while
|
|
149
|
+
* still accumulating the full result. Events are `{stream, line}` for output and
|
|
150
|
+
* `{exitCode}` at the end.
|
|
151
|
+
*/
|
|
152
|
+
async consumeStream(res, opts) {
|
|
153
|
+
const reader = res.body?.getReader();
|
|
154
|
+
if (!reader) return { stdout: "", stderr: "", exitCode: 0 };
|
|
155
|
+
const decoder = new TextDecoder();
|
|
156
|
+
let buf = "";
|
|
157
|
+
let stdout = "";
|
|
158
|
+
let stderr = "";
|
|
159
|
+
let exitCode = 0;
|
|
160
|
+
const handle = (raw) => {
|
|
161
|
+
if (!raw.startsWith("data:")) return;
|
|
162
|
+
let ev;
|
|
163
|
+
try {
|
|
164
|
+
ev = JSON.parse(raw.slice(5).trim());
|
|
165
|
+
} catch {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (ev.line !== void 0) {
|
|
169
|
+
if (ev.stream === "stderr") {
|
|
170
|
+
stderr += ev.line + "\n";
|
|
171
|
+
opts.onStderr?.(ev.line);
|
|
172
|
+
} else {
|
|
173
|
+
stdout += ev.line + "\n";
|
|
174
|
+
opts.onStdout?.(ev.line);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (ev.exitCode !== void 0) exitCode = ev.exitCode;
|
|
178
|
+
};
|
|
179
|
+
for (; ; ) {
|
|
180
|
+
const { done, value } = await reader.read();
|
|
181
|
+
if (done) break;
|
|
182
|
+
buf += decoder.decode(value, { stream: true });
|
|
183
|
+
const lines = buf.split("\n");
|
|
184
|
+
buf = lines.pop() ?? "";
|
|
185
|
+
for (const l of lines) handle(l);
|
|
186
|
+
}
|
|
187
|
+
if (buf) handle(buf);
|
|
188
|
+
return { stdout: stdout.trimEnd(), stderr: stderr.trimEnd(), exitCode };
|
|
144
189
|
}
|
|
145
190
|
};
|
|
146
191
|
|
|
@@ -225,6 +270,49 @@ var Fs = class {
|
|
|
225
270
|
async makeDir(path, opts) {
|
|
226
271
|
await this.execInternal(`mkdir -p ${JSON.stringify(path)}`, opts?.user);
|
|
227
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Metadata for a single path — size, type, permissions, modification time.
|
|
275
|
+
*
|
|
276
|
+
* Saves listing a parent directory and filtering it just to answer "does this
|
|
277
|
+
* exist, and how big is it".
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```ts
|
|
281
|
+
* const info = await sandbox.fs.stat('/app/out.bin')
|
|
282
|
+
* console.log(info.size, info.type)
|
|
283
|
+
* ```
|
|
284
|
+
*
|
|
285
|
+
* Throws `NotFoundError` if the path does not exist. Sandboxes created before
|
|
286
|
+
* this shipped run a guest agent without it and throw `LizardError` (501) —
|
|
287
|
+
* recreate the sandbox to use it.
|
|
288
|
+
*/
|
|
289
|
+
async stat(path, opts) {
|
|
290
|
+
const url = new URL(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/files/stat`);
|
|
291
|
+
url.searchParams.set("path", path);
|
|
292
|
+
if (opts?.user) url.searchParams.set("user", opts.user);
|
|
293
|
+
const res = await fetch(url.toString(), { headers: this.config.headers });
|
|
294
|
+
if (!res.ok) await handleApiError(res);
|
|
295
|
+
return res.json();
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Move or rename a path. Creates the destination's parent directories.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```ts
|
|
302
|
+
* await sandbox.fs.move('/tmp/build.log', '/app/logs/build.log')
|
|
303
|
+
* ```
|
|
304
|
+
*
|
|
305
|
+
* Sandboxes created before this shipped run a guest agent without it and throw
|
|
306
|
+
* `LizardError` (501) — recreate the sandbox to use it.
|
|
307
|
+
*/
|
|
308
|
+
async move(from, to) {
|
|
309
|
+
const res = await fetch(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/files/move`, {
|
|
310
|
+
method: "POST",
|
|
311
|
+
headers: this.config.headers,
|
|
312
|
+
body: JSON.stringify({ from, to })
|
|
313
|
+
});
|
|
314
|
+
if (!res.ok) await handleApiError(res);
|
|
315
|
+
}
|
|
228
316
|
async execInternal(cmd, user) {
|
|
229
317
|
const res = await fetch(`${this.config.apiUrl}/api/sandboxes/${this.sandboxId}/exec`, {
|
|
230
318
|
method: "POST",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lizard-build/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "Firecracker microVM sandboxes for AI agents — boot Linux in milliseconds, run code, write files, expose ports over HTTPS, then pause a session with memory intact and resume it later. TypeScript SDK for Lizard Sandboxes (lizard.build).",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|