@action-llama/action-llama 0.27.4 → 0.28.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 (242) hide show
  1. package/dist/agents/credential-setup.d.ts +0 -1
  2. package/dist/agents/credential-setup.d.ts.map +1 -1
  3. package/dist/agents/credential-setup.js +2 -23
  4. package/dist/agents/credential-setup.js.map +1 -1
  5. package/dist/agents/prompt.js +1 -1
  6. package/dist/agents/prompt.js.map +1 -1
  7. package/dist/agents/scheduler-tools.d.ts +40 -0
  8. package/dist/agents/scheduler-tools.d.ts.map +1 -0
  9. package/dist/agents/scheduler-tools.js +178 -0
  10. package/dist/agents/scheduler-tools.js.map +1 -0
  11. package/dist/agents/transport-runner.d.ts +95 -0
  12. package/dist/agents/transport-runner.d.ts.map +1 -0
  13. package/dist/agents/transport-runner.js +653 -0
  14. package/dist/agents/transport-runner.js.map +1 -0
  15. package/dist/build-info.json +1 -1
  16. package/dist/cli/commands/add.d.ts +1 -0
  17. package/dist/cli/commands/add.d.ts.map +1 -1
  18. package/dist/cli/commands/add.js +24 -9
  19. package/dist/cli/commands/add.js.map +1 -1
  20. package/dist/cli/commands/agent.d.ts +0 -3
  21. package/dist/cli/commands/agent.d.ts.map +1 -1
  22. package/dist/cli/commands/agent.js +3 -67
  23. package/dist/cli/commands/agent.js.map +1 -1
  24. package/dist/cli/commands/logs.d.ts.map +1 -1
  25. package/dist/cli/commands/logs.js +2 -3
  26. package/dist/cli/commands/logs.js.map +1 -1
  27. package/dist/cli/main.js +1 -30
  28. package/dist/cli/main.js.map +1 -1
  29. package/dist/control/routes/dashboard-api.js +1 -1
  30. package/dist/control/routes/dashboard-api.js.map +1 -1
  31. package/dist/control/routes/log-helpers.d.ts +4 -4
  32. package/dist/control/routes/log-helpers.d.ts.map +1 -1
  33. package/dist/control/routes/log-helpers.js +12 -7
  34. package/dist/control/routes/log-helpers.js.map +1 -1
  35. package/dist/control/routes/log-summary.d.ts.map +1 -1
  36. package/dist/control/routes/log-summary.js +22 -9
  37. package/dist/control/routes/log-summary.js.map +1 -1
  38. package/dist/control/routes/logs.d.ts.map +1 -1
  39. package/dist/control/routes/logs.js +10 -10
  40. package/dist/control/routes/logs.js.map +1 -1
  41. package/dist/docker/providers/index.d.ts +0 -4
  42. package/dist/docker/providers/index.d.ts.map +1 -1
  43. package/dist/docker/providers/index.js +0 -38
  44. package/dist/docker/providers/index.js.map +1 -1
  45. package/dist/execution/execution.d.ts +0 -1
  46. package/dist/execution/execution.d.ts.map +1 -1
  47. package/dist/execution/execution.js +5 -9
  48. package/dist/execution/execution.js.map +1 -1
  49. package/dist/execution/index.d.ts +1 -11
  50. package/dist/execution/index.d.ts.map +1 -1
  51. package/dist/execution/index.js +1 -8
  52. package/dist/execution/index.js.map +1 -1
  53. package/dist/execution/runner-setup.d.ts +6 -11
  54. package/dist/execution/runner-setup.d.ts.map +1 -1
  55. package/dist/execution/runner-setup.js +18 -14
  56. package/dist/execution/runner-setup.js.map +1 -1
  57. package/dist/execution/runtime-factory.d.ts +1 -15
  58. package/dist/execution/runtime-factory.d.ts.map +1 -1
  59. package/dist/execution/runtime-factory.js +1 -18
  60. package/dist/execution/runtime-factory.js.map +1 -1
  61. package/dist/frontend/assets/{index-a78B5S5H.js → index-DnSu-8Kw.js} +1 -1
  62. package/dist/frontend/assets/index-DnSu-8Kw.js.map +1 -0
  63. package/dist/frontend/index.html +1 -1
  64. package/dist/gateway/index.d.ts +1 -1
  65. package/dist/gateway/index.d.ts.map +1 -1
  66. package/dist/gateway/index.js +6 -47
  67. package/dist/gateway/index.js.map +1 -1
  68. package/dist/gateway/routes/system.d.ts +1 -4
  69. package/dist/gateway/routes/system.d.ts.map +1 -1
  70. package/dist/gateway/routes/system.js +3 -8
  71. package/dist/gateway/routes/system.js.map +1 -1
  72. package/dist/gateway/stores.d.ts +0 -4
  73. package/dist/gateway/stores.d.ts.map +1 -1
  74. package/dist/gateway/stores.js +2 -10
  75. package/dist/gateway/stores.js.map +1 -1
  76. package/dist/gateway/types.d.ts +0 -13
  77. package/dist/gateway/types.d.ts.map +1 -1
  78. package/dist/mcp/server.d.ts.map +1 -1
  79. package/dist/mcp/server.js +11 -0
  80. package/dist/mcp/server.js.map +1 -1
  81. package/dist/models/providers/custom.d.ts.map +1 -1
  82. package/dist/models/providers/custom.js +3 -2
  83. package/dist/models/providers/custom.js.map +1 -1
  84. package/dist/models/providers/openai.d.ts.map +1 -1
  85. package/dist/models/providers/openai.js +3 -2
  86. package/dist/models/providers/openai.js.map +1 -1
  87. package/dist/scheduler/gateway-setup.d.ts +0 -2
  88. package/dist/scheduler/gateway-setup.d.ts.map +1 -1
  89. package/dist/scheduler/gateway-setup.js +2 -11
  90. package/dist/scheduler/gateway-setup.js.map +1 -1
  91. package/dist/scheduler/index.d.ts.map +1 -1
  92. package/dist/scheduler/index.js +95 -55
  93. package/dist/scheduler/index.js.map +1 -1
  94. package/dist/scheduler/validation.js +1 -1
  95. package/dist/scheduler/validation.js.map +1 -1
  96. package/dist/scheduler/watcher.d.ts +2 -8
  97. package/dist/scheduler/watcher.d.ts.map +1 -1
  98. package/dist/scheduler/watcher.js +7 -104
  99. package/dist/scheduler/watcher.js.map +1 -1
  100. package/dist/shared/config/load-agent.js +2 -2
  101. package/dist/shared/config/load-agent.js.map +1 -1
  102. package/dist/shared/config/load-project.js +2 -2
  103. package/dist/shared/config/load-project.js.map +1 -1
  104. package/dist/shared/config/types.d.ts +10 -2
  105. package/dist/shared/config/types.d.ts.map +1 -1
  106. package/dist/shared/constants.d.ts +1 -1
  107. package/dist/shared/constants.d.ts.map +1 -1
  108. package/dist/shared/constants.js +2 -2
  109. package/dist/shared/constants.js.map +1 -1
  110. package/dist/shared/credential-refs.js +1 -1
  111. package/dist/shared/credential-refs.js.map +1 -1
  112. package/dist/shared/paths.d.ts.map +1 -1
  113. package/dist/shared/paths.js +2 -2
  114. package/dist/shared/paths.js.map +1 -1
  115. package/dist/shared/validation.js +1 -1
  116. package/dist/shared/validation.js.map +1 -1
  117. package/dist/transport/docker-exec.d.ts +41 -0
  118. package/dist/transport/docker-exec.d.ts.map +1 -0
  119. package/dist/transport/docker-exec.js +331 -0
  120. package/dist/transport/docker-exec.js.map +1 -0
  121. package/dist/transport/host-user.d.ts +37 -0
  122. package/dist/transport/host-user.d.ts.map +1 -0
  123. package/dist/transport/host-user.js +232 -0
  124. package/dist/transport/host-user.js.map +1 -0
  125. package/dist/transport/index.d.ts +8 -0
  126. package/dist/transport/index.d.ts.map +1 -0
  127. package/dist/transport/index.js +7 -0
  128. package/dist/transport/index.js.map +1 -0
  129. package/dist/transport/memory.d.ts +35 -0
  130. package/dist/transport/memory.d.ts.map +1 -0
  131. package/dist/transport/memory.js +110 -0
  132. package/dist/transport/memory.js.map +1 -0
  133. package/dist/transport/operations.d.ts +68 -0
  134. package/dist/transport/operations.d.ts.map +1 -0
  135. package/dist/transport/operations.js +164 -0
  136. package/dist/transport/operations.js.map +1 -0
  137. package/dist/transport/ssh.d.ts +43 -0
  138. package/dist/transport/ssh.d.ts.map +1 -0
  139. package/dist/transport/ssh.js +225 -0
  140. package/dist/transport/ssh.js.map +1 -0
  141. package/dist/transport/transport.d.ts +59 -0
  142. package/dist/transport/transport.d.ts.map +1 -0
  143. package/dist/transport/transport.js +29 -0
  144. package/dist/transport/transport.js.map +1 -0
  145. package/package.json +1 -1
  146. package/dist/agents/container-entry.d.ts +0 -31
  147. package/dist/agents/container-entry.d.ts.map +0 -1
  148. package/dist/agents/container-entry.js +0 -302
  149. package/dist/agents/container-entry.js.map +0 -1
  150. package/dist/agents/container-runner.d.ts +0 -59
  151. package/dist/agents/container-runner.d.ts.map +0 -1
  152. package/dist/agents/container-runner.js +0 -472
  153. package/dist/agents/container-runner.js.map +0 -1
  154. package/dist/agents/harness/claude-cli-harness.d.ts +0 -15
  155. package/dist/agents/harness/claude-cli-harness.d.ts.map +0 -1
  156. package/dist/agents/harness/claude-cli-harness.js +0 -240
  157. package/dist/agents/harness/claude-cli-harness.js.map +0 -1
  158. package/dist/agents/harness/consumer.d.ts +0 -31
  159. package/dist/agents/harness/consumer.d.ts.map +0 -1
  160. package/dist/agents/harness/consumer.js +0 -161
  161. package/dist/agents/harness/consumer.js.map +0 -1
  162. package/dist/agents/harness/factory.d.ts +0 -9
  163. package/dist/agents/harness/factory.d.ts.map +0 -1
  164. package/dist/agents/harness/factory.js +0 -25
  165. package/dist/agents/harness/factory.js.map +0 -1
  166. package/dist/agents/harness/index.d.ts +0 -9
  167. package/dist/agents/harness/index.d.ts.map +0 -1
  168. package/dist/agents/harness/index.js +0 -5
  169. package/dist/agents/harness/index.js.map +0 -1
  170. package/dist/agents/harness/pi-harness.d.ts +0 -18
  171. package/dist/agents/harness/pi-harness.d.ts.map +0 -1
  172. package/dist/agents/harness/pi-harness.js +0 -259
  173. package/dist/agents/harness/pi-harness.js.map +0 -1
  174. package/dist/agents/harness/types.d.ts +0 -57
  175. package/dist/agents/harness/types.d.ts.map +0 -1
  176. package/dist/agents/harness/types.js +0 -2
  177. package/dist/agents/harness/types.js.map +0 -1
  178. package/dist/agents/session-loop.d.ts +0 -36
  179. package/dist/agents/session-loop.d.ts.map +0 -1
  180. package/dist/agents/session-loop.js +0 -216
  181. package/dist/agents/session-loop.js.map +0 -1
  182. package/dist/agents/signals.d.ts +0 -34
  183. package/dist/agents/signals.d.ts.map +0 -1
  184. package/dist/agents/signals.js +0 -122
  185. package/dist/agents/signals.js.map +0 -1
  186. package/dist/cli/commands/claude.d.ts +0 -4
  187. package/dist/cli/commands/claude.d.ts.map +0 -1
  188. package/dist/cli/commands/claude.js +0 -6
  189. package/dist/cli/commands/claude.js.map +0 -1
  190. package/dist/cli/commands/run-agent.d.ts +0 -14
  191. package/dist/cli/commands/run-agent.d.ts.map +0 -1
  192. package/dist/cli/commands/run-agent.js +0 -270
  193. package/dist/cli/commands/run-agent.js.map +0 -1
  194. package/dist/docker/cloud-run-runtime.d.ts +0 -48
  195. package/dist/docker/cloud-run-runtime.d.ts.map +0 -1
  196. package/dist/docker/cloud-run-runtime.js +0 -490
  197. package/dist/docker/cloud-run-runtime.js.map +0 -1
  198. package/dist/docker/image.d.ts +0 -19
  199. package/dist/docker/image.d.ts.map +0 -1
  200. package/dist/docker/image.js +0 -111
  201. package/dist/docker/image.js.map +0 -1
  202. package/dist/execution/call-dispatcher.d.ts +0 -11
  203. package/dist/execution/call-dispatcher.d.ts.map +0 -1
  204. package/dist/execution/call-dispatcher.js +0 -75
  205. package/dist/execution/call-dispatcher.js.map +0 -1
  206. package/dist/execution/container-registry.d.ts +0 -42
  207. package/dist/execution/container-registry.d.ts.map +0 -1
  208. package/dist/execution/container-registry.js +0 -76
  209. package/dist/execution/container-registry.js.map +0 -1
  210. package/dist/execution/image-builder.d.ts +0 -48
  211. package/dist/execution/image-builder.d.ts.map +0 -1
  212. package/dist/execution/image-builder.js +0 -155
  213. package/dist/execution/image-builder.js.map +0 -1
  214. package/dist/execution/routes/calls.d.ts +0 -18
  215. package/dist/execution/routes/calls.d.ts.map +0 -1
  216. package/dist/execution/routes/calls.js +0 -74
  217. package/dist/execution/routes/calls.js.map +0 -1
  218. package/dist/execution/routes/locks.d.ts +0 -10
  219. package/dist/execution/routes/locks.d.ts.map +0 -1
  220. package/dist/execution/routes/locks.js +0 -166
  221. package/dist/execution/routes/locks.js.map +0 -1
  222. package/dist/execution/routes/shutdown.d.ts +0 -5
  223. package/dist/execution/routes/shutdown.d.ts.map +0 -1
  224. package/dist/execution/routes/shutdown.js +0 -24
  225. package/dist/execution/routes/shutdown.js.map +0 -1
  226. package/dist/execution/routes/signals.d.ts +0 -12
  227. package/dist/execution/routes/signals.d.ts.map +0 -1
  228. package/dist/execution/routes/signals.js +0 -123
  229. package/dist/execution/routes/signals.js.map +0 -1
  230. package/dist/execution/types.d.ts +0 -23
  231. package/dist/execution/types.d.ts.map +0 -1
  232. package/dist/execution/types.js +0 -2
  233. package/dist/execution/types.js.map +0 -1
  234. package/dist/frontend/assets/index-a78B5S5H.js.map +0 -1
  235. package/dist/gateway/routes/execution.d.ts +0 -24
  236. package/dist/gateway/routes/execution.d.ts.map +0 -1
  237. package/dist/gateway/routes/execution.js +0 -13
  238. package/dist/gateway/routes/execution.js.map +0 -1
  239. package/dist/scheduler/orphan-recovery.d.ts +0 -25
  240. package/dist/scheduler/orphan-recovery.d.ts.map +0 -1
  241. package/dist/scheduler/orphan-recovery.js +0 -144
  242. package/dist/scheduler/orphan-recovery.js.map +0 -1
