@lizard-build/sdk 0.1.31 → 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 +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +47 -2
- package/dist/index.mjs +47 -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 {
|
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 {
|
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
|
|
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
|
|
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",
|