@midhunxavier/code-flow 0.0.0-stage → 0.1.0-preview.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 (284) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +122 -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/browser-7XI52A6V.js +8 -0
  17. package/public/assets/changes-EYFPASEI.js +1 -0
  18. package/public/assets/chunk-7H3ZLTKA.js +2 -0
  19. package/public/assets/chunk-L7TUCIYF.js +1 -0
  20. package/public/assets/chunk-LLMQG23J.js +163 -0
  21. package/public/assets/main-KIRM2OEK.js +1 -0
  22. package/public/assets/settings-QJZVW22Y.js +9 -0
  23. package/public/assets/styles-615EBB20.css +32 -0
  24. package/public/assets/terminal-A2FI2NAX.js +62 -0
  25. package/public/assets.json +1 -0
  26. package/public/bundle-meta.json +1 -0
  27. package/public/docs/.nojekyll +0 -0
  28. package/public/docs/accessibility.html +19 -0
  29. package/public/docs/acp-agents.html +117 -0
  30. package/public/docs/agent-containers.html +79 -0
  31. package/public/docs/api.html +1130 -0
  32. package/public/docs/app.js +342 -0
  33. package/public/docs/application-improvements.html +275 -0
  34. package/public/docs/architecture-explorer.html +19 -0
  35. package/public/docs/architecture.html +149 -0
  36. package/public/docs/auto-team.html +142 -0
  37. package/public/docs/automations.html +109 -0
  38. package/public/docs/checkpoint-review.html +37 -0
  39. package/public/docs/code-hosts.html +35 -0
  40. package/public/docs/code-intelligence.html +35 -0
  41. package/public/docs/configuration.html +346 -0
  42. package/public/docs/context-inspection.html +23 -0
  43. package/public/docs/coordinated-changes.html +28 -0
  44. package/public/docs/deferred-runs.html +25 -0
  45. package/public/docs/dependency-discovery.html +19 -0
  46. package/public/docs/documentation.html +84 -0
  47. package/public/docs/dynamic-teams-evaluation.html +174 -0
  48. package/public/docs/dynamic-teams-implementation-plan.html +882 -0
  49. package/public/docs/dynamic-teams-implementation-review-1.html +141 -0
  50. package/public/docs/dynamic-teams-plan-review-0.html +204 -0
  51. package/public/docs/extensions.html +42 -0
  52. package/public/docs/external-sources.html +96 -0
  53. package/public/docs/filesystem-policy.html +26 -0
  54. package/public/docs/filtered-tasks.html +44 -0
  55. package/public/docs/font-license.txt +92 -0
  56. package/public/docs/getting-started.html +118 -0
  57. package/public/docs/github-review-threads.html +26 -0
  58. package/public/docs/github.html +157 -0
  59. package/public/docs/help.html +69 -0
  60. package/public/docs/index.html +77 -0
  61. package/public/docs/installation.html +74 -0
  62. package/public/docs/launch-profiles.html +48 -0
  63. package/public/docs/maintenance.html +60 -0
  64. package/public/docs/memory-claims.html +31 -0
  65. package/public/docs/memory-storage.html +58 -0
  66. package/public/docs/notifications.html +39 -0
  67. package/public/docs/patterns.html +195 -0
  68. package/public/docs/performance-results.html +210 -0
  69. package/public/docs/performance.html +166 -0
  70. package/public/docs/project-conventions.html +22 -0
  71. package/public/docs/project-first-navigation-plan.html +587 -0
  72. package/public/docs/project-teams-implementation-plan.html +772 -0
  73. package/public/docs/project-teams.html +99 -0
  74. package/public/docs/provider-compatibility.html +20 -0
  75. package/public/docs/releasing.html +61 -0
  76. package/public/docs/remote-workers.html +64 -0
  77. package/public/docs/review-inbox.html +26 -0
  78. package/public/docs/run-storage.html +23 -0
  79. package/public/docs/runs.html +141 -0
  80. package/public/docs/search.json +1 -0
  81. package/public/docs/security.html +133 -0
  82. package/public/docs/shared-projects.html +31 -0
  83. package/public/docs/ssh-projects.html +60 -0
  84. package/public/docs/style.css +768 -0
  85. package/public/docs/task-reruns.html +30 -0
  86. package/public/docs/team-collaboration.html +379 -0
  87. package/public/docs/verification.html +22 -0
  88. package/public/docs/vscode.html +53 -0
  89. package/public/docs/worker-permission-verification.html +122 -0
  90. package/public/index.html +17 -0
  91. package/scripts/check-node.mjs +7 -0
  92. package/scripts/codeflow.mjs +55 -0
  93. package/scripts/pty.py +273 -0
  94. package/src/acp-connections.js +110 -0
  95. package/src/adapters/acp-presets.js +37 -0
  96. package/src/adapters/acp-types.js +0 -0
  97. package/src/adapters/acp.js +328 -0
  98. package/src/adapters/agy-auth.js +67 -0
  99. package/src/adapters/agy-isolation.js +223 -0
  100. package/src/adapters/agy.js +297 -0
  101. package/src/adapters/claude.js +220 -0
  102. package/src/adapters/codex-app-server.js +518 -0
  103. package/src/adapters/codex.js +299 -0
  104. package/src/adapters/errors.js +57 -0
  105. package/src/adapters/index.js +91 -0
  106. package/src/adapters/managed-permissions.js +37 -0
  107. package/src/adapters/model-discovery.js +100 -0
  108. package/src/adapters/process.js +418 -0
  109. package/src/adapters/team-tools.js +102 -0
  110. package/src/adapters/types.js +0 -0
  111. package/src/agent-container.js +155 -0
  112. package/src/approvals.js +121 -0
  113. package/src/architecture.js +456 -0
  114. package/src/assets.js +44 -0
  115. package/src/automation.js +901 -0
  116. package/src/certificate-renewal.js +75 -0
  117. package/src/chat-handoff.js +55 -0
  118. package/src/checkpoint-review.js +72 -0
  119. package/src/checks.js +303 -0
  120. package/src/code-host-git.js +102 -0
  121. package/src/code-hosts-api.js +99 -0
  122. package/src/code-hosts.js +482 -0
  123. package/src/code-intelligence-worker.mjs +452 -0
  124. package/src/code-intelligence.js +141 -0
  125. package/src/collab/api.js +365 -0
  126. package/src/collab/checkpoints.js +198 -0
  127. package/src/collab/connections.js +139 -0
  128. package/src/collab/host-execution.js +347 -0
  129. package/src/collab/hub-client.js +210 -0
  130. package/src/collab/link-api.js +229 -0
  131. package/src/collab/link.js +595 -0
  132. package/src/collab/model.js +496 -0
  133. package/src/collab/runner.js +1014 -0
  134. package/src/collab/store.js +1452 -0
  135. package/src/collab/terminals.js +361 -0
  136. package/src/collaboration-reviews.js +116 -0
  137. package/src/config.js +48 -0
  138. package/src/context-budget.js +14 -0
  139. package/src/context-inspection.js +204 -0
  140. package/src/context.js +154 -0
  141. package/src/conventions.js +283 -0
  142. package/src/coordinated-api.js +247 -0
  143. package/src/deferred-launch.js +82 -0
  144. package/src/deferred-runs.js +515 -0
  145. package/src/dependency-health.js +35 -0
  146. package/src/diff.js +43 -0
  147. package/src/docs-help.js +316 -0
  148. package/src/doctor.js +232 -0
  149. package/src/engine.js +4366 -0
  150. package/src/env.js +44 -0
  151. package/src/experience.js +50 -0
  152. package/src/extensions.js +144 -0
  153. package/src/filesystem-access.js +107 -0
  154. package/src/filesystem-policy.js +238 -0
  155. package/src/filtered-task-limits.js +93 -0
  156. package/src/filtered-tasks-api.js +53 -0
  157. package/src/filtered-tasks.js +576 -0
  158. package/src/fs.js +57 -0
  159. package/src/git.js +626 -0
  160. package/src/github-review-threads.js +181 -0
  161. package/src/github.js +538 -0
  162. package/src/http.js +69 -0
  163. package/src/improve-settings.js +38 -0
  164. package/src/improve.js +187 -0
  165. package/src/integrations.js +373 -0
  166. package/src/integrity.js +259 -0
  167. package/src/journey-api.js +77 -0
  168. package/src/journey-replay.js +65 -0
  169. package/src/journey-store.js +97 -0
  170. package/src/journeys.js +60 -0
  171. package/src/json.js +81 -0
  172. package/src/launch-profiles.js +292 -0
  173. package/src/learnings.js +80 -0
  174. package/src/log.js +39 -0
  175. package/src/maintenance.js +335 -0
  176. package/src/memory-analysis.js +182 -0
  177. package/src/memory-claims.js +153 -0
  178. package/src/memory-sqlite.js +236 -0
  179. package/src/memory.js +552 -0
  180. package/src/merge-validation.js +58 -0
  181. package/src/model-catalog.js +238 -0
  182. package/src/model-research.js +91 -0
  183. package/src/notes.js +176 -0
  184. package/src/notifications.js +413 -0
  185. package/src/patterns/auto-team-model.js +212 -0
  186. package/src/patterns/auto-team.js +659 -0
  187. package/src/patterns/council.js +199 -0
  188. package/src/patterns/debate.js +188 -0
  189. package/src/patterns/findings.js +141 -0
  190. package/src/patterns/index.js +19 -0
  191. package/src/patterns/loop.js +158 -0
  192. package/src/patterns/pipeline.js +125 -0
  193. package/src/patterns/prompts.js +181 -0
  194. package/src/patterns/race.js +147 -0
  195. package/src/patterns/schemas.js +40 -0
  196. package/src/patterns/solo.js +49 -0
  197. package/src/patterns/supervisor.js +235 -0
  198. package/src/patterns/task-draft.js +29 -0
  199. package/src/patterns/tdd.js +231 -0
  200. package/src/patterns/team-strategies.js +67 -0
  201. package/src/performance.js +43 -0
  202. package/src/persistence-status.js +35 -0
  203. package/src/phone-sessions.js +96 -0
  204. package/src/preview-overlay.js +383 -0
  205. package/src/preview.js +219 -0
  206. package/src/privacy.js +23 -0
  207. package/src/project-report.js +22 -0
  208. package/src/project-team-model.js +178 -0
  209. package/src/project-team-policy.js +42 -0
  210. package/src/project-team-store.js +213 -0
  211. package/src/project-teams-api.js +36 -0
  212. package/src/project-teams.js +551 -0
  213. package/src/projects.js +286 -0
  214. package/src/provider-eligibility.js +9 -0
  215. package/src/readiness.js +98 -0
  216. package/src/remote-tls.js +66 -0
  217. package/src/remote-workers/bundles.js +198 -0
  218. package/src/remote-workers/client.js +332 -0
  219. package/src/remote-workers/durability.js +37 -0
  220. package/src/remote-workers/protocol.js +68 -0
  221. package/src/remote-workers/stdio.js +138 -0
  222. package/src/remote-workers/worker.js +304 -0
  223. package/src/remote.js +204 -0
  224. package/src/repo-config.js +201 -0
  225. package/src/repo-coordination.js +732 -0
  226. package/src/repo-tree.js +106 -0
  227. package/src/retention-policy.js +466 -0
  228. package/src/review-inbox.js +38 -0
  229. package/src/routing.js +49 -0
  230. package/src/run-analysis.js +111 -0
  231. package/src/run-cold-store.js +231 -0
  232. package/src/run-environment.js +15 -0
  233. package/src/run-history.js +91 -0
  234. package/src/run-log.js +160 -0
  235. package/src/run-permissions.js +110 -0
  236. package/src/run-projection.js +75 -0
  237. package/src/run-recovery.js +53 -0
  238. package/src/run-status.js +54 -0
  239. package/src/serve.js +138 -0
  240. package/src/server.js +2738 -0
  241. package/src/shared-access.js +103 -0
  242. package/src/shared-accounts.js +301 -0
  243. package/src/shared-api.js +239 -0
  244. package/src/shell.js +346 -0
  245. package/src/ship.js +344 -0
  246. package/src/slots.js +74 -0
  247. package/src/snapshot.js +388 -0
  248. package/src/source-context.js +17 -0
  249. package/src/sse-writer.js +70 -0
  250. package/src/ssh-projects/bootstrap.mjs +95 -0
  251. package/src/ssh-projects/config.js +45 -0
  252. package/src/ssh-projects/gateway.js +143 -0
  253. package/src/ssh-projects/manager.js +320 -0
  254. package/src/ssh-projects/team-autostart.mjs +93 -0
  255. package/src/ssh-projects/team-remove.mjs +69 -0
  256. package/src/ssh-projects/team-status.mjs +39 -0
  257. package/src/ssh-projects/transport.js +116 -0
  258. package/src/stats.js +139 -0
  259. package/src/store.js +544 -0
  260. package/src/stream.js +91 -0
  261. package/src/sweeps.js +241 -0
  262. package/src/tailscale.js +101 -0
  263. package/src/task-draft.js +23 -0
  264. package/src/team-evaluation.js +242 -0
  265. package/src/team-mailbox.js +160 -0
  266. package/src/team-memory.js +84 -0
  267. package/src/team-rerun-graph.js +15 -0
  268. package/src/team-reruns.js +130 -0
  269. package/src/team-scheduler.js +121 -0
  270. package/src/team-template-store.js +98 -0
  271. package/src/team-templates.js +117 -0
  272. package/src/team-tool-client.mjs +79 -0
  273. package/src/team-tools.js +176 -0
  274. package/src/teams.js +0 -0
  275. package/src/templates.js +62 -0
  276. package/src/transcript.js +134 -0
  277. package/src/verification-integrity.js +27 -0
  278. package/src/verification.js +183 -0
  279. package/src/watch.js +195 -0
  280. package/src/work-queue.js +47 -0
  281. package/src/workflow-control.js +83 -0
  282. package/src/workspace-api.js +207 -0
  283. package/src/workspace-changes.js +61 -0
  284. package/src/workspace.js +146 -0