@@ -0,0 +1,41 @@
1
+ /**
2
+ * DockerExecTransport — executes commands inside a running Docker container
3
+ * via `docker exec -i`, maintaining a persistent shell session.
4
+ *
5
+ * Shell state (cwd, env vars) persists across exec() calls because all commands
6
+ * run in the same bash process. File I/O uses `docker cp` for efficient batch
7
+ * transfers without framing issues.
8
+ */
9
+ import type { Transport, ExecResult, ExecOptions } from "./transport.js";
10
+ export interface DockerExecTransportOpts {
11
+ /** The Docker container name or ID to connect to. */
12
+ container: string;
13
+ /** User to run as inside the container. Default: undefined (container default). */
14
+ user?: string;
15
+ /** Initial working directory. Default: "/" */
16
+ cwd?: string;
17
+ }
18
+ export declare class DockerExecTransport implements Transport {
19
+ private opts;
20
+ private container;
21
+ private shell;
22
+ private ready;
23
+ private buffer;
24
+ private user?;
25
+ private _closed;
26
+ constructor(opts: DockerExecTransportOpts);
27
+ /** Start the persistent shell session. Must be called before exec(). */
28
+ connect(): Promise<void>;
29
+ /**
30
+ * Wait for a delimiter to appear in the stdout buffer.
31
+ * Returns the output that appeared before the delimiter.
32
+ * Also parses the exit code from the delimiter line.
33
+ */
34
+ private waitForDelimiter;
35
+ exec(command: string, options?: ExecOptions): Promise<ExecResult>;
36
+ readFiles(paths: string[]): Promise<Map<string, Buffer>>;
37
+ private readSingleFile;
38
+ writeFiles(files: Map<string, Buffer>): Promise<void>;
39
+ close(): Promise<void>;
40
+ }
41
+ //# sourceMappingURL=docker-exec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-exec.d.ts","sourceRoot":"","sources":["../../src/transport/docker-exec.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAYzE,MAAM,WAAW,uBAAuB;IACtC,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,mBAAoB,YAAW,SAAS;IAQvC,OAAO,CAAC,IAAI;IAPxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,OAAO,CAAS;gBAEJ,IAAI,EAAE,uBAAuB;IAKjD,wEAAwE;IAClE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC9B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAyClB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IA2EjE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAmEhD,cAAc;IAiBtB,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA+DrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAc7B"}
@@ -0,0 +1,331 @@
1
+ /**
2
+ * DockerExecTransport — executes commands inside a running Docker container
3
+ * via `docker exec -i`, maintaining a persistent shell session.
4
+ *
5
+ * Shell state (cwd, env vars) persists across exec() calls because all commands
6
+ * run in the same bash process. File I/O uses `docker cp` for efficient batch
7
+ * transfers without framing issues.
8
+ */
9
+ import { spawn, execFileSync } from "child_process";
10
+ import { randomBytes } from "crypto";
11
+ import { mkdtempSync, writeFileSync, readFileSync, readdirSync, rmSync, mkdirSync } from "fs";
12
+ import { join, dirname, basename } from "path";
13
+ import { tmpdir } from "os";
14
+ /** How long to wait for the shell to become ready after spawning (ms). */
15
+ const SHELL_READY_TIMEOUT_MS = 10_000;
16
+ /** Default command execution timeout (ms). */
17
+ const DEFAULT_EXEC_TIMEOUT_MS = 300_000;
18
+ /** Generate a unique delimiter that won't appear in command output. */
19
+ function makeDelimiter() {
20
+ return `__AL_DELIM_${randomBytes(8).toString("hex")}__`;
21
+ }
22
+ export class DockerExecTransport {
23
+ opts;
24
+ container;
25
+ shell = null;
26
+ ready = false;
27
+ buffer = "";
28
+ user;
29
+ _closed = false;
30
+ constructor(opts) {
31
+ this.opts = opts;
32
+ this.container = opts.container;
33
+ this.user = opts.user;
34
+ }
35
+ /** Start the persistent shell session. Must be called before exec(). */
36
+ async connect() {
37
+ const args = ["exec", "-i"];
38
+ if (this.user) {
39
+ args.push("-u", this.user);
40
+ }
41
+ args.push(this.container, "bash", "--norc", "--noprofile");
42
+ this.shell = spawn("docker", args, {
43
+ stdio: ["pipe", "pipe", "pipe"],
44
+ });
45
+ // Absorb EPIPE on stdin — the spawned process may exit before we write to it
46
+ // (e.g. container not found, Docker daemon unreachable, or mocked Docker).
47
+ this.shell.stdin.on("error", () => { });
48
+ this.shell.stdout.on("data", (chunk) => {
49
+ this.buffer += chunk.toString();
50
+ });
51
+ this.shell.on("exit", () => {
52
+ this.ready = false;
53
+ });
54
+ // Wait for shell to be ready by sending a probe command
55
+ const readyDelim = makeDelimiter();
56
+ this.shell.stdin.write(`echo "${readyDelim} $?"\n`);
57
+ await this.waitForDelimiter(readyDelim, SHELL_READY_TIMEOUT_MS);
58
+ this.ready = true;
59
+ // Set initial cwd if specified
60
+ if (this.opts.cwd) {
61
+ await this.exec(`cd ${shellQuote(this.opts.cwd)}`);
62
+ }
63
+ }
64
+ /**
65
+ * Wait for a delimiter to appear in the stdout buffer.
66
+ * Returns the output that appeared before the delimiter.
67
+ * Also parses the exit code from the delimiter line.
68
+ */
69
+ waitForDelimiter(delimiter, timeoutMs) {
70
+ return new Promise((resolve, reject) => {
71
+ const timer = setTimeout(() => {
72
+ clearInterval(poll);
73
+ reject(new Error(`Timed out waiting for shell response (${timeoutMs}ms)`));
74
+ }, timeoutMs);
75
+ const check = () => {
76
+ if (this._closed) {
77
+ clearTimeout(timer);
78
+ clearInterval(poll);
79
+ reject(new Error("Transport closed"));
80
+ return;
81
+ }
82
+ const idx = this.buffer.indexOf(delimiter);
83
+ if (idx === -1)
84
+ return;
85
+ // Find the end of the delimiter line
86
+ const lineEnd = this.buffer.indexOf("\n", idx);
87
+ if (lineEnd === -1)
88
+ return;
89
+ const output = this.buffer.slice(0, idx);
90
+ const delimLine = this.buffer.slice(idx, lineEnd);
91
+ this.buffer = this.buffer.slice(lineEnd + 1);
92
+ clearTimeout(timer);
93
+ clearInterval(poll);
94
+ // Parse exit code from delimiter line: "__AL_DELIM_xxx__ 0"
95
+ const match = delimLine.match(/\s+(\d+)\s*$/);
96
+ const exitCode = match ? parseInt(match[1], 10) : 0;
97
+ resolve({ output, exitCode });
98
+ };
99
+ const poll = setInterval(check, 10);
100
+ // Check immediately in case data already arrived
101
+ check();
102
+ });
103
+ }
104
+ async exec(command, options) {
105
+ if (!this.shell || !this.ready) {
106
+ throw new Error("Transport not connected. Call connect() first.");
107
+ }
108
+ const delimiter = makeDelimiter();
109
+ let stderr = "";
110
+ // Capture stderr for this command
111
+ const stderrHandler = (chunk) => {
112
+ stderr += chunk.toString();
113
+ };
114
+ this.shell.stderr.on("data", stderrHandler);
115
+ // Set up abort handling
116
+ let aborted = false;
117
+ const onAbort = () => {
118
+ aborted = true;
119
+ // Send Ctrl+C to the shell to interrupt the running command
120
+ this.shell?.stdin?.write("\x03\n");
121
+ };
122
+ if (options?.signal) {
123
+ if (options.signal.aborted) {
124
+ this.shell?.stderr?.removeListener("data", stderrHandler);
125
+ return { stdout: "", stderr: "Aborted", exitCode: 130 };
126
+ }
127
+ options.signal.addEventListener("abort", onAbort, { once: true });
128
+ }
129
+ const timeoutMs = options?.timeout ?? DEFAULT_EXEC_TIMEOUT_MS;
130
+ // Set up streaming if onData callback provided
131
+ let streamingInterval;
132
+ let lastStreamedIdx = 0;
133
+ if (options?.onData) {
134
+ streamingInterval = setInterval(() => {
135
+ if (this.buffer.length > lastStreamedIdx) {
136
+ const newData = this.buffer.slice(lastStreamedIdx);
137
+ // Don't stream past a delimiter if one appeared
138
+ const delimIdx = newData.indexOf("__AL_DELIM_");
139
+ const safeData = delimIdx >= 0 ? newData.slice(0, delimIdx) : newData;
140
+ if (safeData.length > 0) {
141
+ options.onData(Buffer.from(safeData));
142
+ lastStreamedIdx += safeData.length;
143
+ }
144
+ }
145
+ }, 50);
146
+ }
147
+ try {
148
+ // Send the command, then send the delimiter probe.
149
+ // The delimiter line echoes the exit code of the command ($?).
150
+ this.shell.stdin.write(`${command}\necho "${delimiter} $?"\n`);
151
+ const { output, exitCode } = await this.waitForDelimiter(delimiter, timeoutMs);
152
+ return {
153
+ stdout: output.trimEnd(),
154
+ stderr: stderr.trimEnd(),
155
+ exitCode: aborted ? 130 : exitCode,
156
+ };
157
+ }
158
+ catch (err) {
159
+ if (aborted) {
160
+ return { stdout: "", stderr: "Aborted", exitCode: 130 };
161
+ }
162
+ throw err;
163
+ }
164
+ finally {
165
+ this.shell?.stderr?.removeListener("data", stderrHandler);
166
+ if (options?.signal) {
167
+ options.signal.removeEventListener("abort", onAbort);
168
+ }
169
+ if (streamingInterval)
170
+ clearInterval(streamingInterval);
171
+ }
172
+ }
173
+ async readFiles(paths) {
174
+ const result = new Map();
175
+ if (paths.length === 0)
176
+ return result;
177
+ if (paths.length === 1) {
178
+ return this.readSingleFile(paths[0]);
179
+ }
180
+ // Batch read: tar on the container → docker cp the tar → extract locally
181
+ const tmpDir = mkdtempSync(join(tmpdir(), "al-read-"));
182
+ try {
183
+ const tarName = `al-batch-${randomBytes(4).toString("hex")}.tar`;
184
+ const remoteTar = `/tmp/${tarName}`;
185
+ // Create tar on container (ignoring missing files, using absolute paths)
186
+ const pathArgs = paths.map(p => shellQuote(p)).join(" ");
187
+ execFileSync("docker", [
188
+ "exec", this.container,
189
+ "bash", "-c", `tar cf ${remoteTar} --ignore-failed-read -P ${pathArgs} 2>/dev/null; true`,
190
+ ], {
191
+ timeout: 30_000,
192
+ stdio: ["pipe", "pipe", "pipe"],
193
+ });
194
+ // Copy tar to local
195
+ execFileSync("docker", ["cp", `${this.container}:${remoteTar}`, join(tmpDir, "batch.tar")], {
196
+ timeout: 30_000,
197
+ });
198
+ // Clean up tar on container
199
+ execFileSync("docker", ["exec", this.container, "rm", "-f", remoteTar], {
200
+ timeout: 5_000,
201
+ stdio: ["pipe", "pipe", "pipe"],
202
+ });
203
+ // Extract locally (with -P to preserve absolute paths)
204
+ execFileSync("tar", ["xf", join(tmpDir, "batch.tar"), "-P", "-C", tmpDir], {
205
+ timeout: 30_000,
206
+ });
207
+ // Read extracted files — tar with -P preserves the full path under tmpDir
208
+ for (const path of paths) {
209
+ const localPath = join(tmpDir, path);
210
+ try {
211
+ result.set(path, readFileSync(localPath));
212
+ }
213
+ catch {
214
+ // File wasn't in the tar (didn't exist on container)
215
+ }
216
+ }
217
+ }
218
+ catch {
219
+ // Fallback: read files one by one
220
+ for (const path of paths) {
221
+ try {
222
+ const single = await this.readSingleFile(path);
223
+ const content = single.get(path);
224
+ if (content)
225
+ result.set(path, content);
226
+ }
227
+ catch {
228
+ // Skip missing files
229
+ }
230
+ }
231
+ }
232
+ finally {
233
+ rmSync(tmpDir, { recursive: true, force: true });
234
+ }
235
+ return result;
236
+ }
237
+ async readSingleFile(path) {
238
+ const result = new Map();
239
+ const tmpDir = mkdtempSync(join(tmpdir(), "al-read-"));
240
+ try {
241
+ execFileSync("docker", ["cp", `${this.container}:${path}`, join(tmpDir, "file")], {
242
+ timeout: 30_000,
243
+ stdio: ["pipe", "pipe", "pipe"],
244
+ });
245
+ result.set(path, readFileSync(join(tmpDir, "file")));
246
+ }
247
+ catch {
248
+ // File not found — omit from result
249
+ }
250
+ finally {
251
+ rmSync(tmpDir, { recursive: true, force: true });
252
+ }
253
+ return result;
254
+ }
255
+ async writeFiles(files) {
256
+ if (files.size === 0)
257
+ return;
258
+ const tmpDir = mkdtempSync(join(tmpdir(), "al-write-"));
259
+ try {
260
+ if (files.size === 1) {
261
+ const [[path, content]] = [...files.entries()];
262
+ // Ensure parent directory exists on container
263
+ const dir = dirname(path);
264
+ if (dir !== "/" && dir !== ".") {
265
+ execFileSync("docker", ["exec", this.container, "mkdir", "-p", dir], {
266
+ timeout: 10_000,
267
+ stdio: ["pipe", "pipe", "pipe"],
268
+ });
269
+ }
270
+ const localPath = join(tmpDir, basename(path));
271
+ writeFileSync(localPath, content);
272
+ execFileSync("docker", ["cp", localPath, `${this.container}:${path}`], {
273
+ timeout: 30_000,
274
+ });
275
+ return;
276
+ }
277
+ // Batch write: create a tar locally → docker cp to container → extract
278
+ for (const [path, content] of files) {
279
+ const localPath = join(tmpDir, "payload", path);
280
+ mkdirSync(dirname(localPath), { recursive: true });
281
+ writeFileSync(localPath, content);
282
+ }
283
+ // Create tar preserving absolute paths.
284
+ // List top-level entries explicitly (not ".") to avoid a "." entry in the
285
+ // tar that maps to "/" on extract and fails with permission errors when
286
+ // the container user is non-root.
287
+ const tarPath = join(tmpDir, "batch.tar");
288
+ const payloadDir = join(tmpDir, "payload");
289
+ const topEntries = readdirSync(payloadDir);
290
+ execFileSync("tar", ["cf", tarPath, "-P", "-C", payloadDir, ...topEntries], {
291
+ timeout: 30_000,
292
+ });
293
+ // Copy tar to container
294
+ const remoteTar = `/tmp/al-batch-${randomBytes(4).toString("hex")}.tar`;
295
+ execFileSync("docker", ["cp", tarPath, `${this.container}:${remoteTar}`], {
296
+ timeout: 30_000,
297
+ });
298
+ // Extract on container and clean up
299
+ execFileSync("docker", [
300
+ "exec", this.container,
301
+ "bash", "-c", `tar xf ${remoteTar} -P -C / && rm -f ${remoteTar}`,
302
+ ], {
303
+ timeout: 30_000,
304
+ stdio: ["pipe", "pipe", "pipe"],
305
+ });
306
+ }
307
+ finally {
308
+ rmSync(tmpDir, { recursive: true, force: true });
309
+ }
310
+ }
311
+ async close() {
312
+ this._closed = true;
313
+ if (this.shell) {
314
+ this.ready = false;
315
+ try {
316
+ this.shell.stdin?.write("exit\n");
317
+ this.shell.stdin?.end();
318
+ }
319
+ catch {
320
+ // stdin may already be closed
321
+ }
322
+ this.shell.kill();
323
+ this.shell = null;
324
+ }
325
+ }
326
+ }
327
+ /** Escape a string for use in a shell command. */
328
+ function shellQuote(s) {
329
+ return `'${s.replace(/'/g, "'\\''")}'`;
330
+ }
331
+ //# sourceMappingURL=docker-exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-exec.js","sourceRoot":"","sources":["../../src/transport/docker-exec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * HostUserTransport — executes commands as a different OS user via `sudo -u`,
3
+ * maintaining a persistent shell session.
4
+ *
5
+ * File I/O uses direct filesystem access since we're on the same machine —
6
+ * files are read/written as root (the scheduler process) then chowned to
7
+ * the target user.
8
+ */
9
+ import type { Transport, ExecResult, ExecOptions } from "./transport.js";
10
+ export interface HostUserTransportOpts {
11
+ /** OS user to run commands as. */
12
+ user: string;
13
+ /** Additional OS groups. */
14
+ groups?: string[];
15
+ /** Initial working directory. Default: user's home dir. */
16
+ cwd?: string;
17
+ }
18
+ export declare class HostUserTransport implements Transport {
19
+ private opts;
20
+ private shell;
21
+ private ready;
22
+ private buffer;
23
+ private uid?;
24
+ private gid?;
25
+ constructor(opts: HostUserTransportOpts);
26
+ /** Start the persistent shell as the target user. Must be called before exec(). */
27
+ connect(): Promise<void>;
28
+ /**
29
+ * Wait for a delimiter to appear in the stdout buffer.
30
+ */
31
+ private waitForDelimiter;
32
+ exec(command: string, options?: ExecOptions): Promise<ExecResult>;
33
+ readFiles(paths: string[]): Promise<Map<string, Buffer>>;
34
+ writeFiles(files: Map<string, Buffer>): Promise<void>;
35
+ close(): Promise<void>;
36
+ }
37
+ //# sourceMappingURL=host-user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-user.d.ts","sourceRoot":"","sources":["../../src/transport/host-user.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAYzE,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,iBAAkB,YAAW,SAAS;IAOrC,OAAO,CAAC,IAAI;IANxB,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,GAAG,CAAC,CAAS;IACrB,OAAO,CAAC,GAAG,CAAC,CAAS;gBAED,IAAI,EAAE,qBAAqB;IAE/C,mFAAmF;IAC7E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgClB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAqEjE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAgBxD,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAa7B"}
@@ -0,0 +1,232 @@
1
+ /**
2
+ * HostUserTransport — executes commands as a different OS user via `sudo -u`,
3
+ * maintaining a persistent shell session.
4
+ *
5
+ * File I/O uses direct filesystem access since we're on the same machine —
6
+ * files are read/written as root (the scheduler process) then chowned to
7
+ * the target user.
8
+ */
9
+ import { spawn } from "child_process";
10
+ import { randomBytes } from "crypto";
11
+ import { readFileSync, writeFileSync, mkdirSync, chownSync, statSync, } from "fs";
12
+ import { dirname } from "path";
13
+ /** How long to wait for the shell to become ready after spawning (ms). */
14
+ const SHELL_READY_TIMEOUT_MS = 10_000;
15
+ /** Default command execution timeout (ms). */
16
+ const DEFAULT_EXEC_TIMEOUT_MS = 300_000;
17
+ /** Generate a unique delimiter that won't appear in command output. */
18
+ function makeDelimiter() {
19
+ return `__AL_DELIM_${randomBytes(8).toString("hex")}__`;
20
+ }
21
+ export class HostUserTransport {
22
+ opts;
23
+ shell = null;
24
+ ready = false;
25
+ buffer = "";
26
+ uid;
27
+ gid;
28
+ constructor(opts) {
29
+ this.opts = opts;
30
+ }
31
+ /** Start the persistent shell as the target user. Must be called before exec(). */
32
+ async connect() {
33
+ const args = ["-u", this.opts.user, "--", "bash", "--norc", "--noprofile"];
34
+ // If groups are specified, use sg or newgrp — but sudo -u is simpler
35
+ // Groups are set via the user's OS group membership, not per-command
36
+ this.shell = spawn("sudo", args, {
37
+ stdio: ["pipe", "pipe", "pipe"],
38
+ });
39
+ this.shell.stdout.on("data", (chunk) => {
40
+ this.buffer += chunk.toString();
41
+ });
42
+ this.shell.on("exit", () => {
43
+ this.ready = false;
44
+ });
45
+ // Wait for shell to be ready
46
+ const readyDelim = makeDelimiter();
47
+ this.shell.stdin.write(`echo "${readyDelim} $?"\n`);
48
+ await this.waitForDelimiter(readyDelim, SHELL_READY_TIMEOUT_MS);
49
+ this.ready = true;
50
+ // Resolve the user's uid/gid for file ownership
51
+ try {
52
+ const { stdout } = await this.exec("id -u");
53
+ this.uid = parseInt(stdout.trim(), 10);
54
+ const { stdout: gidStr } = await this.exec("id -g");
55
+ this.gid = parseInt(gidStr.trim(), 10);
56
+ }
57
+ catch {
58
+ // Non-critical — files won't be chowned if we can't resolve uid/gid
59
+ }
60
+ // Set initial cwd if specified
61
+ if (this.opts.cwd) {
62
+ await this.exec(`cd ${shellQuote(this.opts.cwd)}`);
63
+ }
64
+ }
65
+ /**
66
+ * Wait for a delimiter to appear in the stdout buffer.
67
+ */
68
+ waitForDelimiter(delimiter, timeoutMs) {
69
+ return new Promise((resolve, reject) => {
70
+ const timer = setTimeout(() => {
71
+ clearInterval(poll);
72
+ reject(new Error(`Timed out waiting for shell response (${timeoutMs}ms)`));
73
+ }, timeoutMs);
74
+ const check = () => {
75
+ const idx = this.buffer.indexOf(delimiter);
76
+ if (idx === -1)
77
+ return;
78
+ const lineEnd = this.buffer.indexOf("\n", idx);
79
+ if (lineEnd === -1)
80
+ return;
81
+ const output = this.buffer.slice(0, idx);
82
+ const delimLine = this.buffer.slice(idx, lineEnd);
83
+ this.buffer = this.buffer.slice(lineEnd + 1);
84
+ clearTimeout(timer);
85
+ clearInterval(poll);
86
+ const match = delimLine.match(/\s+(\d+)\s*$/);
87
+ const exitCode = match ? parseInt(match[1], 10) : 0;
88
+ resolve({ output, exitCode });
89
+ };
90
+ const poll = setInterval(check, 10);
91
+ check();
92
+ });
93
+ }
94
+ async exec(command, options) {
95
+ if (!this.shell || !this.ready) {
96
+ throw new Error("Transport not connected. Call connect() first.");
97
+ }
98
+ const delimiter = makeDelimiter();
99
+ let stderr = "";
100
+ const stderrHandler = (chunk) => {
101
+ stderr += chunk.toString();
102
+ };
103
+ this.shell.stderr.on("data", stderrHandler);
104
+ // Abort handling
105
+ let aborted = false;
106
+ const onAbort = () => {
107
+ aborted = true;
108
+ this.shell?.stdin?.write("\x03\n");
109
+ };
110
+ if (options?.signal) {
111
+ if (options.signal.aborted) {
112
+ this.shell.stderr.removeListener("data", stderrHandler);
113
+ return { stdout: "", stderr: "Aborted", exitCode: 130 };
114
+ }
115
+ options.signal.addEventListener("abort", onAbort, { once: true });
116
+ }
117
+ const timeoutMs = options?.timeout ?? DEFAULT_EXEC_TIMEOUT_MS;
118
+ // Streaming
119
+ let streamingInterval;
120
+ let lastStreamedIdx = 0;
121
+ if (options?.onData) {
122
+ streamingInterval = setInterval(() => {
123
+ if (this.buffer.length > lastStreamedIdx) {
124
+ const newData = this.buffer.slice(lastStreamedIdx);
125
+ const delimIdx = newData.indexOf("__AL_DELIM_");
126
+ const safeData = delimIdx >= 0 ? newData.slice(0, delimIdx) : newData;
127
+ if (safeData.length > 0) {
128
+ options.onData(Buffer.from(safeData));
129
+ lastStreamedIdx += safeData.length;
130
+ }
131
+ }
132
+ }, 50);
133
+ }
134
+ try {
135
+ this.shell.stdin.write(`${command}\necho "${delimiter} $?"\n`);
136
+ const { output, exitCode } = await this.waitForDelimiter(delimiter, timeoutMs);
137
+ return {
138
+ stdout: output.trimEnd(),
139
+ stderr: stderr.trimEnd(),
140
+ exitCode: aborted ? 130 : exitCode,
141
+ };
142
+ }
143
+ catch (err) {
144
+ if (aborted) {
145
+ return { stdout: "", stderr: "Aborted", exitCode: 130 };
146
+ }
147
+ throw err;
148
+ }
149
+ finally {
150
+ this.shell.stderr.removeListener("data", stderrHandler);
151
+ if (options?.signal) {
152
+ options.signal.removeEventListener("abort", onAbort);
153
+ }
154
+ if (streamingInterval)
155
+ clearInterval(streamingInterval);
156
+ }
157
+ }
158
+ async readFiles(paths) {
159
+ const result = new Map();
160
+ if (paths.length === 0)
161
+ return result;
162
+ // Direct filesystem read — the scheduler process can read any file
163
+ for (const path of paths) {
164
+ try {
165
+ result.set(path, readFileSync(path));
166
+ }
167
+ catch {
168
+ // Skip missing files
169
+ }
170
+ }
171
+ return result;
172
+ }
173
+ async writeFiles(files) {
174
+ if (files.size === 0)
175
+ return;
176
+ for (const [path, content] of files) {
177
+ const dir = dirname(path);
178
+ if (dir !== "/" && dir !== ".") {
179
+ mkdirSync(dir, { recursive: true });
180
+ // Chown directory to target user
181
+ if (this.uid != null && this.gid != null) {
182
+ try {
183
+ chownRecursiveNew(dir, this.uid, this.gid);
184
+ }
185
+ catch { /* best effort */ }
186
+ }
187
+ }
188
+ writeFileSync(path, content);
189
+ // Chown file to target user so they can read/write it
190
+ if (this.uid != null && this.gid != null) {
191
+ try {
192
+ chownSync(path, this.uid, this.gid);
193
+ }
194
+ catch { /* best effort */ }
195
+ }
196
+ }
197
+ }
198
+ async close() {
199
+ if (this.shell) {
200
+ this.ready = false;
201
+ try {
202
+ this.shell.stdin?.write("exit\n");
203
+ this.shell.stdin?.end();
204
+ }
205
+ catch {
206
+ // stdin may already be closed
207
+ }
208
+ this.shell.kill();
209
+ this.shell = null;
210
+ }
211
+ }
212
+ }
213
+ /** Escape a string for use in a shell command. */
214
+ function shellQuote(s) {
215
+ return `'${s.replace(/'/g, "'\\''")}'`;
216
+ }
217
+ /**
218
+ * Recursively chown newly created directories up to an existing parent.
219
+ * Only chowns directories that were created by mkdirSync (newly created).
220
+ */
221
+ function chownRecursiveNew(dir, uid, gid) {
222
+ try {
223
+ const stat = statSync(dir);
224
+ if (stat.uid !== uid) {
225
+ chownSync(dir, uid, gid);
226
+ }
227
+ }
228
+ catch {
229
+ // Directory doesn't exist or inaccessible
230
+ }
231
+ }
232
+ //# sourceMappingURL=host-user.js.map