@herdctl/core 1.3.0 → 2.0.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 (181) hide show
  1. package/dist/config/__tests__/agent.test.js +12 -12
  2. package/dist/config/__tests__/agent.test.js.map +1 -1
  3. package/dist/config/__tests__/loader.test.js +201 -4
  4. package/dist/config/__tests__/loader.test.js.map +1 -1
  5. package/dist/config/__tests__/merge.test.js +29 -4
  6. package/dist/config/__tests__/merge.test.js.map +1 -1
  7. package/dist/config/__tests__/parser.test.js +13 -13
  8. package/dist/config/__tests__/parser.test.js.map +1 -1
  9. package/dist/config/__tests__/schema.test.js +10 -10
  10. package/dist/config/__tests__/schema.test.js.map +1 -1
  11. package/dist/config/index.d.ts +1 -1
  12. package/dist/config/index.d.ts.map +1 -1
  13. package/dist/config/index.js +2 -2
  14. package/dist/config/index.js.map +1 -1
  15. package/dist/config/loader.d.ts.map +1 -1
  16. package/dist/config/loader.js +71 -0
  17. package/dist/config/loader.js.map +1 -1
  18. package/dist/config/merge.d.ts +4 -1
  19. package/dist/config/merge.d.ts.map +1 -1
  20. package/dist/config/merge.js +16 -0
  21. package/dist/config/merge.js.map +1 -1
  22. package/dist/config/schema.d.ts +906 -89
  23. package/dist/config/schema.d.ts.map +1 -1
  24. package/dist/config/schema.js +109 -7
  25. package/dist/config/schema.js.map +1 -1
  26. package/dist/fleet-manager/__tests__/coverage.test.js +25 -24
  27. package/dist/fleet-manager/__tests__/coverage.test.js.map +1 -1
  28. package/dist/fleet-manager/__tests__/discord-manager.test.js +9 -2
  29. package/dist/fleet-manager/__tests__/discord-manager.test.js.map +1 -1
  30. package/dist/fleet-manager/__tests__/integration.test.js +27 -0
  31. package/dist/fleet-manager/__tests__/integration.test.js.map +1 -1
  32. package/dist/fleet-manager/__tests__/job-control.test.js +66 -0
  33. package/dist/fleet-manager/__tests__/job-control.test.js.map +1 -1
  34. package/dist/fleet-manager/__tests__/status-queries.test.js +12 -11
  35. package/dist/fleet-manager/__tests__/status-queries.test.js.map +1 -1
  36. package/dist/fleet-manager/config-reload.js +9 -9
  37. package/dist/fleet-manager/config-reload.js.map +1 -1
  38. package/dist/fleet-manager/discord-manager.d.ts.map +1 -1
  39. package/dist/fleet-manager/discord-manager.js +27 -4
  40. package/dist/fleet-manager/discord-manager.js.map +1 -1
  41. package/dist/fleet-manager/fleet-manager.d.ts +11 -0
  42. package/dist/fleet-manager/fleet-manager.d.ts.map +1 -1
  43. package/dist/fleet-manager/fleet-manager.js +27 -0
  44. package/dist/fleet-manager/fleet-manager.js.map +1 -1
  45. package/dist/fleet-manager/job-control.d.ts +1 -1
  46. package/dist/fleet-manager/job-control.d.ts.map +1 -1
  47. package/dist/fleet-manager/job-control.js +36 -14
  48. package/dist/fleet-manager/job-control.js.map +1 -1
  49. package/dist/fleet-manager/schedule-executor.d.ts +1 -1
  50. package/dist/fleet-manager/schedule-executor.d.ts.map +1 -1
  51. package/dist/fleet-manager/schedule-executor.js +11 -14
  52. package/dist/fleet-manager/schedule-executor.js.map +1 -1
  53. package/dist/fleet-manager/status-queries.js +7 -7
  54. package/dist/fleet-manager/status-queries.js.map +1 -1
  55. package/dist/fleet-manager/types.d.ts +10 -2
  56. package/dist/fleet-manager/types.d.ts.map +1 -1
  57. package/dist/fleet-manager/working-directory-helper.d.ts +29 -0
  58. package/dist/fleet-manager/working-directory-helper.d.ts.map +1 -0
  59. package/dist/fleet-manager/working-directory-helper.js +36 -0
  60. package/dist/fleet-manager/working-directory-helper.js.map +1 -0
  61. package/dist/index.d.ts +2 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +4 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/runner/__tests__/job-executor.test.js +449 -118
  66. package/dist/runner/__tests__/job-executor.test.js.map +1 -1
  67. package/dist/runner/__tests__/sdk-adapter.test.js +147 -23
  68. package/dist/runner/__tests__/sdk-adapter.test.js.map +1 -1
  69. package/dist/runner/index.d.ts +2 -0
  70. package/dist/runner/index.d.ts.map +1 -1
  71. package/dist/runner/index.js +1 -0
  72. package/dist/runner/index.js.map +1 -1
  73. package/dist/runner/job-executor.d.ts +12 -8
  74. package/dist/runner/job-executor.d.ts.map +1 -1
  75. package/dist/runner/job-executor.js +257 -126
  76. package/dist/runner/job-executor.js.map +1 -1
  77. package/dist/runner/runtime/__tests__/cli-session-path.test.d.ts +2 -0
  78. package/dist/runner/runtime/__tests__/cli-session-path.test.d.ts.map +1 -0
  79. package/dist/runner/runtime/__tests__/cli-session-path.test.js +150 -0
  80. package/dist/runner/runtime/__tests__/cli-session-path.test.js.map +1 -0
  81. package/dist/runner/runtime/__tests__/docker-config.test.d.ts +2 -0
  82. package/dist/runner/runtime/__tests__/docker-config.test.d.ts.map +1 -0
  83. package/dist/runner/runtime/__tests__/docker-config.test.js +352 -0
  84. package/dist/runner/runtime/__tests__/docker-config.test.js.map +1 -0
  85. package/dist/runner/runtime/__tests__/docker-security.test.d.ts +2 -0
  86. package/dist/runner/runtime/__tests__/docker-security.test.d.ts.map +1 -0
  87. package/dist/runner/runtime/__tests__/docker-security.test.js +384 -0
  88. package/dist/runner/runtime/__tests__/docker-security.test.js.map +1 -0
  89. package/dist/runner/runtime/__tests__/factory.test.d.ts +2 -0
  90. package/dist/runner/runtime/__tests__/factory.test.d.ts.map +1 -0
  91. package/dist/runner/runtime/__tests__/factory.test.js +149 -0
  92. package/dist/runner/runtime/__tests__/factory.test.js.map +1 -0
  93. package/dist/runner/runtime/__tests__/integration.test.d.ts +2 -0
  94. package/dist/runner/runtime/__tests__/integration.test.d.ts.map +1 -0
  95. package/dist/runner/runtime/__tests__/integration.test.js +274 -0
  96. package/dist/runner/runtime/__tests__/integration.test.js.map +1 -0
  97. package/dist/runner/runtime/cli-runtime.d.ts +107 -0
  98. package/dist/runner/runtime/cli-runtime.d.ts.map +1 -0
  99. package/dist/runner/runtime/cli-runtime.js +335 -0
  100. package/dist/runner/runtime/cli-runtime.js.map +1 -0
  101. package/dist/runner/runtime/cli-session-path.d.ts +108 -0
  102. package/dist/runner/runtime/cli-session-path.d.ts.map +1 -0
  103. package/dist/runner/runtime/cli-session-path.js +173 -0
  104. package/dist/runner/runtime/cli-session-path.js.map +1 -0
  105. package/dist/runner/runtime/cli-session-watcher.d.ts +55 -0
  106. package/dist/runner/runtime/cli-session-watcher.d.ts.map +1 -0
  107. package/dist/runner/runtime/cli-session-watcher.js +187 -0
  108. package/dist/runner/runtime/cli-session-watcher.js.map +1 -0
  109. package/dist/runner/runtime/container-manager.d.ts +76 -0
  110. package/dist/runner/runtime/container-manager.d.ts.map +1 -0
  111. package/dist/runner/runtime/container-manager.js +229 -0
  112. package/dist/runner/runtime/container-manager.js.map +1 -0
  113. package/dist/runner/runtime/container-runner.d.ts +62 -0
  114. package/dist/runner/runtime/container-runner.d.ts.map +1 -0
  115. package/dist/runner/runtime/container-runner.js +235 -0
  116. package/dist/runner/runtime/container-runner.js.map +1 -0
  117. package/dist/runner/runtime/docker-config.d.ts +100 -0
  118. package/dist/runner/runtime/docker-config.d.ts.map +1 -0
  119. package/dist/runner/runtime/docker-config.js +98 -0
  120. package/dist/runner/runtime/docker-config.js.map +1 -0
  121. package/dist/runner/runtime/factory.d.ts +63 -0
  122. package/dist/runner/runtime/factory.d.ts.map +1 -0
  123. package/dist/runner/runtime/factory.js +68 -0
  124. package/dist/runner/runtime/factory.js.map +1 -0
  125. package/dist/runner/runtime/index.d.ts +20 -0
  126. package/dist/runner/runtime/index.d.ts.map +1 -0
  127. package/dist/runner/runtime/index.js +21 -0
  128. package/dist/runner/runtime/index.js.map +1 -0
  129. package/dist/runner/runtime/interface.d.ts +59 -0
  130. package/dist/runner/runtime/interface.d.ts.map +1 -0
  131. package/dist/runner/runtime/interface.js +12 -0
  132. package/dist/runner/runtime/interface.js.map +1 -0
  133. package/dist/runner/runtime/sdk-runtime.d.ts +46 -0
  134. package/dist/runner/runtime/sdk-runtime.d.ts.map +1 -0
  135. package/dist/runner/runtime/sdk-runtime.js +63 -0
  136. package/dist/runner/runtime/sdk-runtime.js.map +1 -0
  137. package/dist/runner/sdk-adapter.d.ts +4 -0
  138. package/dist/runner/sdk-adapter.d.ts.map +1 -1
  139. package/dist/runner/sdk-adapter.js +35 -16
  140. package/dist/runner/sdk-adapter.js.map +1 -1
  141. package/dist/runner/types.d.ts +12 -6
  142. package/dist/runner/types.d.ts.map +1 -1
  143. package/dist/scheduler/__tests__/schedule-runner.test.js +61 -50
  144. package/dist/scheduler/__tests__/schedule-runner.test.js.map +1 -1
  145. package/dist/scheduler/schedule-runner.d.ts +1 -4
  146. package/dist/scheduler/schedule-runner.d.ts.map +1 -1
  147. package/dist/scheduler/schedule-runner.js +40 -8
  148. package/dist/scheduler/schedule-runner.js.map +1 -1
  149. package/dist/state/__tests__/session-schema.test.js +4 -0
  150. package/dist/state/__tests__/session-schema.test.js.map +1 -1
  151. package/dist/state/__tests__/session-validation.test.d.ts +2 -0
  152. package/dist/state/__tests__/session-validation.test.d.ts.map +1 -0
  153. package/dist/state/__tests__/session-validation.test.js +446 -0
  154. package/dist/state/__tests__/session-validation.test.js.map +1 -0
  155. package/dist/state/__tests__/session.test.js +68 -0
  156. package/dist/state/__tests__/session.test.js.map +1 -1
  157. package/dist/state/__tests__/working-directory-validation.test.d.ts +5 -0
  158. package/dist/state/__tests__/working-directory-validation.test.d.ts.map +1 -0
  159. package/dist/state/__tests__/working-directory-validation.test.js +101 -0
  160. package/dist/state/__tests__/working-directory-validation.test.js.map +1 -0
  161. package/dist/state/index.d.ts +2 -0
  162. package/dist/state/index.d.ts.map +1 -1
  163. package/dist/state/index.js +4 -0
  164. package/dist/state/index.js.map +1 -1
  165. package/dist/state/schemas/session-info.d.ts +32 -0
  166. package/dist/state/schemas/session-info.d.ts.map +1 -1
  167. package/dist/state/schemas/session-info.js +22 -0
  168. package/dist/state/schemas/session-info.js.map +1 -1
  169. package/dist/state/session-validation.d.ts +202 -0
  170. package/dist/state/session-validation.d.ts.map +1 -0
  171. package/dist/state/session-validation.js +407 -0
  172. package/dist/state/session-validation.js.map +1 -0
  173. package/dist/state/session.d.ts +23 -3
  174. package/dist/state/session.d.ts.map +1 -1
  175. package/dist/state/session.js +41 -6
  176. package/dist/state/session.js.map +1 -1
  177. package/dist/state/working-directory-validation.d.ts +52 -0
  178. package/dist/state/working-directory-validation.d.ts.map +1 -0
  179. package/dist/state/working-directory-validation.js +81 -0
  180. package/dist/state/working-directory-validation.js.map +1 -0
  181. package/package.json +7 -2
