@cuylabs/agent-core 0.9.0 → 0.11.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.
Files changed (116) hide show
  1. package/README.md +33 -17
  2. package/dist/chunk-2O4MCSQS.js +780 -0
  3. package/dist/chunk-2TTOLHBT.js +198 -0
  4. package/dist/chunk-5FMSGQVX.js +281 -0
  5. package/dist/chunk-5NVVNXPQ.js +288 -0
  6. package/dist/{chunk-EKR6PKXU.js → chunk-6HZBHFOL.js} +3 -3
  7. package/dist/chunk-CJI7PVS2.js +58 -0
  8. package/dist/{chunk-WKHDSSXG.js → chunk-CMYN2RCB.js} +146 -46
  9. package/dist/chunk-FII65CN7.js +117 -0
  10. package/dist/{chunk-UHCJEM2E.js → chunk-ICZ66572.js} +13 -6
  11. package/dist/chunk-KYLPMBHD.js +316 -0
  12. package/dist/chunk-MXAP4UG6.js +2956 -0
  13. package/dist/{chunk-4QFNWPIF.js → chunk-N3VX7FEE.js} +35 -2
  14. package/dist/{chunk-MAZ5DY5B.js → chunk-NDZWXCBZ.js} +213 -78
  15. package/dist/{chunk-MHKK374K.js → chunk-Q742PSH3.js} +11 -27
  16. package/dist/{chunk-WGZAPU6N.js → chunk-QAL3OMI3.js} +15 -1
  17. package/dist/{chunk-UDCZ673N.js → chunk-RN6WZEUF.js} +27 -23
  18. package/dist/{chunk-ZXAKHMWH.js → chunk-ROTGCYDW.js} +22 -84
  19. package/dist/chunk-SPBFQXOT.js +0 -0
  20. package/dist/chunk-SSFBF3US.js +602 -0
  21. package/dist/chunk-SZ2XBPTW.js +8 -0
  22. package/dist/chunk-T4UIX5D7.js +115 -0
  23. package/dist/{chunk-IYWQOJMQ.js → chunk-TIHPYVAJ.js} +34 -34
  24. package/dist/{chunk-RKEW5WXI.js → chunk-TOTDGK3P.js} +1 -1
  25. package/dist/chunk-V4RFNEET.js +563 -0
  26. package/dist/chunk-VOUEJSW6.js +0 -0
  27. package/dist/{chunk-J4QDGZIA.js → chunk-WBPOZ7CL.js} +659 -275
  28. package/dist/chunk-X4VN4GIJ.js +185 -0
  29. package/dist/dispatch/index.d.ts +93 -0
  30. package/dist/dispatch/index.js +37 -0
  31. package/dist/events/index.d.ts +93 -0
  32. package/dist/events/index.js +6 -0
  33. package/dist/{runtime → execution}/index.d.ts +120 -35
  34. package/dist/{runtime → execution}/index.js +17 -11
  35. package/dist/index.d.ts +489 -115
  36. package/dist/index.js +1665 -462
  37. package/dist/inference/errors/index.js +1 -1
  38. package/dist/inference/index.d.ts +13 -21
  39. package/dist/inference/index.js +15 -12
  40. package/dist/instance-DzPiv6EK.d.ts +5723 -0
  41. package/dist/logger/index.d.ts +50 -0
  42. package/dist/logger/index.js +11 -0
  43. package/dist/mcp/index.d.ts +5 -9
  44. package/dist/mcp/index.js +2 -3
  45. package/dist/middleware/index.d.ts +10 -150
  46. package/dist/middleware/index.js +10 -2
  47. package/dist/model-messages-CJfwfzGe.d.ts +13 -0
  48. package/dist/models/index.d.ts +5 -2
  49. package/dist/models/index.js +2 -1
  50. package/dist/models/reasoning/index.js +2 -1
  51. package/dist/plugin/index.d.ts +55 -11
  52. package/dist/plugin/index.js +1 -1
  53. package/dist/profiles/index.d.ts +55 -0
  54. package/dist/{presets → profiles}/index.js +10 -10
  55. package/dist/prompt/index.d.ts +8 -13
  56. package/dist/safety/index.d.ts +109 -14
  57. package/dist/safety/index.js +59 -3
  58. package/dist/sandbox/index.d.ts +81 -0
  59. package/dist/sandbox/index.js +1 -0
  60. package/dist/skill/index.d.ts +10 -8
  61. package/dist/skill/index.js +2 -2
  62. package/dist/storage/index.d.ts +12 -4
  63. package/dist/storage/index.js +1 -1
  64. package/dist/subagents/index.d.ts +177 -0
  65. package/dist/subagents/index.js +78 -0
  66. package/dist/team/index.d.ts +544 -0
  67. package/dist/team/index.js +41 -0
  68. package/dist/tool/host/index.d.ts +41 -0
  69. package/dist/tool/host/index.js +10 -0
  70. package/dist/tool/index.d.ts +111 -21
  71. package/dist/tool/index.js +20 -13
  72. package/dist/{types-VQgymC1N.d.ts → types-Bj_J8u_W.d.ts} +44 -64
  73. package/dist/{types-CHiPh8U2.d.ts → types-C_LCeYNg.d.ts} +7 -7
  74. package/dist/types-RSCv7nQ4.d.ts +59 -0
  75. package/package.json +46 -47
  76. package/dist/builder-BgZ_j4Vs.d.ts +0 -35
  77. package/dist/chunk-5ARZJWD2.js +0 -259
  78. package/dist/chunk-DXFBQMXP.js +0 -53
  79. package/dist/chunk-H3FUYU52.js +0 -81
  80. package/dist/chunk-JLXG2SH7.js +0 -905
  81. package/dist/chunk-N7P4PN3O.js +0 -84
  82. package/dist/chunk-OFDKHNCX.js +0 -727
  83. package/dist/chunk-SDSBEQXG.js +0 -157
  84. package/dist/chunk-VEKUXUVF.js +0 -41
  85. package/dist/chunk-VNQBHPCT.js +0 -398
  86. package/dist/chunk-WWYYNWEW.js +0 -259
  87. package/dist/context/index.d.ts +0 -259
  88. package/dist/context/index.js +0 -26
  89. package/dist/events-CE72w8W4.d.ts +0 -149
  90. package/dist/host/index.d.ts +0 -45
  91. package/dist/host/index.js +0 -8
  92. package/dist/index-DQuTZ8xL.d.ts +0 -1335
  93. package/dist/messages-BYWGn8TY.d.ts +0 -110
  94. package/dist/presets/index.d.ts +0 -53
  95. package/dist/registry-DwYqsQkX.d.ts +0 -164
  96. package/dist/runner-CI-XeR16.d.ts +0 -91
  97. package/dist/scope/index.d.ts +0 -10
  98. package/dist/scope/index.js +0 -14
  99. package/dist/session-manager-KbYt2WUh.d.ts +0 -282
  100. package/dist/signal/index.d.ts +0 -28
  101. package/dist/signal/index.js +0 -6
  102. package/dist/sub-agent/index.d.ts +0 -24
  103. package/dist/sub-agent/index.js +0 -32
  104. package/dist/tool-CZWN3KbO.d.ts +0 -141
  105. package/dist/tool-DkhSCV2Y.d.ts +0 -145
  106. package/dist/tracker-DClqYqTj.d.ts +0 -96
  107. package/dist/tracking/index.d.ts +0 -111
  108. package/dist/tracking/index.js +0 -20
  109. package/dist/types-BfNpU8NS.d.ts +0 -270
  110. package/dist/types-BlOKk-Bb.d.ts +0 -330
  111. package/dist/types-BlZwmnuW.d.ts +0 -50
  112. package/dist/types-CQL-SvTn.d.ts +0 -29
  113. package/dist/types-CWm-7rvB.d.ts +0 -55
  114. package/dist/types-DTSkxakL.d.ts +0 -651
  115. package/dist/types-DmDwi2zI.d.ts +0 -339
  116. package/dist/types-YuWV4ag7.d.ts +0 -72
