@getforgeai/cli 0.1.0-beta.1

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 (221) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +80 -0
  3. package/dist/bin/forgeai.d.ts +3 -0
  4. package/dist/bin/forgeai.js +8 -0
  5. package/dist/cli.d.ts +4 -0
  6. package/dist/cli.js +180 -0
  7. package/dist/commands/auth-login.d.ts +4 -0
  8. package/dist/commands/auth-login.js +202 -0
  9. package/dist/commands/auth-logout.d.ts +2 -0
  10. package/dist/commands/auth-logout.js +41 -0
  11. package/dist/commands/auth-status.d.ts +4 -0
  12. package/dist/commands/auth-status.js +81 -0
  13. package/dist/commands/cli-list.d.ts +4 -0
  14. package/dist/commands/cli-list.js +40 -0
  15. package/dist/commands/cli-unlink.d.ts +4 -0
  16. package/dist/commands/cli-unlink.js +45 -0
  17. package/dist/commands/config-set.d.ts +5 -0
  18. package/dist/commands/config-set.js +63 -0
  19. package/dist/commands/connect.d.ts +9 -0
  20. package/dist/commands/connect.js +165 -0
  21. package/dist/commands/disconnect.d.ts +5 -0
  22. package/dist/commands/disconnect.js +126 -0
  23. package/dist/commands/journal-clear.d.ts +2 -0
  24. package/dist/commands/journal-clear.js +16 -0
  25. package/dist/commands/journal-list.d.ts +2 -0
  26. package/dist/commands/journal-list.js +20 -0
  27. package/dist/commands/journal-status.d.ts +2 -0
  28. package/dist/commands/journal-status.js +18 -0
  29. package/dist/commands/mcp-serve.d.ts +13 -0
  30. package/dist/commands/mcp-serve.js +74 -0
  31. package/dist/commands/org-list.d.ts +4 -0
  32. package/dist/commands/org-list.js +56 -0
  33. package/dist/commands/org-unlink.d.ts +4 -0
  34. package/dist/commands/org-unlink.js +63 -0
  35. package/dist/commands/reconnect.d.ts +4 -0
  36. package/dist/commands/reconnect.js +46 -0
  37. package/dist/commands/release-open.d.ts +2 -0
  38. package/dist/commands/release-open.js +43 -0
  39. package/dist/commands/session-respond.d.ts +2 -0
  40. package/dist/commands/session-respond.js +29 -0
  41. package/dist/commands/task-open.d.ts +2 -0
  42. package/dist/commands/task-open.js +42 -0
  43. package/dist/commands/vm.d.ts +21 -0
  44. package/dist/commands/vm.js +122 -0
  45. package/dist/commands/workflow-open.d.ts +2 -0
  46. package/dist/commands/workflow-open.js +43 -0
  47. package/dist/config/config-manager.d.ts +31 -0
  48. package/dist/config/config-manager.js +70 -0
  49. package/dist/config/token-manager.d.ts +11 -0
  50. package/dist/config/token-manager.js +87 -0
  51. package/dist/index.d.ts +11 -0
  52. package/dist/index.js +22 -0
  53. package/dist/lib/auth-client.d.ts +1670 -0
  54. package/dist/lib/auth-client.js +10 -0
  55. package/dist/lib/branch-manager.d.ts +79 -0
  56. package/dist/lib/branch-manager.js +620 -0
  57. package/dist/lib/clone-and-checkout.d.ts +6 -0
  58. package/dist/lib/clone-and-checkout.js +41 -0
  59. package/dist/lib/connection-manager.d.ts +58 -0
  60. package/dist/lib/connection-manager.js +436 -0
  61. package/dist/lib/daemon-entry.d.ts +2 -0
  62. package/dist/lib/daemon-entry.js +210 -0
  63. package/dist/lib/daemon.d.ts +38 -0
  64. package/dist/lib/daemon.js +148 -0
  65. package/dist/lib/event-journal.d.ts +24 -0
  66. package/dist/lib/event-journal.js +76 -0
  67. package/dist/lib/ipc-client.d.ts +10 -0
  68. package/dist/lib/ipc-client.js +70 -0
  69. package/dist/lib/ipc-server.d.ts +36 -0
  70. package/dist/lib/ipc-server.js +70 -0
  71. package/dist/lib/node-version-guard.d.ts +8 -0
  72. package/dist/lib/node-version-guard.js +60 -0
  73. package/dist/lib/open-directory.d.ts +7 -0
  74. package/dist/lib/open-directory.js +41 -0
  75. package/dist/lib/repo-manager.d.ts +29 -0
  76. package/dist/lib/repo-manager.js +103 -0
  77. package/dist/lib/resilient-emitter.d.ts +53 -0
  78. package/dist/lib/resilient-emitter.js +160 -0
  79. package/dist/lib/rest-client.d.ts +164 -0
  80. package/dist/lib/rest-client.js +139 -0
  81. package/dist/lib/skill-manager.d.ts +23 -0
  82. package/dist/lib/skill-manager.js +167 -0
  83. package/dist/lib/socket-client.d.ts +68 -0
  84. package/dist/lib/socket-client.js +160 -0
  85. package/dist/mcp/mcp-probe.d.ts +11 -0
  86. package/dist/mcp/mcp-probe.js +106 -0
  87. package/dist/mcp/mcp-response.d.ts +29 -0
  88. package/dist/mcp/mcp-response.js +67 -0
  89. package/dist/mcp/mcp-server.d.ts +19 -0
  90. package/dist/mcp/mcp-server.js +76 -0
  91. package/dist/mcp/resources/project-conventions.d.ts +4 -0
  92. package/dist/mcp/resources/project-conventions.js +50 -0
  93. package/dist/mcp/resources/task-current.d.ts +4 -0
  94. package/dist/mcp/resources/task-current.js +51 -0
  95. package/dist/mcp/tools/forge-add-group-dependency.d.ts +4 -0
  96. package/dist/mcp/tools/forge-add-group-dependency.js +20 -0
  97. package/dist/mcp/tools/forge-add-task-dependency.d.ts +4 -0
  98. package/dist/mcp/tools/forge-add-task-dependency.js +20 -0
  99. package/dist/mcp/tools/forge-ask-human.d.ts +4 -0
  100. package/dist/mcp/tools/forge-ask-human.js +80 -0
  101. package/dist/mcp/tools/forge-column-done.d.ts +4 -0
  102. package/dist/mcp/tools/forge-column-done.js +46 -0
  103. package/dist/mcp/tools/forge-complete-step.d.ts +4 -0
  104. package/dist/mcp/tools/forge-complete-step.js +139 -0
  105. package/dist/mcp/tools/forge-create-task-group.d.ts +4 -0
  106. package/dist/mcp/tools/forge-create-task-group.js +22 -0
  107. package/dist/mcp/tools/forge-create-task.d.ts +4 -0
  108. package/dist/mcp/tools/forge-create-task.js +28 -0
  109. package/dist/mcp/tools/forge-delete-task-group.d.ts +4 -0
  110. package/dist/mcp/tools/forge-delete-task-group.js +17 -0
  111. package/dist/mcp/tools/forge-delete-task.d.ts +4 -0
  112. package/dist/mcp/tools/forge-delete-task.js +17 -0
  113. package/dist/mcp/tools/forge-get-task.d.ts +4 -0
  114. package/dist/mcp/tools/forge-get-task.js +17 -0
  115. package/dist/mcp/tools/forge-merge-back.d.ts +4 -0
  116. package/dist/mcp/tools/forge-merge-back.js +179 -0
  117. package/dist/mcp/tools/forge-release-merge.d.ts +4 -0
  118. package/dist/mcp/tools/forge-release-merge.js +159 -0
  119. package/dist/mcp/tools/forge-release-notes.d.ts +4 -0
  120. package/dist/mcp/tools/forge-release-notes.js +51 -0
  121. package/dist/mcp/tools/forge-release-tag.d.ts +4 -0
  122. package/dist/mcp/tools/forge-release-tag.js +125 -0
  123. package/dist/mcp/tools/forge-remove-group-dependency.d.ts +4 -0
  124. package/dist/mcp/tools/forge-remove-group-dependency.js +19 -0
  125. package/dist/mcp/tools/forge-remove-task-dependency.d.ts +4 -0
  126. package/dist/mcp/tools/forge-remove-task-dependency.js +19 -0
  127. package/dist/mcp/tools/forge-requeue.d.ts +4 -0
  128. package/dist/mcp/tools/forge-requeue.js +39 -0
  129. package/dist/mcp/tools/forge-submit-pr.d.ts +4 -0
  130. package/dist/mcp/tools/forge-submit-pr.js +68 -0
  131. package/dist/mcp/tools/forge-update-status.d.ts +4 -0
  132. package/dist/mcp/tools/forge-update-status.js +77 -0
  133. package/dist/mcp/tools/forge-update-task-group.d.ts +4 -0
  134. package/dist/mcp/tools/forge-update-task-group.js +21 -0
  135. package/dist/mcp/tools/forge-update-task.d.ts +4 -0
  136. package/dist/mcp/tools/forge-update-task.js +25 -0
  137. package/dist/mcp/tools/forge-validate-merge.d.ts +4 -0
  138. package/dist/mcp/tools/forge-validate-merge.js +194 -0
  139. package/dist/mcp/types.d.ts +39 -0
  140. package/dist/mcp/types.js +8 -0
  141. package/dist/methodology/artifact-manager.d.ts +14 -0
  142. package/dist/methodology/artifact-manager.js +55 -0
  143. package/dist/orchestrator/agent-registry.d.ts +53 -0
  144. package/dist/orchestrator/agent-registry.js +96 -0
  145. package/dist/orchestrator/agent-spawner.d.ts +112 -0
  146. package/dist/orchestrator/agent-spawner.js +1518 -0
  147. package/dist/orchestrator/connectors/claude-code-connector.d.ts +3 -0
  148. package/dist/orchestrator/connectors/claude-code-connector.js +60 -0
  149. package/dist/orchestrator/connectors/connector.d.ts +95 -0
  150. package/dist/orchestrator/connectors/connector.js +25 -0
  151. package/dist/orchestrator/connectors/docker-connector.d.ts +14 -0
  152. package/dist/orchestrator/connectors/docker-connector.js +337 -0
  153. package/dist/orchestrator/connectors/native-vm-connector.d.ts +11 -0
  154. package/dist/orchestrator/connectors/native-vm-connector.js +162 -0
  155. package/dist/orchestrator/connectors/opencode-connector.d.ts +3 -0
  156. package/dist/orchestrator/connectors/opencode-connector.js +39 -0
  157. package/dist/orchestrator/connectors/vm-connector.d.ts +3 -0
  158. package/dist/orchestrator/connectors/vm-connector.js +139 -0
  159. package/dist/orchestrator/dispatch-queue.d.ts +43 -0
  160. package/dist/orchestrator/dispatch-queue.js +93 -0
  161. package/dist/orchestrator/resume-handler.d.ts +34 -0
  162. package/dist/orchestrator/resume-handler.js +159 -0
  163. package/dist/orchestrator/stream-batcher.d.ts +16 -0
  164. package/dist/orchestrator/stream-batcher.js +50 -0
  165. package/dist/orchestrator/stream-json-extractor.d.ts +97 -0
  166. package/dist/orchestrator/stream-json-extractor.js +579 -0
  167. package/dist/vm/claude-token-manager.d.ts +36 -0
  168. package/dist/vm/claude-token-manager.js +223 -0
  169. package/dist/vm/docker-executor.d.ts +131 -0
  170. package/dist/vm/docker-executor.js +360 -0
  171. package/dist/vm/docker-process-proxy.d.ts +31 -0
  172. package/dist/vm/docker-process-proxy.js +105 -0
  173. package/dist/vm/forge-vm-agent.d.ts +11 -0
  174. package/dist/vm/forge-vm-agent.js +159 -0
  175. package/dist/vm/git-credentials.d.ts +43 -0
  176. package/dist/vm/git-credentials.js +53 -0
  177. package/dist/vm/image-manager.d.ts +7 -0
  178. package/dist/vm/image-manager.js +61 -0
  179. package/dist/vm/log.d.ts +9 -0
  180. package/dist/vm/log.js +18 -0
  181. package/dist/vm/mcp-bridge.d.ts +63 -0
  182. package/dist/vm/mcp-bridge.js +243 -0
  183. package/dist/vm/mcp-stub.d.ts +15 -0
  184. package/dist/vm/mcp-stub.js +67 -0
  185. package/dist/vm/native-addon.d.ts +69 -0
  186. package/dist/vm/native-addon.js +68 -0
  187. package/dist/vm/provider-resolver.d.ts +7 -0
  188. package/dist/vm/provider-resolver.js +27 -0
  189. package/dist/vm/providers/lima.d.ts +17 -0
  190. package/dist/vm/providers/lima.js +191 -0
  191. package/dist/vm/providers/qemu.d.ts +28 -0
  192. package/dist/vm/providers/qemu.js +260 -0
  193. package/dist/vm/providers/vfkit.d.ts +40 -0
  194. package/dist/vm/providers/vfkit.js +371 -0
  195. package/dist/vm/providers/wsl2.d.ts +27 -0
  196. package/dist/vm/providers/wsl2.js +226 -0
  197. package/dist/vm/session-manager.d.ts +130 -0
  198. package/dist/vm/session-manager.js +494 -0
  199. package/dist/vm/session-snapshot.d.ts +31 -0
  200. package/dist/vm/session-snapshot.js +180 -0
  201. package/dist/vm/types.d.ts +73 -0
  202. package/dist/vm/types.js +2 -0
  203. package/dist/vm/vm-helper-client.d.ts +81 -0
  204. package/dist/vm/vm-helper-client.js +335 -0
  205. package/dist/vm/vm-helper.d.ts +11 -0
  206. package/dist/vm/vm-helper.js +156 -0
  207. package/dist/vm/vm-ipc-protocol.d.ts +84 -0
  208. package/dist/vm/vm-ipc-protocol.js +9 -0
  209. package/dist/vm/vm-manager-facade.d.ts +23 -0
  210. package/dist/vm/vm-manager-facade.js +43 -0
  211. package/dist/vm/vm-manager.d.ts +74 -0
  212. package/dist/vm/vm-manager.js +515 -0
  213. package/dist/vm/vm-process-proxy.d.ts +34 -0
  214. package/dist/vm/vm-process-proxy.js +64 -0
  215. package/dist/vm/vsock-bridge.d.ts +44 -0
  216. package/dist/vm/vsock-bridge.js +140 -0
  217. package/dist/vm/vsock-protocol.d.ts +98 -0
  218. package/dist/vm/vsock-protocol.js +57 -0
  219. package/package.json +65 -0
  220. package/rootfs/Dockerfile +50 -0
  221. package/rootfs/forge-mcp-relay.mjs +158 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Claude OAuth Token Manager — secure storage + interactive setup.