package/scripts/pty.py ADDED
@@ -0,0 +1,273 @@
1
+ """code-flow's pseudo-terminal for the side panel's Terminal (src/shell.ts starts it).
2
+
3
+ python3 pty.py COLS ROWS PROGRAM ARGV0 [ARG...]
4
+
5
+ Runs PROGRAM (your login shell) in a new pseudo-terminal, so pagers, prompts, colours and
6
+ full-screen programs work, without a native npm module. Bytes are relayed as they are:
7
+
8
+ fd 0 keystrokes in fd 1 what the terminal shows, out
9
+ fd 3 control, JSON lines both ways:
10
+ in {"resize": [cols, rows]}
11
+ out {"pid": n, "tty": "/dev/ttys004"} once, then {"fg": pgid} whenever the
12
+ terminal's foreground process group changes (PROGRAM's own pid: nothing
13
+ else is running in the foreground)
14
+
15
+ It stops when PROGRAM exits, on SIGTERM/SIGHUP/SIGINT, or when fd 0 closes (code-flow went
16
+ away). Everything still in PROGRAM's session (what it started, unless that called setsid) then
17
+ gets SIGHUP (and SIGCONT, for stopped jobs), and SIGKILL a second later. Exits with PROGRAM's exit code, or 128 + the signal that ended it.
18
+ Standard library only; Python 3.6 or later.
19
+ """
20
+
21
+ import errno
22
+ import fcntl
23
+ import json
24
+ import os
25
+ import select
26
+ import signal
27
+ import struct
28
+ import subprocess
29
+ import sys
30
+ import termios
31
+ import time
32
+
33
+ CONTROL = 3
34
+ GRACE = 1.0
35
+
36
+
37
+ def set_size(fd, cols, rows):
38
+ fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", rows, cols, 0, 0))
39
+
40
+
41
+ def write_all(fd, data):
42
+ """Write everything, waiting when the other end is full (a non-blocking pipe)."""
43
+ view = memoryview(data)
44
+ while view:
45
+ try:
46
+ view = view[os.write(fd, view):]
47
+ except BlockingIOError:
48
+ select.select([], [fd], [], 1.0)
49
+
50
+
51
+ def start(program, argv, cols, rows, control_open):
52
+ master, slave = os.openpty()
53
+ set_size(slave, cols, rows)
54
+ tty = os.ttyname(slave)
55
+ pid = os.fork()
56
+ if pid == 0:
57
+ try:
58
+ os.close(master)
59
+ if control_open:
60
+ os.close(CONTROL)
61
+ os.setsid() # a session of its own, with this terminal as its controlling terminal
62
+ if hasattr(termios, "TIOCSCTTY"):
63
+ fcntl.ioctl(slave, termios.TIOCSCTTY, 0)
64
+ else:
65
+ os.close(os.open(tty, os.O_RDWR))
66
+ for fd in (0, 1, 2):
67
+ os.dup2(slave, fd)
68
+ if slave > 2:
69
+ os.close(slave)
70
+ # Python ignores these at start-up, and ignored signals survive exec: `yes | head`
71
+ # would complain about a broken pipe instead of just stopping.
72
+ for sig in ("SIGPIPE", "SIGXFSZ"):
73
+ if hasattr(signal, sig):
74
+ signal.signal(getattr(signal, sig), signal.SIG_DFL)
75
+ os.execv(program, argv)
76
+ except BaseException as err: # noqa: B902 (anything: report it on the terminal and stop)
77
+ os.write(2, ("couldn't start %s: %s\r\n" % (program, err)).encode())
78
+ os._exit(127)
79
+ os.close(slave)
80
+ return pid, master, tty
81
+
82
+
83
+ def groups_of(session):
84
+ """The process groups of everything in PROGRAM's session: what it started, still running.
85
+
86
+ By session, not by terminal: once PROGRAM (the session's leader) has exited, its jobs no longer
87
+ have a controlling terminal, but they keep its session.
88
+ """
89
+ if os.path.isdir("/proc/self"):
90
+ pids = [int(entry) for entry in os.listdir("/proc") if entry.isdigit()] # Linux: no ps needed
91
+ else:
92
+ try:
93
+ out = subprocess.run(["ps", "-ax", "-o", "pid="], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=5).stdout
94
+ pids = [int(word) for word in out.split() if word.isdigit()]
95
+ except (OSError, subprocess.SubprocessError):
96
+ pids = []
97
+ found = {session}
98
+ for pid in pids:
99
+ try:
100
+ if os.getsid(pid) == session:
101
+ found.add(os.getpgid(pid))
102
+ except OSError:
103
+ pass # gone, or not ours to look at
104
+ return {g for g in found if g > 1 and g != os.getpgrp()}
105
+
106
+
107
+ def hang_up(pid):
108
+ """SIGHUP to everything PROGRAM started, as when a terminal window closes; SIGKILL to what's left."""
109
+ groups = groups_of(pid)
110
+ for g in groups:
111
+ for sig in (signal.SIGHUP, signal.SIGCONT):
112
+ try:
113
+ os.killpg(g, sig)
114
+ except OSError:
115
+ pass
116
+ deadline = time.monotonic() + GRACE
117
+ while groups and time.monotonic() < deadline:
118
+ reap(pid)
119
+ time.sleep(0.05)
120
+ groups = {g for g in groups if alive(g)}
121
+ for g in groups | groups_of(pid):
122
+ try:
123
+ os.killpg(g, signal.SIGKILL)
124
+ except OSError:
125
+ pass
126
+
127
+
128
+ def alive(group):
129
+ try:
130
+ os.killpg(group, 0)
131
+ return True
132
+ except OSError:
133
+ return False
134
+
135
+
136
+ status = {"code": None}
137
+
138
+
139
+ def reap(pid):
140
+ """PROGRAM's exit code once it has exited (and been waited for), else None."""
141
+ if status["code"] is None:
142
+ try:
143
+ done, raw = os.waitpid(pid, os.WNOHANG)
144
+ except ChildProcessError:
145
+ done, raw = pid, 0
146
+ if done:
147
+ status["code"] = 128 + os.WTERMSIG(raw) if os.WIFSIGNALED(raw) else os.WEXITSTATUS(raw)
148
+ return status["code"]
149
+
150
+
151
+ def main():
152
+ cols, rows, program, argv = int(sys.argv[1]), int(sys.argv[2]), sys.argv[3], sys.argv[4:]
153
+ control_open = True
154
+ try:
155
+ os.fstat(CONTROL)
156
+ except OSError:
157
+ control_open = False # started by hand: no control channel
158
+ pid, master, tty = start(program, argv, cols, rows, control_open)
159
+ os.set_blocking(master, False)
160
+
161
+ def tell(message):
162
+ if control_open:
163
+ try:
164
+ write_all(CONTROL, (json.dumps(message) + "\n").encode())
165
+ except OSError:
166
+ pass
167
+
168
+ tell({"pid": pid, "tty": tty})
169
+ try:
170
+ relay(pid, master, tell, [0, CONTROL] if control_open else [0])
171
+ except OSError:
172
+ pass # code-flow went away mid-write: hang up all the same
173
+ finally:
174
+ if status["code"] is not None:
175
+ # PROGRAM exited: pass on what it printed last before hanging up.
176
+ deadline = time.monotonic() + 0.3
177
+ while time.monotonic() < deadline:
178
+ try:
179
+ if not select.select([master], [], [], 0.05)[0]:
180
+ break
181
+ data = os.read(master, 65536)
182
+ if not data:
183
+ break
184
+ write_all(1, data)
185
+ except OSError:
186
+ break
187
+ hang_up(pid)
188
+ os.close(master)
189
+ deadline = time.monotonic() + GRACE
190
+ while reap(pid) is None and time.monotonic() < deadline:
191
+ time.sleep(0.05)
192
+ code = status["code"]
193
+ sys.exit(code if code is not None else 129)
194
+
195
+
196
+ def relay(pid, master, tell, inputs):
197
+ """Bytes both ways until PROGRAM exits, the terminal closes, a stop signal, or code-flow goes away."""
198
+ # Signals only wake the loop; it decides what to do.
199
+ wake_r, wake_w = os.pipe()
200
+ os.set_blocking(wake_r, False)
201
+ os.set_blocking(wake_w, False)
202
+ signal.set_wakeup_fd(wake_w)
203
+ stop = {"now": False}
204
+
205
+ def on_stop(*_):
206
+ stop["now"] = True
207
+
208
+ for sig in (signal.SIGTERM, signal.SIGHUP, signal.SIGINT):
209
+ signal.signal(sig, on_stop)
210
+ signal.signal(signal.SIGCHLD, lambda *_: None)
211
+
212
+ keys = bytearray() # keystrokes the terminal hasn't taken yet
213
+ control = b""
214
+ fg = None
215
+ while not stop["now"]:
216
+ readable, writable, _ = select.select([master, wake_r] + inputs, [master] if keys else [], [], 0.5)
217
+ if wake_r in readable:
218
+ try:
219
+ os.read(wake_r, 512)
220
+ except OSError:
221
+ pass
222
+ if master in readable:
223
+ try:
224
+ data = os.read(master, 65536)
225
+ except BlockingIOError:
226
+ data = None
227
+ except OSError as err: # EIO on Linux once nothing holds the terminal open
228
+ if err.errno != errno.EIO:
229
+ raise
230
+ data = b""
231
+ if data:
232
+ write_all(1, data)
233
+ elif data == b"":
234
+ break
235
+ if 0 in readable:
236
+ data = os.read(0, 65536)
237
+ if not data:
238
+ break # code-flow went away
239
+ keys += data
240
+ if keys:
241
+ try:
242
+ del keys[:os.write(master, keys)]
243
+ except BlockingIOError:
244
+ pass
245
+ if CONTROL in readable:
246
+ try:
247
+ data = os.read(CONTROL, 65536)
248
+ except BlockingIOError:
249
+ data = None
250
+ if data == b"":
251
+ inputs.remove(CONTROL)
252
+ elif data:
253
+ control += data
254
+ *lines, control = control.split(b"\n")
255
+ for line in lines:
256
+ try:
257
+ size = json.loads(line.decode()).get("resize")
258
+ set_size(master, max(2, int(size[0])), max(1, int(size[1])))
259
+ except (ValueError, TypeError, IndexError, AttributeError, OSError):
260
+ pass
261
+ if reap(pid) is not None:
262
+ break
263
+ try:
264
+ now = os.tcgetpgrp(master)
265
+ except OSError:
266
+ now = None
267
+ if now != fg:
268
+ fg = now
269
+ tell({"fg": fg})
270
+
271
+
272
+ if __name__ == "__main__":
273
+ main()
@@ -0,0 +1,110 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { mkdir, open, rename, unlink } from "node:fs/promises";
4
+ import { isAbsolute, join } from "node:path";
5
+ import { CODEFLOW_HOME } from "./config.js";
6
+ const liveBindings = /* @__PURE__ */ new Map();
7
+ const fingerprint = (connection) => createHash("sha256").update(JSON.stringify([connection.id, connection.command, connection.args])).digest("hex");
8
+ function acpBindings(roles) {
9
+ return [...new Set(Object.values(roles).flat().filter((id) => id.startsWith("acp:")))].sort().map((id) => {
10
+ const binding = liveBindings.get(id);
11
+ if (!binding) throw new Error(`ACP connection ${id} is disabled or missing. Restore it in Settings \u2192 Agents & models.`);
12
+ return { ...binding };
13
+ });
14
+ }
15
+ function assertAcpBindings(run) {
16
+ if (JSON.stringify(acpBindings(run.roles)) !== JSON.stringify(run.acpConnections ?? [])) throw new Error("This run's ACP command or arguments changed. Restore its original connection or start a new run.");
17
+ }
18
+ const object = (value) => !!value && typeof value === "object" && !Array.isArray(value);
19
+ const text = (value, limit) => typeof value === "string" && value.length > 0 && value.length <= limit && !/[\x00-\x1f\x7f]/.test(value);
20
+ function validateAcpConnection(value) {
21
+ if (!object(value) || Object.keys(value).some((key) => !["id", "name", "command", "args", "enabled"].includes(key)) || typeof value.id !== "string" || !/^acp:[a-z][a-z0-9-]{0,40}$/.test(value.id) || !text(value.name, 100) || !value.name.trim() || !text(value.command, 4e3) || !(isAbsolute(value.command) || /^[a-zA-Z0-9_][a-zA-Z0-9._-]*$/.test(value.command)) || !Array.isArray(value.args) || value.args.length > 32 || value.args.some((arg) => typeof arg !== "string" || arg.length > 4e3 || /[\x00-\x1f\x7f]/.test(arg)) || typeof value.enabled !== "boolean") {
22
+ throw new Error("Use a unique connection ID, a name, an executable name or absolute path, and at most 32 arguments. Shell command strings are not supported.");
23
+ }
24
+ return { id: value.id, name: value.name.trim(), command: value.command, args: [...value.args], enabled: value.enabled };
25
+ }
26
+ class AcpConnections {
27
+ constructor(publish, home = CODEFLOW_HOME) {
28
+ this.publish = publish;
29
+ this.home = home;
30
+ }
31
+ document = { version: 1, revision: 0, connections: [] };
32
+ queue = Promise.resolve();
33
+ ready = false;
34
+ snapshot() {
35
+ return structuredClone(this.document);
36
+ }
37
+ activate(document) {
38
+ this.publish(document.connections.filter((connection) => connection.enabled));
39
+ liveBindings.clear();
40
+ for (const connection of document.connections.filter((connection2) => connection2.enabled)) liveBindings.set(connection.id, { id: connection.id, fingerprint: fingerprint(connection) });
41
+ this.document = document;
42
+ }
43
+ async load() {
44
+ try {
45
+ const file = await open(join(this.home, "acp-connections.json"), constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
46
+ let raw;
47
+ try {
48
+ const info = await file.stat();
49
+ if (!info.isFile() || info.size > 256 * 1024) throw new Error("ACP connections must be a regular file under 256 KiB");
50
+ const buffer = Buffer.alloc(256 * 1024 + 1);
51
+ const { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
52
+ if (bytesRead > 256 * 1024) throw new Error("ACP connections exceed 256 KiB");
53
+ raw = JSON.parse(buffer.subarray(0, bytesRead).toString("utf8"));
54
+ } finally {
55
+ await file.close();
56
+ }
57
+ if (!object(raw) || raw.version !== 1 || !Number.isSafeInteger(raw.revision) || raw.revision < 1 || !Array.isArray(raw.connections) || raw.connections.length > 16) throw new Error("Invalid ACP connection settings");
58
+ const connections = raw.connections.map(validateAcpConnection);
59
+ if (new Set(connections.map((connection) => connection.id)).size !== connections.length) throw new Error("Duplicate ACP connection IDs");
60
+ this.activate({ version: 1, revision: raw.revision, connections });
61
+ } catch (error) {
62
+ if (error.code !== "ENOENT") throw error;
63
+ this.activate(this.document);
64
+ }
65
+ this.ready = true;
66
+ }
67
+ save(revision, value) {
68
+ const connection = validateAcpConnection(value);
69
+ return this.change(revision, (connections) => [...connections.filter((item) => item.id !== connection.id), connection]);
70
+ }
71
+ remove(revision, id) {
72
+ return this.change(revision, (connections) => {
73
+ if (!connections.some((connection) => connection.id === id)) throw new Error("ACP connection no longer exists");
74
+ return connections.filter((connection) => connection.id !== id);
75
+ });
76
+ }
77
+ change(revision, update) {
78
+ const work = this.queue.then(async () => {
79
+ if (!this.ready) throw new Error("Repair ACP connection storage and restart before changing connections");
80
+ if (revision !== this.document.revision) throw new Error("ACP connections changed in another window. Reload connections before saving.");
81
+ const next = { version: 1, revision: this.document.revision + 1, connections: update(this.snapshot().connections) };
82
+ const json = JSON.stringify(next, null, 2);
83
+ if (next.connections.length > 16 || Buffer.byteLength(json) > 256 * 1024) throw new Error("Keep at most 16 ACP connections within 256 KiB");
84
+ await mkdir(this.home, { recursive: true, mode: 448 });
85
+ const temporary = join(this.home, `.acp-connections-${randomUUID()}.tmp`);
86
+ try {
87
+ const file = await open(temporary, "wx", 384);
88
+ try {
89
+ await file.writeFile(json);
90
+ await file.sync();
91
+ } finally {
92
+ await file.close();
93
+ }
94
+ await rename(temporary, join(this.home, "acp-connections.json"));
95
+ } finally {
96
+ await unlink(temporary).catch(() => void 0);
97
+ }
98
+ this.activate(next);
99
+ return this.snapshot();
100
+ });
101
+ this.queue = work.catch(() => void 0);
102
+ return work;
103
+ }
104
+ }
105
+ export {
106
+ AcpConnections,
107
+ acpBindings,
108
+ assertAcpBindings,
109
+ validateAcpConnection
110
+ };
@@ -0,0 +1,37 @@
1
+ const acpPresets = [
2
+ {
3
+ key: "opencode",
4
+ name: "OpenCode",
5
+ command: "opencode",
6
+ args: ["acp"],
7
+ install: "npm install -g opencode-ai",
8
+ login: "opencode auth login",
9
+ docs: "https://opencode.ai/docs/acp/"
10
+ },
11
+ {
12
+ key: "kilo",
13
+ name: "Kilo",
14
+ command: "kilo",
15
+ args: ["acp"],
16
+ install: "npm install -g @kilocode/cli",
17
+ login: "kilo auth login",
18
+ docs: "https://kilo.ai/docs/code-with-ai/platforms/cli-reference"
19
+ },
20
+ {
21
+ key: "cline",
22
+ name: "Cline",
23
+ command: "cline",
24
+ args: ["--acp"],
25
+ install: "npm install -g cline",
26
+ login: "cline auth",
27
+ docs: "https://docs.cline.bot/usage/acp",
28
+ help: "Cline asks before edits and commands by default. Enable Ask me for tool permissions in the run\u2019s permissions controls to approve these requests."
29
+ }
30
+ ];
31
+ function acpPresetConnection(preset) {
32
+ return { id: `acp:${preset.key}`, name: preset.name, command: preset.command, args: [...preset.args], enabled: true };
33
+ }
34
+ export {
35
+ acpPresetConnection,
36
+ acpPresets
37
+ };
File without changes