@@ -1,157 +0,0 @@
1
- // src/tool/registry.ts
2
- var ToolRegistry = class {
3
- tools = /* @__PURE__ */ new Map();
4
- groups = /* @__PURE__ */ new Map();
5
- // --------------------------------------------------------------------------
6
- // Tool registration
7
- // --------------------------------------------------------------------------
8
- /**
9
- * Register a tool. Throws if a tool with the same ID is already registered.
10
- * Use `set()` for upsert semantics.
11
- */
12
- register(tool) {
13
- if (this.tools.has(tool.id)) {
14
- throw new Error(`Tool '${tool.id}' is already registered`);
15
- }
16
- this.tools.set(tool.id, tool);
17
- }
18
- /** Register multiple tools (throws on duplicates). */
19
- registerAll(tools) {
20
- for (const tool of tools) {
21
- this.register(tool);
22
- }
23
- }
24
- /** Register or replace a tool (upsert). */
25
- set(tool) {
26
- this.tools.set(tool.id, tool);
27
- }
28
- /** Unregister a tool by ID. Returns `true` if it existed. */
29
- unregister(id) {
30
- return this.tools.delete(id);
31
- }
32
- /** Get a tool by ID. */
33
- get(id) {
34
- return this.tools.get(id);
35
- }
36
- /** Check if a tool is registered. */
37
- has(id) {
38
- return this.tools.has(id);
39
- }
40
- /** Get all tool IDs. */
41
- ids() {
42
- return Array.from(this.tools.keys());
43
- }
44
- /** Get all tools. */
45
- all() {
46
- return Array.from(this.tools.values());
47
- }
48
- /** Clear all tools and groups. */
49
- clear() {
50
- this.tools.clear();
51
- this.groups.clear();
52
- }
53
- /** Number of registered tools. */
54
- get size() {
55
- return this.tools.size;
56
- }
57
- // --------------------------------------------------------------------------
58
- // Group management
59
- // --------------------------------------------------------------------------
60
- /**
61
- * Register a named group of tool IDs.
62
- * The group name can be used in `resolve()` specs.
63
- * Tool IDs don't need to be registered yet — resolution is lazy.
64
- */
65
- registerGroup(name, toolIds) {
66
- this.groups.set(name, toolIds);
67
- }
68
- /** Get tools in a group (only returns registered tools). */
69
- getGroup(name) {
70
- const ids = this.groups.get(name);
71
- if (!ids) return void 0;
72
- return ids.map((id) => this.tools.get(id)).filter((t) => t !== void 0);
73
- }
74
- /** Check if a group is registered. */
75
- hasGroup(name) {
76
- return this.groups.has(name);
77
- }
78
- /** List all group names. */
79
- listGroups() {
80
- return Array.from(this.groups.keys());
81
- }
82
- // --------------------------------------------------------------------------
83
- // Resolution
84
- // --------------------------------------------------------------------------
85
- /**
86
- * Resolve a `ToolSpec` to an array of tools.
87
- *
88
- * Supports group names, individual tool IDs, comma-separated strings,
89
- * exclusions with `-` prefix, booleans, and pass-through arrays.
90
- *
91
- * @example
92
- * ```typescript
93
- * registry.resolve("read-only"); // group name
94
- * registry.resolve("read,grep,glob"); // comma-separated IDs
95
- * registry.resolve("all,-bash"); // all except bash
96
- * registry.resolve(["read", "grep"]); // array of IDs
97
- * registry.resolve(true); // all registered tools
98
- * registry.resolve(false); // empty array
99
- * ```
100
- */
101
- resolve(spec) {
102
- if (spec === true) return this.all();
103
- if (spec === false) return [];
104
- if (Array.isArray(spec) && spec.length > 0 && typeof spec[0] !== "string") {
105
- return spec;
106
- }
107
- const tokens = Array.isArray(spec) ? spec : spec.split(",").map((s) => s.trim()).filter(Boolean);
108
- const includes = [];
109
- const excludes = [];
110
- for (const token of tokens) {
111
- if (token.startsWith("-")) {
112
- excludes.push(token.slice(1));
113
- } else {
114
- includes.push(token);
115
- }
116
- }
117
- const result = /* @__PURE__ */ new Map();
118
- if (includes.length === 0 && excludes.length > 0) {
119
- for (const [id, tool] of this.tools) {
120
- result.set(id, tool);
121
- }
122
- } else {
123
- for (const token of includes) {
124
- if (token === "all") {
125
- for (const [id, tool] of this.tools) {
126
- result.set(id, tool);
127
- }
128
- } else if (this.groups.has(token)) {
129
- const ids = this.groups.get(token);
130
- for (const id of ids) {
131
- const tool = this.tools.get(id);
132
- if (tool) result.set(id, tool);
133
- }
134
- } else if (this.tools.has(token)) {
135
- result.set(token, this.tools.get(token));
136
- }
137
- }
138
- }
139
- for (const id of excludes) {
140
- if (this.groups.has(id)) {
141
- const ids = this.groups.get(id);
142
- for (const gid of ids) {
143
- result.delete(gid);
144
- }
145
- } else {
146
- result.delete(id);
147
- }
148
- }
149
- return Array.from(result.values());
150
- }
151
- };
152
- var defaultRegistry = new ToolRegistry();
153
-
154
- export {
155
- ToolRegistry,
156
- defaultRegistry
157
- };
@@ -1,41 +0,0 @@
1
- // src/tracking/file-tracking.ts
2
- function extractFilePathsFromArgs(args, meta) {
3
- if (!meta.pathArgs || meta.pathArgs.length === 0) {
4
- return [];
5
- }
6
- const paths = [];
7
- for (const argName of meta.pathArgs) {
8
- const value = args[argName];
9
- if (typeof value === "string") {
10
- paths.push(value);
11
- } else if (Array.isArray(value)) {
12
- for (const item of value) {
13
- if (typeof item === "string") {
14
- paths.push(item);
15
- }
16
- }
17
- }
18
- }
19
- return paths;
20
- }
21
- function shouldCaptureBaseline(meta) {
22
- return meta.operationType !== "read";
23
- }
24
- function withFileTracking(tracker, meta, execute) {
25
- return (async (...args) => {
26
- const params = args[0];
27
- if (params && shouldCaptureBaseline(meta)) {
28
- const paths = extractFilePathsFromArgs(params, meta);
29
- for (const path of paths) {
30
- await tracker.beforeWrite(path);
31
- }
32
- }
33
- return await execute(...args);
34
- });
35
- }
36
-
37
- export {
38
- extractFilePathsFromArgs,
39
- shouldCaptureBaseline,
40
- withFileTracking
41
- };
@@ -1,398 +0,0 @@
1
- // src/host/local.ts
2
- import { spawn } from "child_process";
3
- import fs from "fs/promises";
4
- import { existsSync } from "fs";
5
- import path from "path";
6
- import process from "process";
7
- function getShell() {
8
- if (process.platform === "win32") {
9
- return { shell: "cmd.exe", args: ["/c"] };
10
- }
11
- const userShell = process.env.SHELL ?? "/bin/bash";
12
- return { shell: userShell, args: ["-c"] };
13
- }
14
- async function killProcessTree(pid) {
15
- try {
16
- if (process.platform !== "win32") {
17
- process.kill(-pid, "SIGKILL");
18
- } else {
19
- const { exec: execCb } = await import("child_process");
20
- execCb(`taskkill /pid ${pid} /t /f`, () => {
21
- });
22
- }
23
- } catch {
24
- try {
25
- process.kill(pid, "SIGKILL");
26
- } catch {
27
- }
28
- }
29
- }
30
- function localHost(defaultCwd) {
31
- const cwd = defaultCwd ?? process.cwd();
32
- return {
33
- name: "local",
34
- // --------------------------------------------------------------------------
35
- // File system
36
- // --------------------------------------------------------------------------
37
- async readFile(filePath) {
38
- const abs = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
39
- return fs.readFile(abs, "utf-8");
40
- },
41
- async readBytes(filePath, offset, length) {
42
- const abs = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
43
- const fh = await fs.open(abs, "r");
44
- try {
45
- const buf = Buffer.alloc(length);
46
- await fh.read(buf, 0, length, offset);
47
- return buf;
48
- } finally {
49
- await fh.close();
50
- }
51
- },
52
- async writeFile(filePath, content) {
53
- const abs = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
54
- await fs.mkdir(path.dirname(abs), { recursive: true });
55
- await fs.writeFile(abs, content, "utf-8");
56
- },
57
- async exists(filePath) {
58
- const abs = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
59
- return existsSync(abs);
60
- },
61
- async stat(filePath) {
62
- const abs = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
63
- const s = await fs.stat(abs);
64
- return {
65
- size: s.size,
66
- mtime: s.mtime,
67
- isDirectory: s.isDirectory(),
68
- isFile: s.isFile()
69
- };
70
- },
71
- async readdir(dirPath) {
72
- const abs = path.isAbsolute(dirPath) ? dirPath : path.resolve(cwd, dirPath);
73
- const entries = await fs.readdir(abs, { withFileTypes: true });
74
- return entries.map((e) => ({
75
- name: e.name,
76
- isDirectory: e.isDirectory(),
77
- isFile: e.isFile()
78
- }));
79
- },
80
- async mkdir(dirPath) {
81
- const abs = path.isAbsolute(dirPath) ? dirPath : path.resolve(cwd, dirPath);
82
- await fs.mkdir(abs, { recursive: true });
83
- },
84
- // --------------------------------------------------------------------------
85
- // Process execution
86
- // --------------------------------------------------------------------------
87
- exec(command, options) {
88
- const { shell, args } = getShell();
89
- const execCwd = options?.cwd ?? cwd;
90
- const env = {
91
- ...process.env,
92
- ...options?.env,
93
- // Disable pagers — tools collect output, not interactive
94
- PAGER: "cat",
95
- GIT_PAGER: "cat"
96
- };
97
- return new Promise((resolve, reject) => {
98
- let stdout = "";
99
- let stderr = "";
100
- let timedOut = false;
101
- let settled = false;
102
- const child = spawn(shell, [...args, command], {
103
- cwd: execCwd,
104
- detached: process.platform !== "win32",
105
- env,
106
- stdio: ["ignore", "pipe", "pipe"]
107
- });
108
- child.stdout?.on("data", (data) => {
109
- stdout += data.toString();
110
- options?.onStdout?.(data);
111
- });
112
- child.stderr?.on("data", (data) => {
113
- stderr += data.toString();
114
- options?.onStderr?.(data);
115
- });
116
- let timer;
117
- if (options?.timeout && options.timeout > 0) {
118
- timer = setTimeout(() => {
119
- timedOut = true;
120
- if (child.pid) killProcessTree(child.pid);
121
- }, options.timeout);
122
- }
123
- const onAbort = () => {
124
- if (child.pid) killProcessTree(child.pid);
125
- };
126
- options?.signal?.addEventListener("abort", onAbort, { once: true });
127
- child.on("close", (code) => {
128
- if (settled) return;
129
- settled = true;
130
- if (timer) clearTimeout(timer);
131
- options?.signal?.removeEventListener("abort", onAbort);
132
- resolve({ stdout, stderr, exitCode: code, timedOut });
133
- });
134
- child.on("error", (err) => {
135
- if (settled) return;
136
- settled = true;
137
- if (timer) clearTimeout(timer);
138
- options?.signal?.removeEventListener("abort", onAbort);
139
- reject(err);
140
- });
141
- });
142
- }
143
- };
144
- }
145
-
146
- // src/host/docker/exec.ts
147
- async function loadDockerode() {
148
- try {
149
- const module = await import("dockerode");
150
- return module.default ?? module;
151
- } catch {
152
- throw new Error(
153
- "dockerHost requires the 'dockerode' package. Install it with: npm install dockerode"
154
- );
155
- }
156
- }
157
- async function resolveDockerRuntime(options) {
158
- const Dockerode = await loadDockerode();
159
- if (typeof options.container === "string") {
160
- const docker = new Dockerode(options.dockerOptions);
161
- const container2 = docker.getContainer(options.container);
162
- return {
163
- docker,
164
- container: container2,
165
- label: options.container
166
- };
167
- }
168
- const container = options.container;
169
- return {
170
- docker: { modem: container.modem },
171
- container,
172
- label: container.id?.slice(0, 12) ?? "unknown"
173
- };
174
- }
175
- async function containerExec(runtime, command, options = {}) {
176
- const envEntries = options.env ? Object.entries(options.env).filter((entry) => entry[1] !== void 0).map(([key, value]) => `${key}=${value}`) : void 0;
177
- const exec = await runtime.container.exec({
178
- Cmd: ["sh", "-c", command],
179
- ...options.user ? { User: options.user } : {},
180
- ...options.workdir ? { WorkingDir: options.workdir } : {},
181
- ...envEntries && envEntries.length > 0 ? { Env: envEntries } : {},
182
- AttachStdout: true,
183
- AttachStderr: true
184
- });
185
- const stream = await exec.start({});
186
- let timedOut = false;
187
- let aborted = false;
188
- return await new Promise((resolve, reject) => {
189
- const stdoutChunks = [];
190
- const stderrChunks = [];
191
- runtime.docker.modem.demuxStream(
192
- stream,
193
- {
194
- write: (chunk) => {
195
- stdoutChunks.push(chunk);
196
- options.onStdout?.(chunk);
197
- return true;
198
- },
199
- end: function() {
200
- return this;
201
- }
202
- },
203
- {
204
- write: (chunk) => {
205
- stderrChunks.push(chunk);
206
- options.onStderr?.(chunk);
207
- return true;
208
- },
209
- end: function() {
210
- return this;
211
- }
212
- }
213
- );
214
- let timer;
215
- if (options.timeout && options.timeout > 0) {
216
- timer = setTimeout(() => {
217
- timedOut = true;
218
- stream.destroy?.();
219
- }, options.timeout);
220
- }
221
- const onAbort = () => {
222
- aborted = true;
223
- stream.destroy?.();
224
- };
225
- options.signal?.addEventListener("abort", onAbort, { once: true });
226
- stream.on("end", async () => {
227
- if (timer) {
228
- clearTimeout(timer);
229
- }
230
- options.signal?.removeEventListener("abort", onAbort);
231
- try {
232
- const info = await exec.inspect();
233
- resolve({
234
- stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
235
- stderr: Buffer.concat(stderrChunks).toString("utf-8"),
236
- exitCode: timedOut || aborted ? null : info.ExitCode ?? 0,
237
- timedOut
238
- });
239
- } catch (error) {
240
- reject(error);
241
- }
242
- });
243
- stream.on("error", (error) => {
244
- if (timer) {
245
- clearTimeout(timer);
246
- }
247
- options.signal?.removeEventListener("abort", onAbort);
248
- if (timedOut || aborted) {
249
- resolve({
250
- stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
251
- stderr: Buffer.concat(stderrChunks).toString("utf-8"),
252
- exitCode: null,
253
- timedOut
254
- });
255
- return;
256
- }
257
- reject(error);
258
- });
259
- });
260
- }
261
- async function runDockerCommand(options) {
262
- const { runtime, command, defaultUser, defaultWorkdir, execOptions } = options;
263
- return await containerExec(runtime, command, {
264
- user: defaultUser,
265
- workdir: execOptions?.workdir ?? execOptions?.cwd ?? defaultWorkdir,
266
- env: execOptions?.env,
267
- timeout: execOptions?.timeout,
268
- signal: execOptions?.signal,
269
- onStdout: execOptions?.onStdout,
270
- onStderr: execOptions?.onStderr
271
- });
272
- }
273
-
274
- // src/host/docker/shell.ts
275
- function sq(value) {
276
- return `'${value.replace(/'/g, `'\\''`)}'`;
277
- }
278
- function resolveDockerPath(path2, defaultWorkdir) {
279
- if (path2.startsWith("/")) {
280
- return path2;
281
- }
282
- const base = defaultWorkdir.endsWith("/") ? defaultWorkdir : `${defaultWorkdir}/`;
283
- return `${base}${path2}`;
284
- }
285
-
286
- // src/host/docker/host.ts
287
- async function dockerHost(options) {
288
- const runtime = await resolveDockerRuntime(options);
289
- const defaultUser = options.user;
290
- const defaultWorkdir = options.workdir ?? "/";
291
- async function run(command, execOptions) {
292
- return await runDockerCommand({
293
- runtime,
294
- command,
295
- defaultUser,
296
- defaultWorkdir,
297
- execOptions
298
- });
299
- }
300
- return {
301
- name: `docker:${runtime.label}`,
302
- async readFile(filePath) {
303
- const absPath = resolveDockerPath(filePath, defaultWorkdir);
304
- const result = await run(`cat ${sq(absPath)}`);
305
- if (result.exitCode !== 0) {
306
- throw new Error(`readFile failed (${absPath}): ${result.stderr.trim()}`);
307
- }
308
- return result.stdout;
309
- },
310
- async readBytes(filePath, offset, length) {
311
- const absPath = resolveDockerPath(filePath, defaultWorkdir);
312
- const result = await run(
313
- `dd if=${sq(absPath)} bs=1 skip=${offset} count=${length} 2>/dev/null`
314
- );
315
- if (result.exitCode !== 0) {
316
- throw new Error(
317
- `readBytes failed (${absPath}): ${result.stderr.trim()}`
318
- );
319
- }
320
- return Buffer.from(result.stdout, "binary");
321
- },
322
- async writeFile(filePath, content) {
323
- const absPath = resolveDockerPath(filePath, defaultWorkdir);
324
- const dir = absPath.substring(0, absPath.lastIndexOf("/")) || "/";
325
- await run(`mkdir -p ${sq(dir)}`);
326
- const encoded = Buffer.from(content, "utf-8").toString("base64");
327
- const result = await run(`echo ${sq(encoded)} | base64 -d > ${sq(absPath)}`);
328
- if (result.exitCode !== 0) {
329
- throw new Error(
330
- `writeFile failed (${absPath}): ${result.stderr.trim()}`
331
- );
332
- }
333
- },
334
- async exists(filePath) {
335
- const absPath = resolveDockerPath(filePath, defaultWorkdir);
336
- const result = await run(`test -e ${sq(absPath)}`);
337
- return result.exitCode === 0;
338
- },
339
- async stat(filePath) {
340
- const absPath = resolveDockerPath(filePath, defaultWorkdir);
341
- const result = await run(
342
- `stat -c '%s %Y %F' ${sq(absPath)} 2>/dev/null || stat -f '%z %m %HT' ${sq(absPath)}`
343
- );
344
- if (result.exitCode !== 0) {
345
- throw new Error(`stat failed (${absPath}): ${result.stderr.trim()}`);
346
- }
347
- const parts = result.stdout.trim().split(/\s+/);
348
- if (parts.length < 3) {
349
- throw new Error(
350
- `stat: unexpected output format for ${absPath}: ${result.stdout}`
351
- );
352
- }
353
- const size = parseInt(parts[0], 10);
354
- const mtimeSec = parseInt(parts[1], 10);
355
- const typeStr = parts.slice(2).join(" ").toLowerCase();
356
- return {
357
- size,
358
- mtime: new Date(mtimeSec * 1e3),
359
- isDirectory: typeStr.includes("directory"),
360
- isFile: typeStr.includes("regular") || typeStr.includes("file")
361
- };
362
- },
363
- async readdir(dirPath) {
364
- const absPath = resolveDockerPath(dirPath, defaultWorkdir);
365
- const result = await run(
366
- `find ${sq(absPath)} -maxdepth 1 -mindepth 1 -printf '%f\\t%y\\n' 2>/dev/null || for f in ${sq(absPath)}/*; do [ -e "$f" ] || continue; n=$(basename "$f"); if [ -d "$f" ]; then printf '%s\\td\\n' "$n"; else printf '%s\\tf\\n' "$n"; fi; done`
367
- );
368
- if (result.exitCode !== 0) {
369
- throw new Error(
370
- `readdir failed (${absPath}): ${result.stderr.trim()}`
371
- );
372
- }
373
- return result.stdout.trim().split("\n").filter(Boolean).map((line) => {
374
- const [name, type] = line.split(" ");
375
- return {
376
- name,
377
- isDirectory: type === "d",
378
- isFile: type === "f"
379
- };
380
- });
381
- },
382
- async mkdir(dirPath) {
383
- const absPath = resolveDockerPath(dirPath, defaultWorkdir);
384
- const result = await run(`mkdir -p ${sq(absPath)}`);
385
- if (result.exitCode !== 0) {
386
- throw new Error(`mkdir failed (${absPath}): ${result.stderr.trim()}`);
387
- }
388
- },
389
- async exec(command, execOptions) {
390
- return await run(command, execOptions);
391
- }
392
- };
393
- }
394
-
395
- export {
396
- localHost,
397
- dockerHost
398
- };