@fuzdev/fuz_app 0.26.0 → 0.27.0
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/runtime/deno.d.ts.map +1 -1
- package/dist/runtime/deno.js +66 -13
- package/dist/runtime/deps.d.ts +48 -4
- package/dist/runtime/deps.d.ts.map +1 -1
- package/dist/runtime/mock.d.ts +3 -2
- package/dist/runtime/mock.d.ts.map +1 -1
- package/dist/runtime/mock.js +60 -4
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +58 -6
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deno.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"deno.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;AAwDtE;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,WA4HhE,CAAC"}
|
package/dist/runtime/deno.js
CHANGED
|
@@ -38,6 +38,33 @@ export const create_deno_runtime = (args) => ({
|
|
|
38
38
|
mkdir: (path, options) => Deno.mkdir(path, options),
|
|
39
39
|
read_text_file: (path) => Deno.readTextFile(path),
|
|
40
40
|
read_file: (path) => Deno.readFile(path),
|
|
41
|
+
read_text_from_offset: async (path, offset) => {
|
|
42
|
+
const s = await Deno.stat(path);
|
|
43
|
+
const file_size = s.size;
|
|
44
|
+
const bytes_to_read = Math.max(0, file_size - offset);
|
|
45
|
+
if (bytes_to_read === 0)
|
|
46
|
+
return { content: '', bytes_read: 0, file_size };
|
|
47
|
+
const handle = await Deno.open(path, { read: true });
|
|
48
|
+
try {
|
|
49
|
+
await handle.seek(offset, Deno.SeekMode.Start);
|
|
50
|
+
const buffer = new Uint8Array(bytes_to_read);
|
|
51
|
+
const bytes_read = (await handle.read(buffer)) ?? 0;
|
|
52
|
+
return {
|
|
53
|
+
content: new TextDecoder().decode(buffer.subarray(0, bytes_read)),
|
|
54
|
+
bytes_read,
|
|
55
|
+
file_size,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
handle.close();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
readdir: async (path) => {
|
|
63
|
+
const names = [];
|
|
64
|
+
for await (const entry of Deno.readDir(path))
|
|
65
|
+
names.push(entry.name);
|
|
66
|
+
return names;
|
|
67
|
+
},
|
|
41
68
|
write_text_file: (path, content) => Deno.writeTextFile(path, content),
|
|
42
69
|
write_file: (path, data) => Deno.writeFile(path, data),
|
|
43
70
|
rename: (old_path, new_path) => Deno.rename(old_path, new_path),
|
|
@@ -45,20 +72,46 @@ export const create_deno_runtime = (args) => ({
|
|
|
45
72
|
// === HTTP ===
|
|
46
73
|
fetch: globalThis.fetch,
|
|
47
74
|
// === Local Commands ===
|
|
48
|
-
run_command: async (cmd, args) => {
|
|
75
|
+
run_command: async (cmd, args, options) => {
|
|
49
76
|
try {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
const controller = options?.timeout_ms !== undefined ? new AbortController() : null;
|
|
78
|
+
const signal = controller && options?.signal
|
|
79
|
+
? AbortSignal.any([controller.signal, options.signal])
|
|
80
|
+
: (controller?.signal ?? options?.signal);
|
|
81
|
+
const timer = controller && options?.timeout_ms !== undefined
|
|
82
|
+
? setTimeout(() => controller.abort(), options.timeout_ms)
|
|
83
|
+
: null;
|
|
84
|
+
let timed_out = false;
|
|
85
|
+
if (controller) {
|
|
86
|
+
controller.signal.addEventListener('abort', () => {
|
|
87
|
+
if (options?.signal?.aborted)
|
|
88
|
+
return;
|
|
89
|
+
timed_out = true;
|
|
90
|
+
}, { once: true });
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const proc = new Deno.Command(cmd, {
|
|
94
|
+
args,
|
|
95
|
+
cwd: options?.cwd,
|
|
96
|
+
signal,
|
|
97
|
+
stdout: 'piped',
|
|
98
|
+
stderr: 'piped',
|
|
99
|
+
});
|
|
100
|
+
const result = await proc.output();
|
|
101
|
+
const base = {
|
|
102
|
+
success: result.code === 0 && !timed_out,
|
|
103
|
+
code: result.code,
|
|
104
|
+
stdout: new TextDecoder().decode(result.stdout),
|
|
105
|
+
stderr: new TextDecoder().decode(result.stderr),
|
|
106
|
+
};
|
|
107
|
+
if (options?.timeout_ms !== undefined)
|
|
108
|
+
base.timed_out = timed_out;
|
|
109
|
+
return base;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
if (timer !== null)
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
}
|
|
62
115
|
}
|
|
63
116
|
catch (error) {
|
|
64
117
|
const message = error instanceof Error ? error.message : String(error);
|
package/dist/runtime/deps.d.ts
CHANGED
|
@@ -16,12 +16,28 @@ export interface StatResult {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Result of executing a command.
|
|
19
|
+
*
|
|
20
|
+
* `timed_out` is present only when `timeout_ms` was passed in `RunCommandOptions`
|
|
21
|
+
* and the process was killed after exceeding the timeout. Callers that pass
|
|
22
|
+
* `timeout_ms` should check this flag to distinguish timeout from exit-code failure.
|
|
19
23
|
*/
|
|
20
24
|
export interface CommandResult {
|
|
21
25
|
success: boolean;
|
|
22
26
|
code: number;
|
|
23
27
|
stdout: string;
|
|
24
28
|
stderr: string;
|
|
29
|
+
timed_out?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Options for `run_command`.
|
|
33
|
+
*/
|
|
34
|
+
export interface RunCommandOptions {
|
|
35
|
+
/** Working directory for the child process. */
|
|
36
|
+
cwd?: string;
|
|
37
|
+
/** AbortSignal to terminate the child process. */
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
/** Kill the process and return `timed_out: true` after this many milliseconds. */
|
|
40
|
+
timeout_ms?: number;
|
|
25
41
|
}
|
|
26
42
|
/**
|
|
27
43
|
* Environment variable access.
|
|
@@ -32,16 +48,37 @@ export interface EnvDeps {
|
|
|
32
48
|
/** Set an environment variable. */
|
|
33
49
|
env_set: (name: string, value: string) => void;
|
|
34
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Result of reading text from a byte offset.
|
|
53
|
+
*/
|
|
54
|
+
export interface ReadTextFromOffsetResult {
|
|
55
|
+
/** Decoded text content read from the offset. */
|
|
56
|
+
content: string;
|
|
57
|
+
/** Number of bytes actually read. */
|
|
58
|
+
bytes_read: number;
|
|
59
|
+
/** Total file size at the time of the read (for truncation detection). */
|
|
60
|
+
file_size: number;
|
|
61
|
+
}
|
|
35
62
|
/**
|
|
36
63
|
* File system read operations.
|
|
37
64
|
*/
|
|
38
65
|
export interface FsReadDeps {
|
|
39
66
|
/** Get file/directory stats, or null if path doesn't exist. */
|
|
40
67
|
stat: (path: string) => Promise<StatResult | null>;
|
|
41
|
-
/** Read a file as text. */
|
|
68
|
+
/** Read a file as text. Throws if the file does not exist. */
|
|
42
69
|
read_text_file: (path: string) => Promise<string>;
|
|
43
|
-
/** Read a file as bytes. */
|
|
70
|
+
/** Read a file as bytes. Throws if the file does not exist. */
|
|
44
71
|
read_file: (path: string) => Promise<Uint8Array>;
|
|
72
|
+
/**
|
|
73
|
+
* Read text starting from a byte offset. Throws if the file does not exist.
|
|
74
|
+
*
|
|
75
|
+
* Returns `content`, `bytes_read`, and `file_size` so callers can detect
|
|
76
|
+
* truncation (when `file_size < offset`) and tail incrementally without
|
|
77
|
+
* re-reading the whole file.
|
|
78
|
+
*/
|
|
79
|
+
read_text_from_offset: (path: string, offset: number) => Promise<ReadTextFromOffsetResult>;
|
|
80
|
+
/** List directory entries (names, not full paths). Throws if the directory does not exist. */
|
|
81
|
+
readdir: (path: string) => Promise<Array<string>>;
|
|
45
82
|
}
|
|
46
83
|
/**
|
|
47
84
|
* File system write operations.
|
|
@@ -71,8 +108,15 @@ export interface FsRemoveDeps {
|
|
|
71
108
|
* Command execution.
|
|
72
109
|
*/
|
|
73
110
|
export interface CommandDeps {
|
|
74
|
-
/**
|
|
75
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Run a command and return the result. Never throws — failures surface as
|
|
113
|
+
* `success: false`.
|
|
114
|
+
*
|
|
115
|
+
* `options.cwd` sets the child's working directory. `options.signal` aborts
|
|
116
|
+
* the child when the signal fires. `options.timeout_ms` kills the child
|
|
117
|
+
* after the given duration and returns `timed_out: true` on the result.
|
|
118
|
+
*/
|
|
119
|
+
run_command: (cmd: string, args: Array<string>, options?: RunCommandOptions) => Promise<CommandResult>;
|
|
76
120
|
}
|
|
77
121
|
/**
|
|
78
122
|
* HTTP fetch capability.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deps.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;CACtB;AAED
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deps.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/mock.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
import type { RuntimeDeps, CommandResult } from './deps.js';
|
|
10
|
+
import type { RuntimeDeps, CommandResult, RunCommandOptions } from './deps.js';
|
|
11
11
|
/**
|
|
12
12
|
* Mock `RuntimeDeps` with observable state for assertions.
|
|
13
13
|
*/
|
|
@@ -22,10 +22,11 @@ export interface MockRuntime extends RuntimeDeps {
|
|
|
22
22
|
mock_dirs: Set<string>;
|
|
23
23
|
/** Exit calls recorded (exit codes). */
|
|
24
24
|
exit_calls: Array<number>;
|
|
25
|
-
/** Commands executed. */
|
|
25
|
+
/** Commands executed. Captures `options` when passed so tests can assert cwd/timeout/signal. */
|
|
26
26
|
command_calls: Array<{
|
|
27
27
|
cmd: string;
|
|
28
28
|
args: Array<string>;
|
|
29
|
+
options?: RunCommandOptions;
|
|
29
30
|
}>;
|
|
30
31
|
/** Commands executed with inherit. */
|
|
31
32
|
command_inherit_calls: Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/mock.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAc,aAAa,EAAC,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/mock.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAc,aAAa,EAAE,iBAAiB,EAAC,MAAM,WAAW,CAAC;AAIzF;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,kCAAkC;IAClC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,0CAA0C;IAC1C,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,+CAA+C;IAC/C,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACvC,mCAAmC;IACnC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,wCAAwC;IACxC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,gGAAgG;IAChG,aAAa,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,iBAAiB,CAAA;KAAC,CAAC,CAAC;IACtF,sCAAsC;IACtC,qBAAqB,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;KAAC,CAAC,CAAC;IACjE,8BAA8B;IAC9B,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,4CAA4C;IAC5C,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACjD,yCAAyC;IACzC,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,4BAA4B;IAC5B,WAAW,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,WAAW,CAAA;KAAC,CAAC,CAAC;IACxE,wDAAwD;IACxD,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CAC5C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAM,KAAK,CAAC,MAAM,CAAM,KAAG,WAkO9D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,WAAW,KAAG,IAazD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,EAAE,OAAO,MAAM,KAAG,IAEpE,CAAC;AAEF;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM;CAKxB"}
|
package/dist/runtime/mock.js
CHANGED
|
@@ -114,6 +114,55 @@ export const create_mock_runtime = (args = []) => {
|
|
|
114
114
|
error.code = 'ENOENT';
|
|
115
115
|
throw error;
|
|
116
116
|
},
|
|
117
|
+
read_text_from_offset: async (path, offset) => {
|
|
118
|
+
let bytes;
|
|
119
|
+
const stored_bytes = mock_fs_bytes.get(path);
|
|
120
|
+
if (stored_bytes !== undefined) {
|
|
121
|
+
bytes = stored_bytes;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const content = mock_fs.get(path);
|
|
125
|
+
if (content === undefined) {
|
|
126
|
+
const error = new Error(`ENOENT: no such file or directory: ${path}`);
|
|
127
|
+
error.code = 'ENOENT';
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
bytes = new TextEncoder().encode(content);
|
|
131
|
+
}
|
|
132
|
+
const file_size = bytes.length;
|
|
133
|
+
const bytes_to_read = Math.max(0, file_size - offset);
|
|
134
|
+
if (bytes_to_read === 0)
|
|
135
|
+
return { content: '', bytes_read: 0, file_size };
|
|
136
|
+
const slice = bytes.subarray(offset, offset + bytes_to_read);
|
|
137
|
+
return {
|
|
138
|
+
content: new TextDecoder().decode(slice),
|
|
139
|
+
bytes_read: slice.length,
|
|
140
|
+
file_size,
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
readdir: async (path) => {
|
|
144
|
+
const prefix = path.endsWith('/') ? path : path + '/';
|
|
145
|
+
const seen = new Set();
|
|
146
|
+
const collect = (key) => {
|
|
147
|
+
if (!key.startsWith(prefix))
|
|
148
|
+
return;
|
|
149
|
+
const rest = key.slice(prefix.length);
|
|
150
|
+
const slash = rest.indexOf('/');
|
|
151
|
+
seen.add(slash === -1 ? rest : rest.slice(0, slash));
|
|
152
|
+
};
|
|
153
|
+
for (const key of mock_fs.keys())
|
|
154
|
+
collect(key);
|
|
155
|
+
for (const key of mock_fs_bytes.keys())
|
|
156
|
+
collect(key);
|
|
157
|
+
for (const key of mock_dirs)
|
|
158
|
+
collect(key);
|
|
159
|
+
if (seen.size === 0 && !mock_dirs.has(path)) {
|
|
160
|
+
const error = new Error(`ENOENT: no such file or directory: ${path}`);
|
|
161
|
+
error.code = 'ENOENT';
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
return Array.from(seen).sort();
|
|
165
|
+
},
|
|
117
166
|
write_text_file: async (path, content) => {
|
|
118
167
|
mock_fs.set(path, content);
|
|
119
168
|
},
|
|
@@ -163,13 +212,20 @@ export const create_mock_runtime = (args = []) => {
|
|
|
163
212
|
throw new TypeError(`fetch failed (no mock for ${url})`);
|
|
164
213
|
},
|
|
165
214
|
// === Local Commands ===
|
|
166
|
-
run_command: async (cmd, args) => {
|
|
167
|
-
command_calls.push({ cmd, args });
|
|
215
|
+
run_command: async (cmd, args, options) => {
|
|
216
|
+
command_calls.push(options ? { cmd, args, options } : { cmd, args });
|
|
168
217
|
const key = `${cmd} ${args.join(' ')}`;
|
|
169
218
|
const mocked = mock_command_results.get(key);
|
|
170
|
-
if (mocked)
|
|
219
|
+
if (mocked) {
|
|
220
|
+
if (options?.timeout_ms !== undefined && mocked.timed_out === undefined) {
|
|
221
|
+
return { ...mocked, timed_out: false };
|
|
222
|
+
}
|
|
171
223
|
return mocked;
|
|
172
|
-
|
|
224
|
+
}
|
|
225
|
+
const result = { success: true, code: 0, stdout: '', stderr: '' };
|
|
226
|
+
if (options?.timeout_ms !== undefined)
|
|
227
|
+
result.timed_out = false;
|
|
228
|
+
return result;
|
|
173
229
|
},
|
|
174
230
|
run_command_inherit: async (cmd, args) => {
|
|
175
231
|
command_inherit_calls.push({ cmd, args });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC/B,OAAM,aAAa,CAAC,MAAM,CAAyB,KACjD,
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC/B,OAAM,aAAa,CAAC,MAAM,CAAyB,KACjD,WAmKD,CAAC"}
|
package/dist/runtime/node.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Buffer } from 'node:buffer';
|
|
10
10
|
import { spawn } from 'node:child_process';
|
|
11
|
-
import { stat, mkdir, readFile, writeFile, rename, rm } from 'node:fs/promises';
|
|
11
|
+
import { stat, mkdir, readFile, readdir, writeFile, rename, rm, open } from 'node:fs/promises';
|
|
12
12
|
import process from 'node:process';
|
|
13
13
|
/**
|
|
14
14
|
* Create a `RuntimeDeps` backed by Node.js APIs.
|
|
@@ -42,6 +42,27 @@ export const create_node_runtime = (args = process.argv.slice(2)) => ({
|
|
|
42
42
|
},
|
|
43
43
|
read_text_file: (path) => readFile(path, 'utf-8'),
|
|
44
44
|
read_file: (path) => readFile(path).then((buf) => new Uint8Array(buf)),
|
|
45
|
+
read_text_from_offset: async (path, offset) => {
|
|
46
|
+
const s = await stat(path);
|
|
47
|
+
const file_size = s.size;
|
|
48
|
+
const bytes_to_read = Math.max(0, file_size - offset);
|
|
49
|
+
if (bytes_to_read === 0)
|
|
50
|
+
return { content: '', bytes_read: 0, file_size };
|
|
51
|
+
const handle = await open(path, 'r');
|
|
52
|
+
try {
|
|
53
|
+
const buffer = Buffer.alloc(bytes_to_read);
|
|
54
|
+
const { bytesRead } = await handle.read(buffer, 0, bytes_to_read, offset);
|
|
55
|
+
return {
|
|
56
|
+
content: buffer.toString('utf-8', 0, bytesRead),
|
|
57
|
+
bytes_read: bytesRead,
|
|
58
|
+
file_size,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await handle.close();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
readdir: (path) => readdir(path),
|
|
45
66
|
write_text_file: (path, content) => writeFile(path, content, 'utf-8'),
|
|
46
67
|
write_file: (path, data) => writeFile(path, data),
|
|
47
68
|
rename: (old_path, new_path) => rename(old_path, new_path),
|
|
@@ -49,17 +70,45 @@ export const create_node_runtime = (args = process.argv.slice(2)) => ({
|
|
|
49
70
|
// === HTTP ===
|
|
50
71
|
fetch: globalThis.fetch,
|
|
51
72
|
// === Local Commands ===
|
|
52
|
-
run_command: (cmd, args) => {
|
|
73
|
+
run_command: (cmd, args, options) => {
|
|
53
74
|
return new Promise((resolve) => {
|
|
54
75
|
const proc = spawn(cmd, args, {
|
|
55
76
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
77
|
+
cwd: options?.cwd,
|
|
56
78
|
});
|
|
57
79
|
const stdout_chunks = [];
|
|
58
80
|
const stderr_chunks = [];
|
|
81
|
+
let timed_out = false;
|
|
82
|
+
let done = false;
|
|
83
|
+
const finish = (result) => {
|
|
84
|
+
if (done)
|
|
85
|
+
return;
|
|
86
|
+
done = true;
|
|
87
|
+
if (timer !== null)
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
if (options?.signal)
|
|
90
|
+
options.signal.removeEventListener('abort', on_abort);
|
|
91
|
+
resolve(result);
|
|
92
|
+
};
|
|
93
|
+
const on_abort = () => {
|
|
94
|
+
proc.kill();
|
|
95
|
+
};
|
|
96
|
+
const timer = options?.timeout_ms !== undefined
|
|
97
|
+
? setTimeout(() => {
|
|
98
|
+
timed_out = true;
|
|
99
|
+
proc.kill();
|
|
100
|
+
}, options.timeout_ms)
|
|
101
|
+
: null;
|
|
102
|
+
if (options?.signal) {
|
|
103
|
+
if (options.signal.aborted)
|
|
104
|
+
proc.kill();
|
|
105
|
+
else
|
|
106
|
+
options.signal.addEventListener('abort', on_abort, { once: true });
|
|
107
|
+
}
|
|
59
108
|
proc.stdout.on('data', (chunk) => stdout_chunks.push(chunk));
|
|
60
109
|
proc.stderr.on('data', (chunk) => stderr_chunks.push(chunk));
|
|
61
110
|
proc.on('error', (error) => {
|
|
62
|
-
|
|
111
|
+
finish({
|
|
63
112
|
success: false,
|
|
64
113
|
code: 1,
|
|
65
114
|
stdout: '',
|
|
@@ -67,12 +116,15 @@ export const create_node_runtime = (args = process.argv.slice(2)) => ({
|
|
|
67
116
|
});
|
|
68
117
|
});
|
|
69
118
|
proc.on('close', (code) => {
|
|
70
|
-
|
|
71
|
-
success: code === 0,
|
|
119
|
+
const result = {
|
|
120
|
+
success: code === 0 && !timed_out,
|
|
72
121
|
code: code ?? 1,
|
|
73
122
|
stdout: Buffer.concat(stdout_chunks).toString('utf-8').trim(),
|
|
74
123
|
stderr: Buffer.concat(stderr_chunks).toString('utf-8').trim(),
|
|
75
|
-
}
|
|
124
|
+
};
|
|
125
|
+
if (options?.timeout_ms !== undefined)
|
|
126
|
+
result.timed_out = timed_out;
|
|
127
|
+
finish(result);
|
|
76
128
|
});
|
|
77
129
|
});
|
|
78
130
|
},
|