@@ -0,0 +1,55 @@
1
+ /**
2
+ * CLI session file watcher - monitors CLI session files for new messages
3
+ *
4
+ * Event-driven watcher that yields messages as they're written to the session file.
5
+ * No polling, no timeouts - just clean async/await.
6
+ */
7
+ import type { SDKMessage } from "../types.js";
8
+ /**
9
+ * Watches a CLI session file and yields new messages as they're written
10
+ */
11
+ export declare class CLISessionWatcher {
12
+ private watcher;
13
+ private lastLineCount;
14
+ private sessionFilePath;
15
+ private messageQueue;
16
+ private pendingMessageResolve;
17
+ private stopped;
18
+ constructor(sessionFilePath: string);
19
+ /**
20
+ * Initialize watcher by reading existing file content
21
+ *
22
+ * When resuming a session, call this before watch() to skip existing
23
+ * messages and only process new content appended after this point.
24
+ */
25
+ initialize(): Promise<void>;
26
+ /**
27
+ * Process file and queue any new messages
28
+ */
29
+ private processFile;
30
+ /**
31
+ * Watch session file and yield messages as they arrive
32
+ *
33
+ * This is event-driven - it waits (blocks) until messages are available.
34
+ * No polling, no timeouts.
35
+ */
36
+ watch(): AsyncIterable<SDKMessage>;
37
+ /**
38
+ * Process any remaining messages in the file
39
+ *
40
+ * Called when the CLI process exits to ensure we don't miss final messages
41
+ * that haven't triggered a chokidar event yet.
42
+ *
43
+ * @returns Array of any remaining messages found
44
+ */
45
+ flushRemainingMessages(): Promise<SDKMessage[]>;
46
+ /**
47
+ * Stop watching
48
+ */
49
+ stop(): void;
50
+ }
51
+ /**
52
+ * Convenience function to watch a session file
53
+ */
54
+ export declare function watchSessionFile(sessionFilePath: string, signal?: AbortSignal): AsyncIterable<SDKMessage>;
55
+ //# sourceMappingURL=cli-session-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-session-watcher.d.ts","sourceRoot":"","sources":["../../../src/runner/runtime/cli-session-watcher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAkD;IACjE,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,qBAAqB,CAA6B;IAC1D,OAAO,CAAC,OAAO,CAAS;gBAEZ,eAAe,EAAE,MAAM;IAInC;;;;;OAKG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBjC;;OAEG;YACW,WAAW;IAwCzB;;;;;OAKG;IACI,KAAK,IAAI,aAAa,CAAC,UAAU,CAAC;IA0DzC;;;;;;;OAOG;IACG,sBAAsB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAYrD;;OAEG;IACH,IAAI,IAAI,IAAI;CAab;AAED;;GAEG;AACH,wBAAuB,gBAAgB,CACrC,eAAe,EAAE,MAAM,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,aAAa,CAAC,UAAU,CAAC,CAa3B"}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * CLI session file watcher - monitors CLI session files for new messages
3
+ *
4
+ * Event-driven watcher that yields messages as they're written to the session file.
5
+ * No polling, no timeouts - just clean async/await.
6
+ */
7
+ import chokidar from "chokidar";
8
+ import { readFile } from "node:fs/promises";
9
+ /**
10
+ * Watches a CLI session file and yields new messages as they're written
11
+ */
12
+ export class CLISessionWatcher {
13
+ watcher = null;
14
+ lastLineCount = 0;
15
+ sessionFilePath;
16
+ messageQueue = [];
17
+ pendingMessageResolve = null;
18
+ stopped = false;
19
+ constructor(sessionFilePath) {
20
+ this.sessionFilePath = sessionFilePath;
21
+ }
22
+ /**
23
+ * Initialize watcher by reading existing file content
24
+ *
25
+ * When resuming a session, call this before watch() to skip existing
26
+ * messages and only process new content appended after this point.
27
+ */
28
+ async initialize() {
29
+ try {
30
+ const content = await readFile(this.sessionFilePath, "utf-8");
31
+ const lines = content.split("\n").filter((line) => line.trim() !== "");
32
+ this.lastLineCount = lines.length;
33
+ console.log(`[CLISessionWatcher] Initialized at line ${this.lastLineCount}, will skip existing content`);
34
+ }
35
+ catch (error) {
36
+ if (error.code === "ENOENT") {
37
+ // File doesn't exist yet - that's fine for new sessions
38
+ console.log(`[CLISessionWatcher] File doesn't exist yet, starting from line 0`);
39
+ }
40
+ else {
41
+ console.warn(`[CLISessionWatcher] Failed to initialize: ${error instanceof Error ? error.message : String(error)}`);
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Process file and queue any new messages
47
+ */
48
+ async processFile() {
49
+ try {
50
+ const content = await readFile(this.sessionFilePath, "utf-8");
51
+ const lines = content.split("\n").filter((line) => line.trim() !== "");
52
+ // Process only new lines since last read
53
+ const newLines = lines.slice(this.lastLineCount);
54
+ this.lastLineCount = lines.length;
55
+ console.log(`[CLISessionWatcher] Processing ${newLines.length} new lines`);
56
+ // Parse and queue valid messages
57
+ for (const line of newLines) {
58
+ try {
59
+ const message = JSON.parse(line);
60
+ console.log(`[CLISessionWatcher] Queued message type: ${message.type}`);
61
+ this.messageQueue.push(message);
62
+ }
63
+ catch (error) {
64
+ // Skip invalid JSON lines (CLI may output non-JSON)
65
+ console.warn(`[CLISessionWatcher] Failed to parse line: ${error instanceof Error ? error.message : String(error)}`);
66
+ }
67
+ }
68
+ // If someone is waiting for a message and we have one, wake them up
69
+ if (this.pendingMessageResolve && this.messageQueue.length > 0) {
70
+ console.log(`[CLISessionWatcher] Waking up waiting iterator`);
71
+ this.pendingMessageResolve();
72
+ this.pendingMessageResolve = null;
73
+ }
74
+ }
75
+ catch (error) {
76
+ if (error.code !== "ENOENT") {
77
+ console.warn(`[CLISessionWatcher] Error reading session file: ${error instanceof Error ? error.message : String(error)}`);
78
+ }
79
+ }
80
+ }
81
+ /**
82
+ * Watch session file and yield messages as they arrive
83
+ *
84
+ * This is event-driven - it waits (blocks) until messages are available.
85
+ * No polling, no timeouts.
86
+ */
87
+ async *watch() {
88
+ // Configure chokidar
89
+ this.watcher = chokidar.watch(this.sessionFilePath, {
90
+ awaitWriteFinish: {
91
+ stabilityThreshold: 500,
92
+ pollInterval: 100,
93
+ },
94
+ ignoreInitial: false,
95
+ });
96
+ this.watcher.on("add", async () => {
97
+ console.log("[CLISessionWatcher] File 'add' event");
98
+ await this.processFile();
99
+ });
100
+ this.watcher.on("change", async () => {
101
+ console.log("[CLISessionWatcher] File 'change' event");
102
+ await this.processFile();
103
+ });
104
+ this.watcher.on("error", (error) => {
105
+ console.error(`[CLISessionWatcher] Watcher error: ${error instanceof Error ? error.message : String(error)}`);
106
+ });
107
+ try {
108
+ while (!this.stopped) {
109
+ // If we have queued messages, yield them
110
+ while (this.messageQueue.length > 0) {
111
+ const message = this.messageQueue.shift();
112
+ console.log(`[CLISessionWatcher] Yielding message type: ${message.type}`);
113
+ yield message;
114
+ }
115
+ // No messages - wait for chokidar to add one
116
+ console.log(`[CLISessionWatcher] No messages, waiting for chokidar event`);
117
+ await new Promise((resolve) => {
118
+ this.pendingMessageResolve = resolve;
119
+ // Also wake up if stopped
120
+ if (this.stopped) {
121
+ resolve();
122
+ }
123
+ });
124
+ console.log(`[CLISessionWatcher] Woke up (stopped: ${this.stopped}, queue: ${this.messageQueue.length})`);
125
+ // Check if we should exit
126
+ if (this.stopped) {
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ finally {
132
+ console.log(`[CLISessionWatcher] Generator exiting`);
133
+ this.stop();
134
+ }
135
+ }
136
+ /**
137
+ * Process any remaining messages in the file
138
+ *
139
+ * Called when the CLI process exits to ensure we don't miss final messages
140
+ * that haven't triggered a chokidar event yet.
141
+ *
142
+ * @returns Array of any remaining messages found
143
+ */
144
+ async flushRemainingMessages() {
145
+ console.log(`[CLISessionWatcher] Flushing remaining messages from file`);
146
+ await this.processFile();
147
+ // Return all queued messages
148
+ const messages = [...this.messageQueue];
149
+ this.messageQueue = [];
150
+ console.log(`[CLISessionWatcher] Flushed ${messages.length} remaining message(s)`);
151
+ return messages;
152
+ }
153
+ /**
154
+ * Stop watching
155
+ */
156
+ stop() {
157
+ console.log(`[CLISessionWatcher] stop() called`);
158
+ this.stopped = true;
159
+ if (this.watcher) {
160
+ this.watcher.close();
161
+ this.watcher = null;
162
+ }
163
+ // Wake up any waiting iterator
164
+ if (this.pendingMessageResolve) {
165
+ this.pendingMessageResolve();
166
+ this.pendingMessageResolve = null;
167
+ }
168
+ }
169
+ }
170
+ /**
171
+ * Convenience function to watch a session file
172
+ */
173
+ export async function* watchSessionFile(sessionFilePath, signal) {
174
+ const watcher = new CLISessionWatcher(sessionFilePath);
175
+ try {
176
+ for await (const message of watcher.watch()) {
177
+ if (signal?.aborted) {
178
+ break;
179
+ }
180
+ yield message;
181
+ }
182
+ }
183
+ finally {
184
+ watcher.stop();
185
+ }
186
+ }
187
+ //# sourceMappingURL=cli-session-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-session-watcher.js","sourceRoot":"","sources":["../../../src/runner/runtime/cli-session-watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Docker container lifecycle management
3
+ *
4
+ * Handles container creation, security configuration, and cleanup.
5
+ * Uses dockerode for Docker API communication.
6
+ */
7
+ import type { Container, Exec } from "dockerode";
8
+ import type { DockerConfig, PathMapping } from "./docker-config.js";
9
+ import type { ResolvedAgent } from "../../config/index.js";
10
+ /**
11
+ * Container manager for herdctl Docker execution
12
+ */
13
+ export declare class ContainerManager {
14
+ private docker;
15
+ private runningContainers;
16
+ constructor(docker?: import("dockerode"));
17
+ /**
18
+ * Get or create a container for an agent
19
+ *
20
+ * For persistent containers (ephemeral: false), reuses existing running container.
21
+ * For ephemeral containers, always creates a new container with AutoRemove.
22
+ *
23
+ * @param agentName - Name of the agent
24
+ * @param config - Docker configuration
25
+ * @param mounts - Volume mounts
26
+ * @param env - Environment variables
27
+ * @returns Docker container
28
+ */
29
+ getOrCreateContainer(agentName: string, config: DockerConfig, mounts: PathMapping[], env: string[]): Promise<Container>;
30
+ /**
31
+ * Create a new Docker container with security hardening
32
+ */
33
+ private createContainer;
34
+ /**
35
+ * Execute a command inside a container
36
+ *
37
+ * @param container - Docker container
38
+ * @param command - Command and arguments
39
+ * @param workDir - Working directory inside container
40
+ * @returns Exec instance for stream access
41
+ */
42
+ execInContainer(container: Container, command: string[], workDir?: string): Promise<Exec>;
43
+ /**
44
+ * Clean up old containers for an agent
45
+ *
46
+ * Removes oldest containers when count exceeds maxContainers.
47
+ *
48
+ * @param agentName - Name of the agent
49
+ * @param maxContainers - Maximum containers to keep
50
+ */
51
+ cleanupOldContainers(agentName: string, maxContainers: number): Promise<void>;
52
+ /**
53
+ * Stop and remove a specific container
54
+ */
55
+ stopContainer(container: Container): Promise<void>;
56
+ }
57
+ /**
58
+ * Build volume mounts for container execution
59
+ *
60
+ * Creates mounts for working directory, auth files, and Docker sessions.
61
+ *
62
+ * @param agent - Resolved agent configuration
63
+ * @param dockerConfig - Docker configuration
64
+ * @param stateDir - herdctl state directory (.herdctl/)
65
+ * @returns Array of path mappings
66
+ */
67
+ export declare function buildContainerMounts(agent: ResolvedAgent, dockerConfig: DockerConfig, stateDir: string): PathMapping[];
68
+ /**
69
+ * Build environment variables for container
70
+ *
71
+ * @param agent - Resolved agent configuration
72
+ * @param config - Docker configuration (for custom env vars)
73
+ * @returns Array of "KEY=value" strings
74
+ */
75
+ export declare function buildContainerEnv(agent: ResolvedAgent, config?: DockerConfig): string[];
76
+ //# sourceMappingURL=container-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-manager.d.ts","sourceRoot":"","sources":["../../../src/runner/runtime/container-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA0B,IAAI,EAAE,MAAM,WAAW,CAAC;AAIzE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,iBAAiB,CAAgC;gBAE7C,MAAM,CAAC,EAAE,OAAO,WAAW,CAAC;IAIxC;;;;;;;;;;;OAWG;IACG,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,WAAW,EAAE,EACrB,GAAG,EAAE,MAAM,EAAE,GACZ,OAAO,CAAC,SAAS,CAAC;IA+BrB;;OAEG;YACW,eAAe;IAoD7B;;;;;;;OAOG;IACG,eAAe,CACnB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,GAAE,MAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC;IAWhB;;;;;;;OAOG;IACG,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;IAwBhB;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,aAAa,EACpB,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,MAAM,GACf,WAAW,EAAE,CAiCf;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,aAAa,EACpB,MAAM,CAAC,EAAE,YAAY,GACpB,MAAM,EAAE,CA2BV"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Docker container lifecycle management
3
+ *
4
+ * Handles container creation, security configuration, and cleanup.
5
+ * Uses dockerode for Docker API communication.
6
+ */
7
+ import Dockerode from "dockerode";
8
+ import * as path from "node:path";
9
+ /**
10
+ * Container manager for herdctl Docker execution
11
+ */
12
+ export class ContainerManager {
13
+ docker;
14
+ runningContainers = new Map();
15
+ constructor(docker) {
16
+ this.docker = docker ?? new Dockerode();
17
+ }
18
+ /**
19
+ * Get or create a container for an agent
20
+ *
21
+ * For persistent containers (ephemeral: false), reuses existing running container.
22
+ * For ephemeral containers, always creates a new container with AutoRemove.
23
+ *
24
+ * @param agentName - Name of the agent
25
+ * @param config - Docker configuration
26
+ * @param mounts - Volume mounts
27
+ * @param env - Environment variables
28
+ * @returns Docker container
29
+ */
30
+ async getOrCreateContainer(agentName, config, mounts, env) {
31
+ // For persistent containers, check if already running
32
+ if (!config.ephemeral) {
33
+ const existing = this.runningContainers.get(agentName);
34
+ if (existing) {
35
+ try {
36
+ const info = await existing.inspect();
37
+ if (info.State.Running) {
38
+ return existing;
39
+ }
40
+ }
41
+ catch {
42
+ // Container no longer exists, remove from map
43
+ this.runningContainers.delete(agentName);
44
+ }
45
+ }
46
+ }
47
+ // Create new container
48
+ const container = await this.createContainer(agentName, config, mounts, env);
49
+ // Start the container
50
+ await container.start();
51
+ // Track persistent containers
52
+ if (!config.ephemeral) {
53
+ this.runningContainers.set(agentName, container);
54
+ }
55
+ return container;
56
+ }
57
+ /**
58
+ * Create a new Docker container with security hardening
59
+ */
60
+ async createContainer(agentName, config, mounts, env) {
61
+ const containerName = `herdctl-${agentName}-${Date.now()}`;
62
+ const createOptions = {
63
+ Image: config.image,
64
+ name: containerName,
65
+ Tty: false,
66
+ OpenStdin: true,
67
+ StdinOnce: false,
68
+ // Keep container running for exec commands
69
+ Cmd: ["sleep", "infinity"],
70
+ WorkingDir: "/workspace",
71
+ Env: env,
72
+ HostConfig: {
73
+ // Resource limits
74
+ Memory: config.memoryBytes,
75
+ MemorySwap: config.memoryBytes, // Same as Memory = no swap
76
+ CpuShares: config.cpuShares, // undefined = no limit (full CPU access)
77
+ // Network isolation
78
+ NetworkMode: config.network,
79
+ // Volume mounts
80
+ Binds: mounts.map((m) => `${m.hostPath}:${m.containerPath}:${m.mode}`),
81
+ // Security hardening
82
+ SecurityOpt: ["no-new-privileges:true"],
83
+ CapDrop: ["ALL"],
84
+ ReadonlyRootfs: false, // Claude needs to write temp files
85
+ // Cleanup
86
+ AutoRemove: config.ephemeral,
87
+ },
88
+ // Non-root user
89
+ User: config.user,
90
+ };
91
+ return this.docker.createContainer(createOptions);
92
+ }
93
+ /**
94
+ * Execute a command inside a container
95
+ *
96
+ * @param container - Docker container
97
+ * @param command - Command and arguments
98
+ * @param workDir - Working directory inside container
99
+ * @returns Exec instance for stream access
100
+ */
101
+ async execInContainer(container, command, workDir = "/workspace") {
102
+ return container.exec({
103
+ Cmd: command,
104
+ AttachStdout: true,
105
+ AttachStderr: true,
106
+ AttachStdin: false,
107
+ Tty: false,
108
+ WorkingDir: workDir,
109
+ });
110
+ }
111
+ /**
112
+ * Clean up old containers for an agent
113
+ *
114
+ * Removes oldest containers when count exceeds maxContainers.
115
+ *
116
+ * @param agentName - Name of the agent
117
+ * @param maxContainers - Maximum containers to keep
118
+ */
119
+ async cleanupOldContainers(agentName, maxContainers) {
120
+ const containers = await this.docker.listContainers({
121
+ all: true,
122
+ filters: {
123
+ name: [`herdctl-${agentName}-`],
124
+ },
125
+ });
126
+ // Sort by creation time, oldest first
127
+ const sorted = containers.sort((a, b) => a.Created - b.Created);
128
+ // Remove oldest until under limit
129
+ const toRemove = sorted.slice(0, Math.max(0, sorted.length - maxContainers));
130
+ for (const info of toRemove) {
131
+ const container = this.docker.getContainer(info.Id);
132
+ try {
133
+ await container.remove({ force: true });
134
+ }
135
+ catch {
136
+ // Ignore errors for already-removed containers
137
+ }
138
+ }
139
+ }
140
+ /**
141
+ * Stop and remove a specific container
142
+ */
143
+ async stopContainer(container) {
144
+ try {
145
+ await container.stop({ t: 5 }); // 5 second timeout
146
+ }
147
+ catch {
148
+ // Container may already be stopped
149
+ }
150
+ try {
151
+ const info = await container.inspect();
152
+ if (!info.HostConfig?.AutoRemove) {
153
+ await container.remove({ force: true });
154
+ }
155
+ }
156
+ catch {
157
+ // Container may already be removed
158
+ }
159
+ }
160
+ }
161
+ /**
162
+ * Build volume mounts for container execution
163
+ *
164
+ * Creates mounts for working directory, auth files, and Docker sessions.
165
+ *
166
+ * @param agent - Resolved agent configuration
167
+ * @param dockerConfig - Docker configuration
168
+ * @param stateDir - herdctl state directory (.herdctl/)
169
+ * @returns Array of path mappings
170
+ */
171
+ export function buildContainerMounts(agent, dockerConfig, stateDir) {
172
+ const mounts = [];
173
+ // Working directory mount
174
+ const working_directory = agent.working_directory;
175
+ if (working_directory) {
176
+ const working_directoryRoot = typeof working_directory === "string"
177
+ ? working_directory
178
+ : working_directory.root;
179
+ mounts.push({
180
+ hostPath: working_directoryRoot,
181
+ containerPath: "/workspace",
182
+ mode: dockerConfig.workspaceMode,
183
+ });
184
+ }
185
+ // Docker sessions directory (separate from host sessions)
186
+ // Claude CLI writes sessions to ~/.claude/projects/<encoded-workspace>/
187
+ // Inside container, working dir is /workspace → encoded as "-workspace"
188
+ // Mount docker-sessions to this location so we can watch files from host
189
+ // Note: Authentication uses ANTHROPIC_API_KEY env var, so no auth mount needed
190
+ const dockerSessionsDir = path.join(stateDir, "docker-sessions");
191
+ mounts.push({
192
+ hostPath: dockerSessionsDir,
193
+ containerPath: "/home/claude/.claude/projects/-workspace",
194
+ mode: "rw",
195
+ });
196
+ // Custom volumes from config
197
+ mounts.push(...dockerConfig.volumes);
198
+ return mounts;
199
+ }
200
+ /**
201
+ * Build environment variables for container
202
+ *
203
+ * @param agent - Resolved agent configuration
204
+ * @param config - Docker configuration (for custom env vars)
205
+ * @returns Array of "KEY=value" strings
206
+ */
207
+ export function buildContainerEnv(agent, config) {
208
+ const env = [];
209
+ // Pass through API key if available (preferred over mounted auth)
210
+ if (process.env.ANTHROPIC_API_KEY) {
211
+ env.push(`ANTHROPIC_API_KEY=${process.env.ANTHROPIC_API_KEY}`);
212
+ }
213
+ // Pass through OAuth token if available (for Claude Max web authentication)
214
+ if (process.env.CLAUDE_CODE_OAUTH_TOKEN) {
215
+ env.push(`CLAUDE_CODE_OAUTH_TOKEN=${process.env.CLAUDE_CODE_OAUTH_TOKEN}`);
216
+ }
217
+ // Add custom environment variables from docker config
218
+ if (config?.env) {
219
+ for (const [key, value] of Object.entries(config.env)) {
220
+ env.push(`${key}=${value}`);
221
+ }
222
+ }
223
+ // Terminal support
224
+ env.push("TERM=xterm-256color");
225
+ // HOME directory for claude user
226
+ env.push("HOME=/home/claude");
227
+ return env;
228
+ }
229
+ //# sourceMappingURL=container-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-manager.js","sourceRoot":"","sources":["../../../src/runner/runtime/container-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * ContainerRunner - Docker container decorator for RuntimeInterface
3
+ *
4
+ * Wraps any runtime (SDK or CLI) and transparently executes inside Docker containers.
5
+ * Handles path translation, mount configuration, and container lifecycle.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const baseRuntime = new CLIRuntime();
10
+ * const dockerRuntime = new ContainerRunner(baseRuntime, dockerConfig);
11
+ *
12
+ * // Execution happens inside Docker container
13
+ * for await (const message of dockerRuntime.execute(options)) {
14
+ * console.log(message);
15
+ * }
16
+ * ```
17
+ */
18
+ import type { RuntimeInterface, RuntimeExecuteOptions } from "./interface.js";
19
+ import type { SDKMessage } from "../types.js";
20
+ import type { DockerConfig } from "./docker-config.js";
21
+ /**
22
+ * Container runtime decorator
23
+ *
24
+ * Decorates any RuntimeInterface to execute inside Docker containers.
25
+ * The wrapped runtime's execute logic runs via `docker exec` inside the container.
26
+ */
27
+ export declare class ContainerRunner implements RuntimeInterface {
28
+ private wrapped;
29
+ private config;
30
+ private manager;
31
+ private stateDir;
32
+ /**
33
+ * Create a new ContainerRunner
34
+ *
35
+ * @param wrapped - The underlying runtime to execute inside containers
36
+ * @param config - Docker configuration
37
+ * @param stateDir - herdctl state directory (.herdctl/)
38
+ * @param docker - Optional Docker client for testing
39
+ */
40
+ constructor(wrapped: RuntimeInterface, config: DockerConfig, stateDir: string, docker?: import("dockerode"));
41
+ /**
42
+ * Execute agent inside Docker container
43
+ *
44
+ * Creates or reuses container, then executes based on runtime type:
45
+ * - CLI runtime: docker exec claude, watch session files on host
46
+ * - SDK runtime: docker exec wrapper script, stream JSONL from stdout
47
+ */
48
+ execute(options: RuntimeExecuteOptions): AsyncIterable<SDKMessage>;
49
+ /**
50
+ * Execute CLI runtime inside Docker container
51
+ *
52
+ * Spawns claude CLI via docker exec and watches session files on host.
53
+ */
54
+ private executeCLIRuntime;
55
+ /**
56
+ * Execute SDK runtime inside Docker container
57
+ *
58
+ * Runs docker-sdk-wrapper.js script which imports SDK and streams messages as JSONL.
59
+ */
60
+ private executeSDKRuntime;
61
+ }
62
+ //# sourceMappingURL=container-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-runner.d.ts","sourceRoot":"","sources":["../../../src/runner/runtime/container-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAWvD;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAapD,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;IAbhB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;;OAOG;gBAEO,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,YAAY,EAC5B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,OAAO,WAAW,CAAC;IAM9B;;;;;;OAMG;IACI,OAAO,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa,CAAC,UAAU,CAAC;IAqEzE;;;;OAIG;YACY,iBAAiB;IAkChC;;;;OAIG;YACY,iBAAiB;CAgGjC"}