@midhunxavier/code-flow 0.0.0-stage → 0.1.0-preview.3

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 (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -2
  3. package/bin/codeflow.mjs +8 -0
  4. package/bin/launcher.mjs +127 -0
  5. package/dist/remote-host.json +1 -0
  6. package/licenses/inter.txt +92 -0
  7. package/licenses/preact.txt +21 -0
  8. package/licenses/signals-core.txt +21 -0
  9. package/licenses/signals.txt +21 -0
  10. package/licenses/typescript-notices.txt +193 -0
  11. package/licenses/typescript.txt +55 -0
  12. package/licenses/uqr.txt +22 -0
  13. package/licenses/xterm-fit.txt +19 -0
  14. package/licenses/xterm.txt +21 -0
  15. package/package.json +34 -4
  16. package/public/assets/changes-62ZIEQSN.js +1 -0
  17. package/public/assets/chunk-2PZVOKID.js +1 -0
  18. package/public/assets/chunk-4D5IENEB.js +1 -0
  19. package/public/assets/chunk-5SARP5MU.js +1 -0
  20. package/public/assets/chunk-63YLZ3PR.js +1 -0
  21. package/public/assets/chunk-7FUHKF2M.js +1 -0
  22. package/public/assets/chunk-APQEVJ2K.js +1 -0
  23. package/public/assets/chunk-CGOZQMEZ.js +2 -0
  24. package/public/assets/chunk-J4VYS3KV.js +2 -0
  25. package/public/assets/chunk-L7TUCIYF.js +1 -0
  26. package/public/assets/chunk-SIHDWM2Y.js +154 -0
  27. package/public/assets/chunk-TEJVYIKC.js +1 -0
  28. package/public/assets/code-browser-VB2HGZSG.js +4 -0
  29. package/public/assets/filtered-tasks-ES5CP4KV.js +1 -0
  30. package/public/assets/interactive-browser-7F5ZXRUV.js +7 -0
  31. package/public/assets/join-project-6RTFHNCA.js +1 -0
  32. package/public/assets/main-65EW73SB.js +1 -0
  33. package/public/assets/project-invitations-view-GMPWGWYM.js +1 -0
  34. package/public/assets/settings-UB5Q4AQM.js +9 -0
  35. package/public/assets/styles-18792BB4.css +32 -0
  36. package/public/assets/team-server-wizard-2GLDWS6D.js +8 -0
  37. package/public/assets/terminal-IWAIM4DE.js +62 -0
  38. package/public/assets.json +1 -0
  39. package/public/bundle-meta.json +1 -0
  40. package/public/docs/.nojekyll +0 -0
  41. package/public/docs/accessibility.html +19 -0
  42. package/public/docs/acp-agents.html +117 -0
  43. package/public/docs/agent-containers.html +79 -0
  44. package/public/docs/api.html +1130 -0
  45. package/public/docs/app.js +342 -0
  46. package/public/docs/application-improvements.html +275 -0
  47. package/public/docs/architecture-explorer.html +19 -0
  48. package/public/docs/architecture.html +149 -0
  49. package/public/docs/auto-team.html +148 -0
  50. package/public/docs/automations.html +109 -0
  51. package/public/docs/checkpoint-review.html +37 -0
  52. package/public/docs/code-hosts.html +35 -0
  53. package/public/docs/code-intelligence.html +35 -0
  54. package/public/docs/configuration.html +346 -0
  55. package/public/docs/context-inspection.html +23 -0
  56. package/public/docs/coordinated-changes.html +28 -0
  57. package/public/docs/deferred-runs.html +25 -0
  58. package/public/docs/dependency-discovery.html +19 -0
  59. package/public/docs/documentation.html +84 -0
  60. package/public/docs/dynamic-teams-evaluation.html +174 -0
  61. package/public/docs/dynamic-teams-implementation-plan.html +882 -0
  62. package/public/docs/dynamic-teams-implementation-review-1.html +141 -0
  63. package/public/docs/dynamic-teams-plan-review-0.html +204 -0
  64. package/public/docs/extensions.html +42 -0
  65. package/public/docs/external-sources.html +96 -0
  66. package/public/docs/filesystem-policy.html +26 -0
  67. package/public/docs/filtered-tasks.html +44 -0
  68. package/public/docs/font-license.txt +92 -0
  69. package/public/docs/getting-started.html +143 -0
  70. package/public/docs/github-review-threads.html +26 -0
  71. package/public/docs/github.html +157 -0
  72. package/public/docs/help.html +69 -0
  73. package/public/docs/index.html +88 -0
  74. package/public/docs/installation.html +80 -0
  75. package/public/docs/launch-profiles.html +48 -0
  76. package/public/docs/maintenance.html +60 -0
  77. package/public/docs/memory-claims.html +31 -0
  78. package/public/docs/memory-storage.html +58 -0
  79. package/public/docs/notifications.html +39 -0
  80. package/public/docs/patterns.html +195 -0
  81. package/public/docs/performance-results.html +210 -0
  82. package/public/docs/performance.html +166 -0
  83. package/public/docs/project-conventions.html +22 -0
  84. package/public/docs/project-first-navigation-plan.html +587 -0
  85. package/public/docs/project-teams-implementation-plan.html +772 -0
  86. package/public/docs/project-teams.html +129 -0
  87. package/public/docs/provider-compatibility.html +20 -0
  88. package/public/docs/releasing.html +65 -0
  89. package/public/docs/remote-workers.html +64 -0
  90. package/public/docs/review-inbox.html +26 -0
  91. package/public/docs/run-storage.html +23 -0
  92. package/public/docs/runs.html +141 -0
  93. package/public/docs/search.json +1 -0
  94. package/public/docs/security.html +133 -0
  95. package/public/docs/shared-projects.html +31 -0
  96. package/public/docs/ssh-projects.html +60 -0
  97. package/public/docs/style.css +768 -0
  98. package/public/docs/task-reruns.html +30 -0
  99. package/public/docs/team-collaboration.html +412 -0
  100. package/public/docs/verification.html +22 -0
  101. package/public/docs/vscode.html +53 -0
  102. package/public/docs/worker-permission-verification.html +131 -0
  103. package/public/index.html +17 -0
  104. package/scripts/check-node.mjs +7 -0
  105. package/scripts/codeflow.mjs +55 -0
  106. package/scripts/computer-use.swift +64 -0
  107. package/scripts/pty.py +273 -0
  108. package/src/acp-connections.js +110 -0
  109. package/src/adapters/acp-presets.js +37 -0
  110. package/src/adapters/acp-types.js +0 -0
  111. package/src/adapters/acp.js +328 -0
  112. package/src/adapters/agent-tools.js +28 -0
  113. package/src/adapters/agy-auth.js +67 -0
  114. package/src/adapters/agy-isolation.js +242 -0
  115. package/src/adapters/agy-tools.js +45 -0
  116. package/src/adapters/agy.js +326 -0
  117. package/src/adapters/claude-stream.js +58 -0
  118. package/src/adapters/claude.js +282 -0
  119. package/src/adapters/codex-app-server.js +555 -0
  120. package/src/adapters/codex.js +311 -0
  121. package/src/adapters/errors.js +57 -0
  122. package/src/adapters/index.js +91 -0
  123. package/src/adapters/managed-permissions.js +37 -0
  124. package/src/adapters/model-discovery.js +100 -0
  125. package/src/adapters/process.js +433 -0
  126. package/src/adapters/team-tools.js +103 -0
  127. package/src/adapters/types.js +0 -0
  128. package/src/agent-browser.js +688 -0
  129. package/src/agent-computer.js +88 -0
  130. package/src/agent-container.js +155 -0
  131. package/src/agent-team-definitions.js +131 -0
  132. package/src/agent-tool-client.mjs +79 -0
  133. package/src/agent-tools.js +296 -0
  134. package/src/approvals.js +134 -0
  135. package/src/architecture.js +456 -0
  136. package/src/assets.js +44 -0
  137. package/src/automation.js +901 -0
  138. package/src/browser-files.js +293 -0
  139. package/src/browser-ownership.js +150 -0
  140. package/src/certificate-renewal.js +75 -0
  141. package/src/chat-family.js +25 -0
  142. package/src/chat-handoff.js +87 -0
  143. package/src/checkpoint-review.js +72 -0
  144. package/src/checks.js +303 -0
  145. package/src/code-host-git.js +102 -0
  146. package/src/code-hosts-api.js +99 -0
  147. package/src/code-hosts.js +482 -0
  148. package/src/code-intelligence-worker.mjs +452 -0
  149. package/src/code-intelligence.js +141 -0
  150. package/src/collab/api.js +365 -0
  151. package/src/collab/checkpoints.js +198 -0
  152. package/src/collab/connections.js +139 -0
  153. package/src/collab/host-execution.js +347 -0
  154. package/src/collab/hub-client.js +210 -0
  155. package/src/collab/link-api.js +229 -0
  156. package/src/collab/link.js +595 -0
  157. package/src/collab/model.js +496 -0
  158. package/src/collab/runner.js +1014 -0
  159. package/src/collab/store.js +1452 -0
  160. package/src/collab/terminals.js +361 -0
  161. package/src/collaboration-reviews.js +116 -0
  162. package/src/config.js +48 -0
  163. package/src/context-budget.js +14 -0
  164. package/src/context-inspection.js +204 -0
  165. package/src/context.js +154 -0
  166. package/src/conventions.js +283 -0
  167. package/src/coordinated-api.js +247 -0
  168. package/src/deferred-launch.js +82 -0
  169. package/src/deferred-runs.js +515 -0
  170. package/src/dependency-health.js +35 -0
  171. package/src/diff.js +43 -0
  172. package/src/docs-help.js +316 -0
  173. package/src/doctor.js +232 -0
  174. package/src/documentation-changes.js +20 -0
  175. package/src/engine.js +5258 -0
  176. package/src/env.js +44 -0
  177. package/src/execution-budget.js +8 -0
  178. package/src/experience.js +50 -0
  179. package/src/extensions.js +144 -0
  180. package/src/filesystem-access.js +107 -0
  181. package/src/filesystem-policy.js +238 -0
  182. package/src/filtered-task-limits.js +93 -0
  183. package/src/filtered-tasks-api.js +53 -0
  184. package/src/filtered-tasks.js +576 -0
  185. package/src/frame-policy.js +66 -0
  186. package/src/fs.js +57 -0
  187. package/src/git.js +626 -0
  188. package/src/github-review-threads.js +181 -0
  189. package/src/github.js +538 -0
  190. package/src/http.js +69 -0
  191. package/src/improve-settings.js +38 -0
  192. package/src/improve.js +187 -0
  193. package/src/integrations.js +373 -0
  194. package/src/integrity.js +259 -0
  195. package/src/journey-api.js +77 -0
  196. package/src/journey-replay.js +65 -0
  197. package/src/journey-store.js +97 -0
  198. package/src/journeys.js +60 -0
  199. package/src/json.js +81 -0
  200. package/src/launch-profiles.js +292 -0
  201. package/src/learnings.js +80 -0
  202. package/src/local-app-url.js +19 -0
  203. package/src/log.js +39 -0
  204. package/src/maintenance.js +335 -0
  205. package/src/memory-analysis.js +182 -0
  206. package/src/memory-claims.js +153 -0
  207. package/src/memory-sqlite.js +236 -0
  208. package/src/memory.js +552 -0
  209. package/src/merge-validation.js +58 -0
  210. package/src/model-catalog.js +239 -0
  211. package/src/model-research.js +91 -0
  212. package/src/notes.js +176 -0
  213. package/src/notifications.js +413 -0
  214. package/src/patterns/auto-team-model.js +230 -0
  215. package/src/patterns/auto-team.js +816 -0
  216. package/src/patterns/council.js +199 -0
  217. package/src/patterns/debate.js +188 -0
  218. package/src/patterns/findings.js +141 -0
  219. package/src/patterns/index.js +21 -0
  220. package/src/patterns/loop.js +158 -0
  221. package/src/patterns/pipeline.js +125 -0
  222. package/src/patterns/prompts.js +181 -0
  223. package/src/patterns/race.js +147 -0
  224. package/src/patterns/schemas.js +40 -0
  225. package/src/patterns/solo.js +46 -0
  226. package/src/patterns/supervisor.js +235 -0
  227. package/src/patterns/task-draft.js +29 -0
  228. package/src/patterns/task-review.js +45 -0
  229. package/src/patterns/tdd.js +231 -0
  230. package/src/patterns/team-strategies.js +67 -0
  231. package/src/performance.js +43 -0
  232. package/src/persistence-status.js +35 -0
  233. package/src/phone-sessions.js +96 -0
  234. package/src/preview-overlay.js +392 -0
  235. package/src/preview.js +230 -0
  236. package/src/privacy.js +23 -0
  237. package/src/project-report.js +22 -0
  238. package/src/project-team-budget.js +22 -0
  239. package/src/project-team-model.js +225 -0
  240. package/src/project-team-policy.js +42 -0
  241. package/src/project-team-store.js +234 -0
  242. package/src/project-team-usage.js +24 -0
  243. package/src/project-teams-api.js +43 -0
  244. package/src/project-teams.js +644 -0
  245. package/src/projects.js +290 -0
  246. package/src/provider-eligibility.js +9 -0
  247. package/src/readiness.js +98 -0
  248. package/src/remote-tls.js +66 -0
  249. package/src/remote-workers/bundles.js +198 -0
  250. package/src/remote-workers/client.js +332 -0
  251. package/src/remote-workers/durability.js +37 -0
  252. package/src/remote-workers/protocol.js +68 -0
  253. package/src/remote-workers/stdio.js +138 -0
  254. package/src/remote-workers/worker.js +304 -0
  255. package/src/remote.js +204 -0
  256. package/src/repo-config.js +201 -0
  257. package/src/repo-coordination.js +725 -0
  258. package/src/repo-tree.js +106 -0
  259. package/src/repository-identity.js +13 -0
  260. package/src/retention-policy.js +466 -0
  261. package/src/review-inbox.js +38 -0
  262. package/src/routing.js +49 -0
  263. package/src/run-analysis.js +111 -0
  264. package/src/run-cold-store.js +231 -0
  265. package/src/run-environment.js +15 -0
  266. package/src/run-history.js +91 -0
  267. package/src/run-log.js +160 -0
  268. package/src/run-permissions.js +111 -0
  269. package/src/run-projection.js +77 -0
  270. package/src/run-recovery.js +53 -0
  271. package/src/run-status.js +56 -0
  272. package/src/serve.js +138 -0
  273. package/src/server.js +2871 -0
  274. package/src/shared-access.js +103 -0
  275. package/src/shared-accounts.js +381 -0
  276. package/src/shared-api.js +239 -0
  277. package/src/shell.js +346 -0
  278. package/src/ship.js +344 -0
  279. package/src/slots.js +74 -0
  280. package/src/snapshot.js +388 -0
  281. package/src/source-context.js +17 -0
  282. package/src/sse-writer.js +70 -0
  283. package/src/ssh-projects/bootstrap.mjs +95 -0
  284. package/src/ssh-projects/config.js +45 -0
  285. package/src/ssh-projects/gateway.js +143 -0
  286. package/src/ssh-projects/manager.js +320 -0
  287. package/src/ssh-projects/team-autostart.mjs +93 -0
  288. package/src/ssh-projects/team-remove.mjs +69 -0
  289. package/src/ssh-projects/team-status.mjs +39 -0
  290. package/src/ssh-projects/transport.js +133 -0
  291. package/src/stats.js +139 -0
  292. package/src/store.js +544 -0
  293. package/src/stream.js +91 -0
  294. package/src/sweeps.js +241 -0
  295. package/src/tailscale.js +101 -0
  296. package/src/task-draft.js +23 -0
  297. package/src/task-team.js +164 -0
  298. package/src/team-evaluation.js +243 -0
  299. package/src/team-mailbox.js +160 -0
  300. package/src/team-memory.js +84 -0
  301. package/src/team-rerun-graph.js +15 -0
  302. package/src/team-reruns.js +130 -0
  303. package/src/team-scheduler.js +121 -0
  304. package/src/team-steering.js +21 -0
  305. package/src/team-template-store.js +98 -0
  306. package/src/team-templates.js +117 -0
  307. package/src/team-tool-client.mjs +79 -0
  308. package/src/team-tools.js +235 -0
  309. package/src/teams.js +0 -0
  310. package/src/templates.js +62 -0
  311. package/src/transcript.js +134 -0
  312. package/src/verification-integrity.js +27 -0
  313. package/src/verification.js +183 -0
  314. package/src/watch.js +195 -0
  315. package/src/work-queue.js +47 -0
  316. package/src/workflow-control.js +83 -0
  317. package/src/workspace-api.js +207 -0
  318. package/src/workspace-changes.js +61 -0
  319. package/src/workspace.js +146 -0
@@ -0,0 +1,88 @@
1
+ import { execFile } from "node:child_process";
2
+ import { mkdir, readFile, rename, rm, stat } from "node:fs/promises";
3
+ import { createHash, randomBytes } from "node:crypto";
4
+ import { join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { CODEFLOW_HOME } from "./config.js";
7
+ let compiling;
8
+ function run(file, args, input, signal) {
9
+ return new Promise((resolve, reject) => {
10
+ const child = execFile(file, args, { signal, timeout: 6e4, maxBuffer: 20 * 1024 * 1024 }, (error, stdout, stderr) => {
11
+ if (error) {
12
+ let message = stderr.trim() || error.message;
13
+ try {
14
+ message = JSON.parse(stdout).error || message;
15
+ } catch {
16
+ }
17
+ reject(new Error(message));
18
+ } else resolve(stdout);
19
+ });
20
+ child.stdin?.end(input === void 0 ? void 0 : JSON.stringify(input));
21
+ });
22
+ }
23
+ async function helper() {
24
+ if (process.platform !== "darwin") throw new Error("Native computer tools currently support macOS. Use browser tools or a configured computer-use MCP server on this platform.");
25
+ if (!compiling) compiling = (async () => {
26
+ const packaged = fileURLToPath(new URL("../scripts/computer-use", import.meta.url));
27
+ if (await stat(packaged).then((info) => info.isFile() && !!(info.mode & 73), () => false)) return packaged;
28
+ const source = fileURLToPath(new URL("../scripts/computer-use.swift", import.meta.url));
29
+ const hash = createHash("sha256").update(await readFile(source)).digest("hex").slice(0, 16);
30
+ const dir = join(CODEFLOW_HOME, "helpers"), binary = join(dir, `computer-${hash}`);
31
+ if (await stat(binary).then(() => true, () => false)) return binary;
32
+ await mkdir(dir, { recursive: true, mode: 448 });
33
+ const temporary = `${binary}-${randomBytes(4).toString("hex")}`;
34
+ try {
35
+ await run("/usr/bin/swiftc", [source, "-o", temporary]);
36
+ await rename(temporary, binary);
37
+ } catch (error) {
38
+ await rm(temporary, { force: true });
39
+ throw new Error(`Computer helper is unavailable: ${error.message}`);
40
+ }
41
+ return binary;
42
+ })().catch((error) => {
43
+ compiling = void 0;
44
+ throw error;
45
+ });
46
+ return compiling;
47
+ }
48
+ class AgentComputer {
49
+ constructor(execute = async (input, signal) => JSON.parse(await run(await helper(), [], input, signal))) {
50
+ this.execute = execute;
51
+ }
52
+ screenshot;
53
+ static generation = 0;
54
+ static queue = Promise.resolve();
55
+ call(name, args, signal) {
56
+ const work = AgentComputer.queue.catch(() => {
57
+ }).then(async () => {
58
+ if (signal.aborted) throw new Error("This computer turn has ended");
59
+ if (name === "computer_screenshot") {
60
+ const result = await this.execute({ action: "screenshot", ...args.app ? { expectedApp: args.app } : {} }, signal);
61
+ const id = randomBytes(12).toString("hex");
62
+ this.screenshot = { id, at: Date.now(), width: result.width, height: result.height, pointWidth: result.pointWidth, pointHeight: result.pointHeight, app: result.app, generation: AgentComputer.generation };
63
+ return { content: [{ type: "text", text: JSON.stringify({ screenshot: id, width: result.width, height: result.height, app: result.app, note: "Coordinates default to these screenshot pixels. Set coordinateSpace=normalized explicitly for 0\u20131000 coordinates. Capture again after every action." }) }, { type: "image", mimeType: "image/png", data: result.data }] };
64
+ }
65
+ if (name !== "computer_action") throw new Error("Unknown computer tool");
66
+ const seen = this.screenshot;
67
+ if (!seen || args.screenshot !== seen.id || Date.now() - seen.at > 6e4 || seen.generation !== AgentComputer.generation) throw new Error("Take a fresh computer_screenshot before acting");
68
+ const request = { ...args };
69
+ delete request.screenshot;
70
+ delete request.coordinateSpace;
71
+ if (args.action === "click") {
72
+ const width = args.coordinateSpace === "normalized" ? 1e3 : seen.width, height = args.coordinateSpace === "normalized" ? 1e3 : seen.height;
73
+ if (!Number.isFinite(args.x) || !Number.isFinite(args.y) || args.x < 0 || args.y < 0 || args.x >= width || args.y >= height) throw new Error("Coordinates are outside the screenshot");
74
+ request.x = args.x * seen.pointWidth / width;
75
+ request.y = args.y * seen.pointHeight / height;
76
+ }
77
+ this.screenshot = void 0;
78
+ AgentComputer.generation++;
79
+ request.expectedApp = seen.app;
80
+ return this.execute(request, signal);
81
+ });
82
+ AgentComputer.queue = work;
83
+ return work;
84
+ }
85
+ }
86
+ export {
87
+ AgentComputer
88
+ };
@@ -0,0 +1,155 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { execFile, spawnSync } from "node:child_process";
3
+ import { lstatSync, readFileSync, realpathSync } from "node:fs";
4
+ import { dirname, isAbsolute, join, resolve } from "node:path";
5
+ import { childEnv } from "./env.js";
6
+ const filteredContainers = /* @__PURE__ */ new WeakMap();
7
+ function filteredTaskContainer(raw, id, readOnly = []) {
8
+ if (raw.authVolume || !/^sha256:[a-f0-9]{64}$/.test(raw.image)) throw new Error("Filtered containers require an immutable image and no authentication volume");
9
+ if (!/^filtered-[a-f0-9]{24}$/.test(id) || readOnly.length > 500 || readOnly.some((path) => !path || path.startsWith("/") || /[\\\x00-\x1f]/.test(path) || path.split("/").some((part) => !part || part === "." || part === ".." || part === ".git"))) throw new Error("Invalid filtered container receipt");
10
+ const config = validateAgentContainer(raw);
11
+ filteredContainers.set(config, { id, readOnly: [...readOnly] });
12
+ return config;
13
+ }
14
+ function validateAgentContainer(raw) {
15
+ const value = raw;
16
+ if (!value || typeof value !== "object" || typeof value.image !== "string" || !/^[a-zA-Z0-9][\w./:@-]{0,199}$/.test(value.image)) throw new Error("Agent isolation needs a valid prebuilt Docker image");
17
+ if (value.authVolume !== void 0 && (typeof value.authVolume !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.test(value.authVolume))) throw new Error("Agent authentication must use an explicitly named Docker volume, never a host path");
18
+ if (value.network !== void 0 && typeof value.network !== "boolean") throw new Error("Agent container network must be enabled or disabled explicitly");
19
+ if (value.memoryMb !== void 0 && (!Number.isInteger(value.memoryMb) || value.memoryMb < 128 || value.memoryMb > 65536)) throw new Error("Agent container memory must be 128\u201365536 MB");
20
+ if (value.cpus !== void 0 && (!Number.isFinite(value.cpus) || value.cpus < 0.1 || value.cpus > 64)) throw new Error("Agent container CPUs must be 0.1\u201364");
21
+ return {
22
+ image: value.image,
23
+ ...value.authVolume ? { authVolume: value.authVolume } : {},
24
+ ...value.network === void 0 ? {} : { network: value.network },
25
+ ...value.memoryMb === void 0 ? {} : { memoryMb: value.memoryMb },
26
+ ...value.cpus === void 0 ? {} : { cpus: value.cpus }
27
+ };
28
+ }
29
+ function agentContainerLaunch(command, args, cwd, mode, raw, extraReadOnly = []) {
30
+ const filtered = filteredContainers.get(raw);
31
+ const container = validateAgentContainer(raw);
32
+ if (!["claude", "codex", "agy"].includes(command)) throw new Error("Only supported official agent CLIs can run in an agent container");
33
+ const root = realpathSync(cwd);
34
+ if (!lstatSync(root).isDirectory()) throw new Error("Agent workspace must be a directory");
35
+ const mounts = /* @__PURE__ */ new Map([[root, mode === "read"]]);
36
+ for (const path of readOnlyGitMetadata(root)) mounts.set(path, true);
37
+ for (const path of extraReadOnly) mounts.set(realpathSync(path), true);
38
+ for (const relative of filtered?.readOnly ?? []) {
39
+ const target = realpathSync(join(root, relative));
40
+ if (!target.startsWith(`${root}/`) || !lstatSync(target).isFile()) throw new Error("Filtered read-only mounts must be regular files inside the export");
41
+ mounts.set(target, true);
42
+ }
43
+ for (const path of mounts.keys()) if (!isAbsolute(path) || /[,\n\r\0]/.test(path)) throw new Error("Docker isolation cannot mount paths containing commas or line breaks");
44
+ const name = `codeflow-agent-${randomUUID().replace(/-/g, "")}`;
45
+ const user = typeof process.getuid === "function" ? ["--user", `${process.getuid()}:${process.getgid()}`] : ["--user", "65532:65532"];
46
+ return {
47
+ command: "docker",
48
+ name,
49
+ args: [
50
+ "run",
51
+ "--rm",
52
+ "--init",
53
+ "--interactive",
54
+ "--pull=never",
55
+ "--name",
56
+ name,
57
+ "--label",
58
+ "codeflow.managed-agent=true",
59
+ "--cap-drop=ALL",
60
+ "--security-opt=no-new-privileges",
61
+ ...filtered ? ["--label", `codeflow.filtered-task=${filtered.id}`] : [],
62
+ ...filtered ? ["--read-only", "--tmpfs", `/home/codeflow:rw,nosuid,nodev,size=134217728,uid=${typeof process.getuid === "function" ? process.getuid() : 65532},gid=${typeof process.getgid === "function" ? process.getgid() : 65532},mode=0700`] : [],
63
+ "--pids-limit",
64
+ "256",
65
+ "--memory",
66
+ `${container.memoryMb ?? 4096}m`,
67
+ "--cpus",
68
+ String(container.cpus ?? 2),
69
+ ...user,
70
+ "--workdir",
71
+ root,
72
+ ...[...mounts].flatMap(([path, readOnly]) => ["--mount", `type=bind,source=${path},target=${path}${readOnly ? ",readonly" : ""}`]),
73
+ ...container.authVolume ? ["--mount", `type=volume,source=${container.authVolume},target=/home/codeflow`] : [],
74
+ "--tmpfs",
75
+ "/tmp:rw,nosuid,nodev,size=268435456",
76
+ "--env",
77
+ "HOME=/home/codeflow",
78
+ "--env",
79
+ "CODEX_HOME=/home/codeflow/.codex",
80
+ "--env",
81
+ "GIT_TERMINAL_PROMPT=0",
82
+ "--env",
83
+ "GIT_OPTIONAL_LOCKS=0",
84
+ "--env",
85
+ "NO_COLOR=1",
86
+ "--env",
87
+ "GIT_CONFIG_GLOBAL=/dev/null",
88
+ "--env",
89
+ "GIT_CONFIG_SYSTEM=/dev/null",
90
+ "--env",
91
+ "GIT_CONFIG_COUNT=1",
92
+ "--env",
93
+ "GIT_CONFIG_KEY_0=core.excludesFile",
94
+ "--env",
95
+ "GIT_CONFIG_VALUE_0=/dev/null",
96
+ ...container.network === false ? ["--network", "none"] : [],
97
+ "--entrypoint",
98
+ command,
99
+ container.image,
100
+ ...args
101
+ ]
102
+ };
103
+ }
104
+ function readOnlyGitMetadata(root) {
105
+ const marker = join(root, ".git");
106
+ let info;
107
+ try {
108
+ info = lstatSync(marker);
109
+ } catch (error) {
110
+ if (error.code === "ENOENT") return [];
111
+ throw error;
112
+ }
113
+ if (info.isSymbolicLink()) throw new Error("Agent isolation refuses a symlinked .git marker");
114
+ if (info.isDirectory()) return [marker];
115
+ if (!info.isFile()) throw new Error("Agent workspace has an invalid .git marker");
116
+ const gitdir = /^gitdir: (.+)\r?\n?$/.exec(readFileSync(marker, "utf8"));
117
+ if (!gitdir) throw new Error("Agent workspace has an invalid worktree .git marker");
118
+ const admin = realpathSync(resolve(root, gitdir[1]));
119
+ const common = realpathSync(resolve(admin, readFileSync(join(admin, "commondir"), "utf8").trim()));
120
+ const backlink = realpathSync(resolve(admin, readFileSync(join(admin, "gitdir"), "utf8").trim()));
121
+ if (backlink !== realpathSync(marker) || dirname(admin) !== join(common, "worktrees") || !lstatSync(join(common, "objects")).isDirectory() || !lstatSync(join(common, "HEAD")).isFile()) throw new Error("Agent worktree metadata does not belong to this workspace");
122
+ return [marker, common];
123
+ }
124
+ const isAgentContainerName = (name) => typeof name === "string" && /^codeflow-agent-[a-f0-9]{32}$/.test(name);
125
+ function stopAgentContainer(name) {
126
+ if (!isAgentContainerName(name)) return Promise.resolve();
127
+ return new Promise((resolve2) => {
128
+ execFile("docker", ["rm", "--force", name], { env: childEnv(), timeout: 5e3 }, () => resolve2());
129
+ });
130
+ }
131
+ function stopAgentContainerSync(name) {
132
+ if (isAgentContainerName(name)) spawnSync("docker", ["rm", "--force", name], { env: childEnv(), timeout: 1500, stdio: "ignore" });
133
+ }
134
+ function agentContainerCapabilities(container) {
135
+ const configured = validateAgentContainer(container);
136
+ return {
137
+ runtime: "docker",
138
+ image: configured.image,
139
+ network: configured.network !== false,
140
+ credentialSource: configured.authVolume ? "named-volume" : "image",
141
+ liveTeamTools: false,
142
+ nativeCodexApprovals: true,
143
+ hostHomeMounted: false
144
+ };
145
+ }
146
+ export {
147
+ agentContainerCapabilities,
148
+ agentContainerLaunch,
149
+ filteredTaskContainer,
150
+ isAgentContainerName,
151
+ readOnlyGitMetadata,
152
+ stopAgentContainer,
153
+ stopAgentContainerSync,
154
+ validateAgentContainer
155
+ };
@@ -0,0 +1,131 @@
1
+ import { constants } from "node:fs";
2
+ import { mkdir, open, readdir, rename, rm } from "node:fs/promises";
3
+ import { randomBytes } from "node:crypto";
4
+ import { join } from "node:path";
5
+ import { closed, digest, text, validateTeamConfiguration, validateTeamSnapshot } from "./project-team-model.js";
6
+ import { ProjectTeamConflict } from "./project-team-store.js";
7
+ const MAX_BYTES = 2 * 1024 * 1024;
8
+ function validate(value) {
9
+ const r = closed(value, ["version", "id", "revision", "name", "configuration", "createdAt", "updatedAt", "versions", "operations"], "reusable agent team");
10
+ const snapshot = validateTeamSnapshot({ id: r.id, revision: r.revision, name: r.name, configuration: r.configuration });
11
+ if (r.version !== 1 || !Number.isSafeInteger(r.createdAt) || r.createdAt < 0 || !Number.isSafeInteger(r.updatedAt) || r.updatedAt < 0 || !Array.isArray(r.versions) || r.versions.length !== r.revision || r.versions.length > 100 || !Array.isArray(r.operations) || r.operations.length !== r.revision) throw new Error("Invalid reusable team history");
12
+ r.versions.forEach((entry, i) => {
13
+ const v = validateTeamSnapshot(entry);
14
+ if (v.id !== r.id || v.revision !== i + 1) throw new Error("Invalid reusable team version sequence");
15
+ });
16
+ if (digest(snapshot) !== digest(r.versions.at(-1))) throw new Error("Reusable team differs from its current version");
17
+ for (const op of r.operations) if (!op || typeof op.id !== "string" || !op.id || op.id.length > 150 || !/^[a-f0-9]{64}$/.test(op.hash)) throw new Error("Invalid reusable team operation");
18
+ if (new Set(r.operations.map((op) => op.id)).size !== r.operations.length) throw new Error("Duplicate reusable team operation");
19
+ return r;
20
+ }
21
+ class AgentTeamDefinitions {
22
+ constructor(directory, changed = () => {
23
+ }) {
24
+ this.directory = directory;
25
+ this.changed = changed;
26
+ }
27
+ definitions = /* @__PURE__ */ new Map();
28
+ queue = Promise.resolve();
29
+ loaded = false;
30
+ faults = /* @__PURE__ */ new Map();
31
+ async load() {
32
+ this.loaded = false;
33
+ this.definitions.clear();
34
+ this.faults.clear();
35
+ const files = await readdir(this.directory).catch((e) => {
36
+ if (e.code === "ENOENT") return [];
37
+ throw e;
38
+ });
39
+ for (const name of files.filter((f) => /^at-[a-f0-9]{24}\.json$/.test(f))) {
40
+ try {
41
+ const file = await open(join(this.directory, name), constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
42
+ let value;
43
+ try {
44
+ const info = await file.stat();
45
+ if (!info.isFile() || info.size > MAX_BYTES) throw new Error("Reusable team exceeds its file bound");
46
+ const buffer = Buffer.alloc(MAX_BYTES + 1), { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
47
+ if (bytesRead > MAX_BYTES) throw new Error("Reusable team grew beyond its bound");
48
+ value = JSON.parse(buffer.subarray(0, bytesRead).toString());
49
+ } finally {
50
+ await file.close();
51
+ }
52
+ const definition = validate(value);
53
+ if (name !== `${definition.id}.json`) throw new Error("Reusable team filename differs from identity");
54
+ this.definitions.set(definition.id, definition);
55
+ } catch (error) {
56
+ this.faults.set(name.slice(0, -5), error.message);
57
+ }
58
+ }
59
+ this.loaded = true;
60
+ }
61
+ list() {
62
+ return [...this.definitions.values()].map(({ id, revision, name, configuration }) => structuredClone({ id, revision, name, configuration }));
63
+ }
64
+ snapshot(id, revision) {
65
+ const definition = this.get(id), snapshot = definition.versions.find((v) => v.revision === revision);
66
+ if (!snapshot) throw new Error("Choose an existing reusable team version");
67
+ return structuredClone(snapshot);
68
+ }
69
+ get(id) {
70
+ if (this.faults.has(id)) throw new Error(`Reusable team storage is unavailable: ${this.faults.get(id)}`);
71
+ const value = this.definitions.get(id);
72
+ if (!value) throw Object.assign(new Error("Reusable agent team not found"), { status: 404 });
73
+ return structuredClone(value);
74
+ }
75
+ save(input) {
76
+ const work = this.queue.then(async () => {
77
+ if (!this.loaded) throw new Error("Load reusable teams before writing");
78
+ const operationId = text(input.operationId, 150, "operation ID"), name = text(input.name, 80, "team name"), configuration = validateTeamConfiguration(input.configuration);
79
+ const hash = digest({ name, configuration }), id = input.id ?? `at-${digest(operationId).slice(0, 24)}`;
80
+ if (this.faults.has(id)) throw new Error("Repair the damaged reusable team before writing");
81
+ const before = input.id ? this.get(id) : this.definitions.get(id);
82
+ const prior = before?.operations.find((op) => op.id === operationId);
83
+ if (prior) {
84
+ if (prior.hash !== hash) throw new ProjectTeamConflict("Operation ID was reused with different content");
85
+ return structuredClone(before);
86
+ }
87
+ if (before && !input.id) throw new ProjectTeamConflict("Reusable team creation ID collision");
88
+ if (before && input.revision !== before.revision) throw new ProjectTeamConflict("Reusable team changed. Reload before saving.");
89
+ if (!before && this.definitions.size >= 100) throw new Error("The reusable team library is full");
90
+ const revision = (before?.revision ?? 0) + 1, now = Date.now();
91
+ const snapshot = { id, revision, name, configuration };
92
+ const next = { ...snapshot, version: 1, createdAt: before?.createdAt ?? now, updatedAt: now, versions: [...before?.versions ?? [], snapshot], operations: [...before?.operations ?? [], { id: operationId, hash }] };
93
+ validate(next);
94
+ const data = JSON.stringify(next);
95
+ if (Buffer.byteLength(data) > MAX_BYTES) throw new Error("Reusable team history is full");
96
+ await mkdir(this.directory, { recursive: true, mode: 448 });
97
+ const temp = join(this.directory, `.${id}-${randomBytes(6).toString("hex")}.tmp`);
98
+ try {
99
+ const file = await open(temp, "wx", 384);
100
+ try {
101
+ await file.writeFile(data);
102
+ await file.sync();
103
+ } finally {
104
+ await file.close();
105
+ }
106
+ await rename(temp, join(this.directory, `${id}.json`));
107
+ if (process.platform !== "win32") {
108
+ const dir = await open(this.directory, "r");
109
+ try {
110
+ await dir.sync();
111
+ } finally {
112
+ await dir.close();
113
+ }
114
+ }
115
+ this.definitions.set(id, structuredClone(next));
116
+ this.changed();
117
+ return structuredClone(next);
118
+ } finally {
119
+ await rm(temp, { force: true }).catch(() => void 0);
120
+ }
121
+ });
122
+ this.queue = work.catch(() => void 0);
123
+ return work;
124
+ }
125
+ async flush() {
126
+ await this.queue;
127
+ }
128
+ }
129
+ export {
130
+ AgentTeamDefinitions
131
+ };
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ // Dependency-free stdio MCP transport. Only the parent-created turn capability is used;
3
+ // this process has no file, shell, permission-approval, or identity-selection operations.
4
+ import { createInterface } from "node:readline";
5
+ import { readFileSync, unlinkSync } from "node:fs";
6
+
7
+ let connection;
8
+ try {
9
+ if (process.argv[2] === "--connection") {
10
+ connection = JSON.parse(readFileSync(process.argv[3], "utf8"));
11
+ // Read once, then gone: every CLI on this machine runs as the same user, so a credential
12
+ // left on disk could be read by another process's shell for as long as the turn lasts.
13
+ try { unlinkSync(process.argv[3]); } catch { /* already removed */ }
14
+ }
15
+ } catch { process.exit(1); }
16
+ const endpoint = connection?.endpoint;
17
+ const token = connection?.token;
18
+ delete process.env.CODEFLOW_TEAM_CAPABILITY;
19
+ delete process.env.CODEFLOW_TOKEN;
20
+ if (!endpoint || !/^http:\/\/127\.0\.0\.1:\d+\/v1\/agent-tools$/.test(endpoint) || !token) process.exit(1);
21
+
22
+ let initialized = false;
23
+ const send = (value) => process.stdout.write(`${JSON.stringify(value)}\n`);
24
+ async function bridge(body) {
25
+ const response = await fetch(endpoint, {
26
+ method: "POST", redirect: "error", signal: AbortSignal.timeout(660_000),
27
+ headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
28
+ body: JSON.stringify(body),
29
+ });
30
+ const result = await response.json();
31
+ if (!response.ok || result.error) throw new Error(result.error ?? "Agent bridge request failed");
32
+ return result;
33
+ }
34
+ async function request(message) {
35
+ // JSON can parse successfully without yielding a JSON-RPC object (for example null).
36
+ // Reject the envelope before dereferencing it so malformed input cannot terminate MCP.
37
+ const validId = (id) => typeof id === "string" || typeof id === "number" && Number.isFinite(id);
38
+ if (!message || typeof message !== "object" || Array.isArray(message) || message.jsonrpc !== "2.0" ||
39
+ typeof message.method !== "string" || (message.id !== undefined && !validId(message.id))) {
40
+ send({ jsonrpc: "2.0", id: validId(message?.id) ? message.id : null, error: { code: -32600, message: "Invalid request" } }); return;
41
+ }
42
+ const id = message.id;
43
+ if (id === undefined) return;
44
+ try {
45
+ let result;
46
+ if (message.method === "initialize") {
47
+ initialized = true;
48
+ const supported = ["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"];
49
+ result = { protocolVersion: supported.includes(message.params?.protocolVersion) ? message.params.protocolVersion : "2025-03-26", capabilities: { tools: {} }, serverInfo: { name: "code-flow-agent", version: "1.0.0" } };
50
+ } else if (message.method === "ping") result = {};
51
+ else if (!initialized) throw new Error("Initialize the agent bridge first");
52
+ else if (message.method === "tools/list") result = await bridge({ method: "tools/list" });
53
+ else if (message.method === "tools/call") {
54
+ try {
55
+ const value = await bridge({ name: message.params?.name, arguments: message.params?.arguments ?? {} });
56
+ result = value;
57
+ } catch (error) { result = { content: [{ type: "text", text: error instanceof Error ? error.message : "Agent command failed" }], isError: true }; }
58
+ } else {
59
+ send({ jsonrpc: "2.0", id, error: { code: -32601, message: "Method not found" } }); return;
60
+ }
61
+ send({ jsonrpc: "2.0", id, result });
62
+ } catch (error) { send({ jsonrpc: "2.0", id, error: { code: -32603, message: error instanceof Error ? error.message : "Agent bridge failed" } }); }
63
+ }
64
+
65
+ const input = createInterface({ input: process.stdin, crlfDelay: Infinity });
66
+ let inflight = 0;
67
+ const limit = (id) => send({ jsonrpc: "2.0", id, error: { code: -32600, message: "Request exceeds bridge limits" } });
68
+ /** The id of an envelope too large to parse, so the caller's request still gets its answer instead of hanging. */
69
+ const idOf = (line) => { const match = /"id"\s*:\s*("(?:[^"\\]|\\.)*"|-?\d+)/.exec(line.slice(0, 4_096)); try { return match ? JSON.parse(match[1]) : null; } catch { return null; } };
70
+ for await (const line of input) {
71
+ if (line.length > 64 * 1024) { limit(idOf(line)); continue; }
72
+ try {
73
+ const message = JSON.parse(line);
74
+ // Answer with the request's own id: an `id: null` error leaves that request waiting until the CLI's tool timeout.
75
+ if (inflight >= 4) { limit(message && typeof message === "object" && (typeof message.id === "string" || typeof message.id === "number") ? message.id : null); continue; }
76
+ inflight++;
77
+ void request(message).finally(() => { inflight--; });
78
+ } catch { send({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } }); }
79
+ }