3
+ *
4
+ * Uses `claude setup-token` to obtain a long-lived OAuth token (1 year)
5
+ * for programmatic use in Docker containers.
6
+ *
7
+ * Storage:
8
+ * - macOS: Keychain via `security` command (service: "ForgeAI-claude-oauth")
9
+ * - Linux/Windows: encrypted file at ~/.forgeai/auth/claude-oauth.json
10
+ *
11
+ * Flow:
12
+ * 1. ensureClaudeToken() checks for stored token
13
+ * 2. If absent, runs `claude setup-token` interactively
14
+ * 3. Captures the token from stdout
15
+ * 4. Saves to Keychain (macOS) or encrypted file (other platforms)
16
+ * 5. Returns the token for injection into containers
17
+ */
18
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
19
+ import { homedir, hostname, userInfo } from "node:os";
20
+ import { join } from "node:path";
21
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync, } from "node:crypto";
22
+ import { execFile, spawn } from "node:child_process";
23
+ import { promisify } from "node:util";
24
+ import { vmLog } from "./log.js";
25
+ const execFileAsync = promisify(execFile);
26
+ const KEYCHAIN_SERVICE = "ForgeAI-claude-oauth";
27
+ const KEYCHAIN_ACCOUNT = "claude-oauth-token";
28
+ const FILE_TOKEN_PATH = join(homedir(), ".forgeai", "auth", "claude-oauth.json");
29
+ const ALGORITHM = "aes-256-gcm";
30
+ // ---------------------------------------------------------------------------
31
+ // Platform-specific storage
32
+ // ---------------------------------------------------------------------------
33
+ /** Save token to macOS Keychain. */
34
+ async function saveToKeychain(token) {
35
+ // Delete existing entry first (ignore errors if not found)
36
+ try {
37
+ await execFileAsync("security", [
38
+ "delete-generic-password",
39
+ "-s",
40
+ KEYCHAIN_SERVICE,
41
+ "-a",
42
+ KEYCHAIN_ACCOUNT,
43
+ ], { timeout: 5_000 });
44
+ }
45
+ catch {
46
+ /* not found — OK */
47
+ }
48
+ await execFileAsync("security", [
49
+ "add-generic-password",
50
+ "-s",
51
+ KEYCHAIN_SERVICE,
52
+ "-a",
53
+ KEYCHAIN_ACCOUNT,
54
+ "-w",
55
+ token,
56
+ "-U", // update if exists
57
+ ], { timeout: 5_000 });
58
+ }
59
+ /** Load token from macOS Keychain. Returns null if not found. */
60
+ async function loadFromKeychain() {
61
+ try {
62
+ const { stdout } = await execFileAsync("security", [
63
+ "find-generic-password",
64
+ "-s",
65
+ KEYCHAIN_SERVICE,
66
+ "-a",
67
+ KEYCHAIN_ACCOUNT,
68
+ "-w",
69
+ ], { timeout: 5_000 });
70
+ const token = stdout.trim();
71
+ return token || null;
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // File-based fallback (Linux/Windows) — encrypted with machine identity
79
+ // ---------------------------------------------------------------------------
80
+ function deriveKey() {
81
+ const machineId = `${hostname()}:${userInfo().username}`;
82
+ return scryptSync(machineId, "forgeai-claude-oauth-salt", 32);
83
+ }
84
+ function saveToFile(token) {
85
+ const key = deriveKey();
86
+ const iv = randomBytes(16);
87
+ const cipher = createCipheriv(ALGORITHM, key, iv);
88
+ let encrypted = cipher.update(token, "utf-8", "hex");
89
+ encrypted += cipher.final("hex");
90
+ const authTag = cipher.getAuthTag().toString("hex");
91
+ const dir = join(homedir(), ".forgeai", "auth");
92
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
93
+ writeFileSync(FILE_TOKEN_PATH, JSON.stringify({ iv: iv.toString("hex"), authTag, encrypted }), { mode: 0o600 });
94
+ }
95
+ function loadFromFile() {
96
+ if (!existsSync(FILE_TOKEN_PATH))
97
+ return null;
98
+ try {
99
+ const data = JSON.parse(readFileSync(FILE_TOKEN_PATH, "utf-8"));
100
+ const key = deriveKey();
101
+ const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(data.iv, "hex"));
102
+ decipher.setAuthTag(Buffer.from(data.authTag, "hex"));
103
+ let decrypted = decipher.update(data.encrypted, "hex", "utf-8");
104
+ decrypted += decipher.final("utf-8");
105
+ return decrypted;
106
+ }
107
+ catch {
108
+ return null;
109
+ }
110
+ }
111
+ // ---------------------------------------------------------------------------
112
+ // Public API
113
+ // ---------------------------------------------------------------------------
114
+ /** Store the Claude OAuth token securely. Keychain on macOS, encrypted file elsewhere. */
115
+ export async function saveClaudeToken(token) {
116
+ if (process.platform === "darwin") {
117
+ await saveToKeychain(token);
118
+ vmLog.info("claude-token", "Token saved to macOS Keychain");
119
+ }
120
+ else {
121
+ saveToFile(token);
122
+ vmLog.info("claude-token", "Token saved to encrypted file");
123
+ }
124
+ }
125
+ /** Load the Claude OAuth token. Returns null if not found or invalid. */
126
+ export async function loadClaudeToken() {
127
+ if (process.platform === "darwin") {
128
+ return loadFromKeychain();
129
+ }
130
+ return loadFromFile();
131
+ }
132
+ /** Check if a Claude OAuth token is stored. */
133
+ export async function hasClaudeToken() {
134
+ return (await loadClaudeToken()) !== null;
135
+ }
136
+ // ---------------------------------------------------------------------------
137
+ // Interactive setup
138
+ // ---------------------------------------------------------------------------
139
+ /**
140
+ * Run `claude setup-token` interactively to obtain a long-lived OAuth token.
141
+ * The user must be logged into Claude Code on this machine.
142
+ * Returns the token string, or throws if the process fails.
143
+ */
144
+ export async function runClaudeSetupToken() {
145
+ vmLog.cyan("claude-token", "Running 'claude setup-token' to obtain OAuth token...");
146
+ const { tmpdir } = await import("node:os");
147
+ const captureFile = join(tmpdir(), `forgeai-setup-token-${Date.now()}.txt`);
148
+ return new Promise((resolve, reject) => {
149
+ // Use `script` to run in a real PTY (full interactive UI) while capturing output.
150
+ // macOS: script -q <file> <command> [args...]
151
+ // This preserves TTY so claude setup-token shows colors, spinners, and input prompts.
152
+ const proc = spawn("script", ["-q", captureFile, "claude", "setup-token"], {
153
+ stdio: "inherit",
154
+ });
155
+ proc.on("close", async (code) => {
156
+ // Read captured output and extract token
157
+ let captured = "";
158
+ try {
159
+ captured = readFileSync(captureFile, "utf-8");
160
+ }
161
+ catch {
162
+ // File not created
163
+ }
164
+ // Clean up temp file
165
+ try {
166
+ const { unlinkSync } = await import("node:fs");
167
+ unlinkSync(captureFile);
168
+ }
169
+ catch {
170
+ /* best effort */
171
+ }
172
+ if (code !== 0) {
173
+ reject(new Error(`claude setup-token exited with code ${code}`));
174
+ return;
175
+ }
176
+ // Extract token from captured output (format: sk-ant-oat01-...)
177
+ const tokenMatch = captured.match(/(sk-ant-oat01-[A-Za-z0-9_-]+)/);
178
+ if (tokenMatch) {
179
+ const accessToken = tokenMatch[1];
180
+ await saveClaudeToken(accessToken);
181
+ console.log("\n✓ Token stored securely in macOS Keychain (ForgeAI-claude-oauth). No need to copy it.");
182
+ resolve(accessToken);
183
+ return;
184
+ }
185
+ // Fallback: ask user to paste
186
+ vmLog.warn("claude-token", "Could not extract token from output, asking user...");
187
+ const { createInterface } = await import("node:readline");
188
+ const rl = createInterface({
189
+ input: process.stdin,
190
+ output: process.stdout,
191
+ });
192
+ const token = await new Promise((res) => {
193
+ rl.question("Paste the token shown above (sk-ant-oat01-...): ", (answer) => {
194
+ rl.close();
195
+ res(answer.trim());
196
+ });
197
+ });
198
+ if (token.startsWith("sk-ant-oat01-")) {
199
+ await saveClaudeToken(token);
200
+ console.log("✓ Token stored securely in macOS Keychain (ForgeAI-claude-oauth).");
201
+ resolve(token);
202
+ }
203
+ else {
204
+ reject(new Error("Invalid token format. Run: forge auth setup-claude"));
205
+ }
206
+ });
207
+ proc.on("error", (err) => {
208
+ reject(new Error(`Failed to run claude setup-token: ${err.message}`));
209
+ });
210
+ });
211
+ }
212
+ /**
213
+ * Ensure a Claude OAuth token is available.
214
+ * If not stored, runs `claude setup-token` interactively.
215
+ * Returns the token.
216
+ */
217
+ export async function ensureClaudeToken() {
218
+ const existing = await loadClaudeToken();
219
+ if (existing)
220
+ return existing;
221
+ return runClaudeSetupToken();
222
+ }
223
+ //# sourceMappingURL=claude-token-manager.js.map
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Low-level Docker CLI wrapper using child_process.
3
+ * Zero npm dependencies — shells out to `docker` commands directly.
4
+ *
5
+ * All containers get labels:
6
+ * - forgeai.managed=true
7
+ * - forgeai.session-id=<sessionId>
8
+ * - forgeai.task-id=<taskId>
9
+ *
10
+ * On Linux, adds `--add-host host.docker.internal:host-gateway` to enable
11
+ * communication from container to host. macOS/Windows Docker Desktop handles
12
+ * this automatically.
13
+ */
14
+ import { ChildProcess } from "node:child_process";
15
+ /**
16
+ * Resolve the gateway IP of the default Docker bridge network (Linux).
17
+ * This is the host-side address containers reach via
18
+ * `--add-host host.docker.internal:host-gateway`. Cached after first lookup.
19
+ * Falls back to the Docker default 172.17.0.1 if inspection fails.
20
+ */
21
+ export declare function getDockerBridgeGatewayIp(): Promise<string>;
22
+ /**
23
+ * Check if Docker is available on the system.
24
+ * Runs `docker info --format '{{.ID}}'` with 5s timeout.
25
+ * Returns true if exit code 0, false otherwise.
26
+ */
27
+ export declare function isDockerAvailable(): Promise<boolean>;
28
+ /**
29
+ * Pull a Docker image.
30
+ * Runs `docker pull <image>` and logs progress.
31
+ */
32
+ export declare function pullImage(image: string): Promise<void>;
33
+ /**
34
+ * Check if a Docker image exists locally.
35
+ * Runs `docker image inspect <image>`.
36
+ * Returns true if exit code 0, false otherwise.
37
+ */
38
+ export declare function imageExists(image: string): Promise<boolean>;
39
+ export type ContainerCreateOpts = {
40
+ name: string;
41
+ image: string;
42
+ volumes: Array<{
43
+ hostPath: string;
44
+ containerPath: string;
45
+ readOnly?: boolean;
46
+ }>;
47
+ env?: Record<string, string>;
48
+ labels?: Record<string, string>;
49
+ sessionId: string;
50
+ taskId: string;
51
+ };
52
+ /**
53
+ * Create a Docker container.
54
+ *
55
+ * Runs:
56
+ * docker create --name <name> --platform linux/arm64 \
57
+ * -v <mounts...> \
58
+ * -e <envs...> \
59
+ * --label forgeai.managed=true \
60
+ * --label forgeai.session-id=<sessionId> \
61
+ * --label forgeai.task-id=<taskId> \
62
+ * [--label <custom labels...>] \
63
+ * [--add-host host.docker.internal:host-gateway (Linux only)] \
64
+ * <image> sleep infinity
65
+ *
66
+ * Returns the container ID from stdout.
67
+ */
68
+ export declare function createContainer(opts: ContainerCreateOpts): Promise<string>;
69
+ /**
70
+ * Start a Docker container.
71
+ * Runs `docker start <containerId>`.
72
+ */
73
+ export declare function startContainer(containerId: string): Promise<void>;
74
+ export type ExecOpts = {
75
+ cwd?: string;
76
+ env?: Record<string, string>;
77
+ secretEnv?: Record<string, string>;
78
+ timeout?: number;
79
+ };
80
+ /**
81
+ * Execute a command inside a running container.
82
+ *
83
+ * Runs:
84
+ * docker exec [-w <cwd>] [-e KEY=VAL...] [-e SECRET...] <containerId> <cmd...>
85
+ *
86
+ * Returns { stdout, stderr, exitCode }.
87
+ * Does NOT throw on non-zero exit — caller can check exitCode.
88
+ */
89
+ export declare function execInContainer(containerId: string, cmd: string[], opts?: ExecOpts): Promise<{
90
+ stdout: string;
91
+ stderr: string;
92
+ exitCode: number;
93
+ }>;
94
+ /**
95
+ * Spawn a long-running command inside a container with stdin attached.
96
+ *
97
+ * Runs:
98
+ * docker exec -i [-w <cwd>] [-e KEY=VAL...] <containerId> <cmd...>
99
+ *
100
+ * The `-i` flag keeps stdin open. Returns the raw ChildProcess.
101
+ */
102
+ export declare function spawnInContainer(containerId: string, cmd: string[], opts?: {
103
+ cwd?: string;
104
+ env?: Record<string, string>;
105
+ secretEnv?: Record<string, string>;
106
+ user?: string;
107
+ }): ChildProcess;
108
+ /**
109
+ * Kill a container.
110
+ * Runs `docker kill [--signal <signal>] <containerId>`.
111
+ */
112
+ export declare function killContainer(containerId: string, signal?: string): Promise<void>;
113
+ /**
114
+ * Remove a container.
115
+ * Runs `docker rm [-f] <containerId>`.
116
+ * Ignores errors (container may already be gone).
117
+ */
118
+ export declare function removeContainer(containerId: string, force?: boolean): Promise<void>;
119
+ export type ContainerListItem = {
120
+ id: string;
121
+ name: string;
122
+ status: string;
123
+ labels: Record<string, string>;
124
+ };
125
+ /**
126
+ * List all ForgeAI-managed containers.
127
+ * Runs `docker ps -a --filter label=forgeai.managed=true --format '{{json .}}'`.
128
+ * Parses JSON output and returns array of containers with extracted labels.
129
+ */
130
+ export declare function listForgeContainers(): Promise<ContainerListItem[]>;
131
+ //# sourceMappingURL=docker-executor.d.ts.map