@av-pi-studio/server 0.0.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 (315) hide show
  1. package/README.md +411 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/agent/agent-manager.d.ts +107 -0
  4. package/dist/agent/agent-manager.d.ts.map +1 -0
  5. package/dist/agent/agent-manager.js +218 -0
  6. package/dist/agent/agent-manager.js.map +1 -0
  7. package/dist/agent/agent-service.d.ts +36 -0
  8. package/dist/agent/agent-service.d.ts.map +1 -0
  9. package/dist/agent/agent-service.js +195 -0
  10. package/dist/agent/agent-service.js.map +1 -0
  11. package/dist/agent/index.d.ts +17 -0
  12. package/dist/agent/index.d.ts.map +1 -0
  13. package/dist/agent/index.js +18 -0
  14. package/dist/agent/index.js.map +1 -0
  15. package/dist/agent/manifest.d.ts +7 -0
  16. package/dist/agent/manifest.d.ts.map +1 -0
  17. package/dist/agent/manifest.js +27 -0
  18. package/dist/agent/manifest.js.map +1 -0
  19. package/dist/agent/mcp-server.d.ts +100 -0
  20. package/dist/agent/mcp-server.d.ts.map +1 -0
  21. package/dist/agent/mcp-server.js +163 -0
  22. package/dist/agent/mcp-server.js.map +1 -0
  23. package/dist/agent/permissions.d.ts +71 -0
  24. package/dist/agent/permissions.d.ts.map +1 -0
  25. package/dist/agent/permissions.js +132 -0
  26. package/dist/agent/permissions.js.map +1 -0
  27. package/dist/agent/provider-contract.d.ts +134 -0
  28. package/dist/agent/provider-contract.d.ts.map +1 -0
  29. package/dist/agent/provider-contract.js +2 -0
  30. package/dist/agent/provider-contract.js.map +1 -0
  31. package/dist/agent/provider-registry.d.ts +37 -0
  32. package/dist/agent/provider-registry.d.ts.map +1 -0
  33. package/dist/agent/provider-registry.js +92 -0
  34. package/dist/agent/provider-registry.js.map +1 -0
  35. package/dist/agent/provider-snapshot.d.ts +52 -0
  36. package/dist/agent/provider-snapshot.d.ts.map +1 -0
  37. package/dist/agent/provider-snapshot.js +73 -0
  38. package/dist/agent/provider-snapshot.js.map +1 -0
  39. package/dist/agent/providers/mock/mock-provider.d.ts +44 -0
  40. package/dist/agent/providers/mock/mock-provider.d.ts.map +1 -0
  41. package/dist/agent/providers/mock/mock-provider.js +162 -0
  42. package/dist/agent/providers/mock/mock-provider.js.map +1 -0
  43. package/dist/agent/providers/pi/agent.d.ts +80 -0
  44. package/dist/agent/providers/pi/agent.d.ts.map +1 -0
  45. package/dist/agent/providers/pi/agent.js +322 -0
  46. package/dist/agent/providers/pi/agent.js.map +1 -0
  47. package/dist/agent/providers/pi/event-mapper.d.ts +17 -0
  48. package/dist/agent/providers/pi/event-mapper.d.ts.map +1 -0
  49. package/dist/agent/providers/pi/event-mapper.js +150 -0
  50. package/dist/agent/providers/pi/event-mapper.js.map +1 -0
  51. package/dist/agent/providers/pi/rpc-transport.d.ts +53 -0
  52. package/dist/agent/providers/pi/rpc-transport.d.ts.map +1 -0
  53. package/dist/agent/providers/pi/rpc-transport.js +190 -0
  54. package/dist/agent/providers/pi/rpc-transport.js.map +1 -0
  55. package/dist/agent/providers/pi/session-hydration.d.ts +11 -0
  56. package/dist/agent/providers/pi/session-hydration.d.ts.map +1 -0
  57. package/dist/agent/providers/pi/session-hydration.js +167 -0
  58. package/dist/agent/providers/pi/session-hydration.js.map +1 -0
  59. package/dist/agent/rewind-rpc.d.ts +15 -0
  60. package/dist/agent/rewind-rpc.d.ts.map +1 -0
  61. package/dist/agent/rewind-rpc.js +30 -0
  62. package/dist/agent/rewind-rpc.js.map +1 -0
  63. package/dist/agent/session-operations.d.ts +32 -0
  64. package/dist/agent/session-operations.d.ts.map +1 -0
  65. package/dist/agent/session-operations.js +130 -0
  66. package/dist/agent/session-operations.js.map +1 -0
  67. package/dist/agent/structured-generation.d.ts +39 -0
  68. package/dist/agent/structured-generation.d.ts.map +1 -0
  69. package/dist/agent/structured-generation.js +78 -0
  70. package/dist/agent/structured-generation.js.map +1 -0
  71. package/dist/agent/timeline-rpc.d.ts +21 -0
  72. package/dist/agent/timeline-rpc.d.ts.map +1 -0
  73. package/dist/agent/timeline-rpc.js +59 -0
  74. package/dist/agent/timeline-rpc.js.map +1 -0
  75. package/dist/agent/timeline-store.d.ts +100 -0
  76. package/dist/agent/timeline-store.d.ts.map +1 -0
  77. package/dist/agent/timeline-store.js +255 -0
  78. package/dist/agent/timeline-store.js.map +1 -0
  79. package/dist/auth/index.d.ts +2 -0
  80. package/dist/auth/index.d.ts.map +1 -0
  81. package/dist/auth/index.js +3 -0
  82. package/dist/auth/index.js.map +1 -0
  83. package/dist/auth/password-auth.d.ts +44 -0
  84. package/dist/auth/password-auth.d.ts.map +1 -0
  85. package/dist/auth/password-auth.js +78 -0
  86. package/dist/auth/password-auth.js.map +1 -0
  87. package/dist/config/daemon-config.d.ts +634 -0
  88. package/dist/config/daemon-config.d.ts.map +1 -0
  89. package/dist/config/daemon-config.js +258 -0
  90. package/dist/config/daemon-config.js.map +1 -0
  91. package/dist/config/index.d.ts +3 -0
  92. package/dist/config/index.d.ts.map +1 -0
  93. package/dist/config/index.js +4 -0
  94. package/dist/config/index.js.map +1 -0
  95. package/dist/config/project-config.d.ts +104 -0
  96. package/dist/config/project-config.d.ts.map +1 -0
  97. package/dist/config/project-config.js +106 -0
  98. package/dist/config/project-config.js.map +1 -0
  99. package/dist/daemon/bootstrap.d.ts +37 -0
  100. package/dist/daemon/bootstrap.d.ts.map +1 -0
  101. package/dist/daemon/bootstrap.js +448 -0
  102. package/dist/daemon/bootstrap.js.map +1 -0
  103. package/dist/daemon/dev-bootstrap.d.ts +14 -0
  104. package/dist/daemon/dev-bootstrap.d.ts.map +1 -0
  105. package/dist/daemon/dev-bootstrap.js +231 -0
  106. package/dist/daemon/dev-bootstrap.js.map +1 -0
  107. package/dist/daemon/dev-main.d.ts +3 -0
  108. package/dist/daemon/dev-main.d.ts.map +1 -0
  109. package/dist/daemon/dev-main.js +36 -0
  110. package/dist/daemon/dev-main.js.map +1 -0
  111. package/dist/daemon/index.d.ts +15 -0
  112. package/dist/daemon/index.d.ts.map +1 -0
  113. package/dist/daemon/index.js +21 -0
  114. package/dist/daemon/index.js.map +1 -0
  115. package/dist/daemon/main.d.ts +3 -0
  116. package/dist/daemon/main.d.ts.map +1 -0
  117. package/dist/daemon/main.js +29 -0
  118. package/dist/daemon/main.js.map +1 -0
  119. package/dist/daemon/orchestration-rpc.d.ts +19 -0
  120. package/dist/daemon/orchestration-rpc.d.ts.map +1 -0
  121. package/dist/daemon/orchestration-rpc.js +136 -0
  122. package/dist/daemon/orchestration-rpc.js.map +1 -0
  123. package/dist/files/download-token-store.d.ts +14 -0
  124. package/dist/files/download-token-store.d.ts.map +1 -0
  125. package/dist/files/download-token-store.js +33 -0
  126. package/dist/files/download-token-store.js.map +1 -0
  127. package/dist/files/file-explorer.d.ts +54 -0
  128. package/dist/files/file-explorer.d.ts.map +1 -0
  129. package/dist/files/file-explorer.js +182 -0
  130. package/dist/files/file-explorer.js.map +1 -0
  131. package/dist/files/file-transfer.d.ts +30 -0
  132. package/dist/files/file-transfer.d.ts.map +1 -0
  133. package/dist/files/file-transfer.js +132 -0
  134. package/dist/files/file-transfer.js.map +1 -0
  135. package/dist/files/index.d.ts +4 -0
  136. package/dist/files/index.d.ts.map +1 -0
  137. package/dist/files/index.js +5 -0
  138. package/dist/files/index.js.map +1 -0
  139. package/dist/http/host-allowlist.d.ts +15 -0
  140. package/dist/http/host-allowlist.d.ts.map +1 -0
  141. package/dist/http/host-allowlist.js +52 -0
  142. package/dist/http/host-allowlist.js.map +1 -0
  143. package/dist/http/http-server.d.ts +27 -0
  144. package/dist/http/http-server.d.ts.map +1 -0
  145. package/dist/http/http-server.js +76 -0
  146. package/dist/http/http-server.js.map +1 -0
  147. package/dist/http/index.d.ts +3 -0
  148. package/dist/http/index.d.ts.map +1 -0
  149. package/dist/http/index.js +4 -0
  150. package/dist/http/index.js.map +1 -0
  151. package/dist/index.d.ts +18 -0
  152. package/dist/index.d.ts.map +1 -0
  153. package/dist/index.js +35 -0
  154. package/dist/index.js.map +1 -0
  155. package/dist/logging/index.d.ts +2 -0
  156. package/dist/logging/index.d.ts.map +1 -0
  157. package/dist/logging/index.js +2 -0
  158. package/dist/logging/index.js.map +1 -0
  159. package/dist/logging/logger.d.ts +48 -0
  160. package/dist/logging/logger.d.ts.map +1 -0
  161. package/dist/logging/logger.js +53 -0
  162. package/dist/logging/logger.js.map +1 -0
  163. package/dist/orchestration/chat-service.d.ts +61 -0
  164. package/dist/orchestration/chat-service.d.ts.map +1 -0
  165. package/dist/orchestration/chat-service.js +156 -0
  166. package/dist/orchestration/chat-service.js.map +1 -0
  167. package/dist/orchestration/cron.d.ts +30 -0
  168. package/dist/orchestration/cron.d.ts.map +1 -0
  169. package/dist/orchestration/cron.js +136 -0
  170. package/dist/orchestration/cron.js.map +1 -0
  171. package/dist/orchestration/index.d.ts +5 -0
  172. package/dist/orchestration/index.d.ts.map +1 -0
  173. package/dist/orchestration/index.js +6 -0
  174. package/dist/orchestration/index.js.map +1 -0
  175. package/dist/orchestration/loop-service.d.ts +129 -0
  176. package/dist/orchestration/loop-service.d.ts.map +1 -0
  177. package/dist/orchestration/loop-service.js +234 -0
  178. package/dist/orchestration/loop-service.js.map +1 -0
  179. package/dist/orchestration/schedule-service.d.ts +65 -0
  180. package/dist/orchestration/schedule-service.d.ts.map +1 -0
  181. package/dist/orchestration/schedule-service.js +201 -0
  182. package/dist/orchestration/schedule-service.js.map +1 -0
  183. package/dist/persistence/atomic-store.d.ts +45 -0
  184. package/dist/persistence/atomic-store.d.ts.map +1 -0
  185. package/dist/persistence/atomic-store.js +92 -0
  186. package/dist/persistence/atomic-store.js.map +1 -0
  187. package/dist/persistence/entity-schemas.d.ts +1707 -0
  188. package/dist/persistence/entity-schemas.d.ts.map +1 -0
  189. package/dist/persistence/entity-schemas.js +250 -0
  190. package/dist/persistence/entity-schemas.js.map +1 -0
  191. package/dist/persistence/entity-stores.d.ts +46 -0
  192. package/dist/persistence/entity-stores.d.ts.map +1 -0
  193. package/dist/persistence/entity-stores.js +167 -0
  194. package/dist/persistence/entity-stores.js.map +1 -0
  195. package/dist/persistence/index.d.ts +4 -0
  196. package/dist/persistence/index.d.ts.map +1 -0
  197. package/dist/persistence/index.js +5 -0
  198. package/dist/persistence/index.js.map +1 -0
  199. package/dist/projects/checkout-diff-manager.d.ts +44 -0
  200. package/dist/projects/checkout-diff-manager.d.ts.map +1 -0
  201. package/dist/projects/checkout-diff-manager.js +82 -0
  202. package/dist/projects/checkout-diff-manager.js.map +1 -0
  203. package/dist/projects/git-checkout-rpc.d.ts +18 -0
  204. package/dist/projects/git-checkout-rpc.d.ts.map +1 -0
  205. package/dist/projects/git-checkout-rpc.js +57 -0
  206. package/dist/projects/git-checkout-rpc.js.map +1 -0
  207. package/dist/projects/git-detect.d.ts +21 -0
  208. package/dist/projects/git-detect.d.ts.map +1 -0
  209. package/dist/projects/git-detect.js +34 -0
  210. package/dist/projects/git-detect.js.map +1 -0
  211. package/dist/projects/git-operations.d.ts +67 -0
  212. package/dist/projects/git-operations.d.ts.map +1 -0
  213. package/dist/projects/git-operations.js +200 -0
  214. package/dist/projects/git-operations.js.map +1 -0
  215. package/dist/projects/github-service.d.ts +80 -0
  216. package/dist/projects/github-service.d.ts.map +1 -0
  217. package/dist/projects/github-service.js +172 -0
  218. package/dist/projects/github-service.js.map +1 -0
  219. package/dist/projects/index.d.ts +13 -0
  220. package/dist/projects/index.d.ts.map +1 -0
  221. package/dist/projects/index.js +19 -0
  222. package/dist/projects/index.js.map +1 -0
  223. package/dist/projects/open-project.d.ts +35 -0
  224. package/dist/projects/open-project.d.ts.map +1 -0
  225. package/dist/projects/open-project.js +106 -0
  226. package/dist/projects/open-project.js.map +1 -0
  227. package/dist/projects/reconciliation.d.ts +15 -0
  228. package/dist/projects/reconciliation.d.ts.map +1 -0
  229. package/dist/projects/reconciliation.js +63 -0
  230. package/dist/projects/reconciliation.js.map +1 -0
  231. package/dist/projects/status-projection.d.ts +33 -0
  232. package/dist/projects/status-projection.d.ts.map +1 -0
  233. package/dist/projects/status-projection.js +98 -0
  234. package/dist/projects/status-projection.js.map +1 -0
  235. package/dist/projects/workspace-activity.d.ts +28 -0
  236. package/dist/projects/workspace-activity.d.ts.map +1 -0
  237. package/dist/projects/workspace-activity.js +74 -0
  238. package/dist/projects/workspace-activity.js.map +1 -0
  239. package/dist/projects/workspace-git-service.d.ts +27 -0
  240. package/dist/projects/workspace-git-service.d.ts.map +1 -0
  241. package/dist/projects/workspace-git-service.js +60 -0
  242. package/dist/projects/workspace-git-service.js.map +1 -0
  243. package/dist/projects/workspace-registry.d.ts +59 -0
  244. package/dist/projects/workspace-registry.d.ts.map +1 -0
  245. package/dist/projects/workspace-registry.js +149 -0
  246. package/dist/projects/workspace-registry.js.map +1 -0
  247. package/dist/projects/worktree-service.d.ts +100 -0
  248. package/dist/projects/worktree-service.d.ts.map +1 -0
  249. package/dist/projects/worktree-service.js +216 -0
  250. package/dist/projects/worktree-service.js.map +1 -0
  251. package/dist/proxy/index.d.ts +4 -0
  252. package/dist/proxy/index.d.ts.map +1 -0
  253. package/dist/proxy/index.js +5 -0
  254. package/dist/proxy/index.js.map +1 -0
  255. package/dist/proxy/service-hostname.d.ts +21 -0
  256. package/dist/proxy/service-hostname.d.ts.map +1 -0
  257. package/dist/proxy/service-hostname.js +44 -0
  258. package/dist/proxy/service-hostname.js.map +1 -0
  259. package/dist/proxy/service-port-registry.d.ts +36 -0
  260. package/dist/proxy/service-port-registry.d.ts.map +1 -0
  261. package/dist/proxy/service-port-registry.js +55 -0
  262. package/dist/proxy/service-port-registry.js.map +1 -0
  263. package/dist/proxy/service-proxy.d.ts +34 -0
  264. package/dist/proxy/service-proxy.d.ts.map +1 -0
  265. package/dist/proxy/service-proxy.js +54 -0
  266. package/dist/proxy/service-proxy.js.map +1 -0
  267. package/dist/terminal/index.d.ts +4 -0
  268. package/dist/terminal/index.d.ts.map +1 -0
  269. package/dist/terminal/index.js +5 -0
  270. package/dist/terminal/index.js.map +1 -0
  271. package/dist/terminal/pty-backend.d.ts +44 -0
  272. package/dist/terminal/pty-backend.d.ts.map +1 -0
  273. package/dist/terminal/pty-backend.js +119 -0
  274. package/dist/terminal/pty-backend.js.map +1 -0
  275. package/dist/terminal/screen-buffer.d.ts +21 -0
  276. package/dist/terminal/screen-buffer.d.ts.map +1 -0
  277. package/dist/terminal/screen-buffer.js +55 -0
  278. package/dist/terminal/screen-buffer.js.map +1 -0
  279. package/dist/terminal/terminal-manager.d.ts +79 -0
  280. package/dist/terminal/terminal-manager.d.ts.map +1 -0
  281. package/dist/terminal/terminal-manager.js +174 -0
  282. package/dist/terminal/terminal-manager.js.map +1 -0
  283. package/dist/terminal/terminal-rpc.d.ts +20 -0
  284. package/dist/terminal/terminal-rpc.d.ts.map +1 -0
  285. package/dist/terminal/terminal-rpc.js +122 -0
  286. package/dist/terminal/terminal-rpc.js.map +1 -0
  287. package/dist/util/concurrency.d.ts +16 -0
  288. package/dist/util/concurrency.d.ts.map +1 -0
  289. package/dist/util/concurrency.js +23 -0
  290. package/dist/util/concurrency.js.map +1 -0
  291. package/dist/util/index.d.ts +2 -0
  292. package/dist/util/index.d.ts.map +1 -0
  293. package/dist/util/index.js +2 -0
  294. package/dist/util/index.js.map +1 -0
  295. package/dist/ws/capability-store.d.ts +11 -0
  296. package/dist/ws/capability-store.d.ts.map +1 -0
  297. package/dist/ws/capability-store.js +10 -0
  298. package/dist/ws/capability-store.js.map +1 -0
  299. package/dist/ws/index.d.ts +5 -0
  300. package/dist/ws/index.d.ts.map +1 -0
  301. package/dist/ws/index.js +6 -0
  302. package/dist/ws/index.js.map +1 -0
  303. package/dist/ws/router.d.ts +34 -0
  304. package/dist/ws/router.d.ts.map +1 -0
  305. package/dist/ws/router.js +97 -0
  306. package/dist/ws/router.js.map +1 -0
  307. package/dist/ws/session.d.ts +28 -0
  308. package/dist/ws/session.d.ts.map +1 -0
  309. package/dist/ws/session.js +36 -0
  310. package/dist/ws/session.js.map +1 -0
  311. package/dist/ws/ws-server.d.ts +41 -0
  312. package/dist/ws/ws-server.d.ts.map +1 -0
  313. package/dist/ws/ws-server.js +104 -0
  314. package/dist/ws/ws-server.js.map +1 -0
  315. package/package.json +48 -0
@@ -0,0 +1,119 @@
1
+ import { spawn } from "node:child_process";
2
+ import nodePty from "node-pty";
3
+ import treeKill from "tree-kill";
4
+ import which from "which";
5
+ const encoder = new TextEncoder();
6
+ /**
7
+ * Resolve a shell/binary name to an absolute path via `$PATH` (cross-platform, honors Windows
8
+ * PATHEXT). Returns the input unchanged when it already looks like a path or cannot be resolved
9
+ * (the spawn will then surface a clear ENOENT).
10
+ */
11
+ export function resolveExecutable(bin, env = process.env) {
12
+ if (bin.includes("/") || bin.includes("\\"))
13
+ return bin;
14
+ const resolved = which.sync(bin, { nothrow: true, path: env.PATH ?? env.Path });
15
+ return resolved ?? bin;
16
+ }
17
+ /** Best-effort termination of a whole process tree (kills orphaned grandchildren too). */
18
+ function killTree(pid, signal = "SIGTERM") {
19
+ if (pid === undefined)
20
+ return;
21
+ treeKill(pid, signal, () => {
22
+ // Best-effort: ignore errors (process may already be gone).
23
+ });
24
+ }
25
+ /** Production backend: a real PTY via `node-pty`. */
26
+ export class NodePtyBackend {
27
+ spawn(opts) {
28
+ const shell = resolveExecutable(opts.shell, opts.env ?? process.env);
29
+ const proc = nodePty.spawn(shell, opts.args ?? [], {
30
+ name: "xterm-256color",
31
+ cols: opts.cols,
32
+ rows: opts.rows,
33
+ cwd: opts.cwd,
34
+ env: { ...process.env, ...opts.env },
35
+ });
36
+ return {
37
+ write: (data) => proc.write(Buffer.from(data).toString("utf8")),
38
+ resize: (cols, rows) => {
39
+ try {
40
+ proc.resize(cols, rows);
41
+ }
42
+ catch {
43
+ // PTY may have exited between the size change and this call; ignore.
44
+ }
45
+ },
46
+ kill: () => {
47
+ killTree(proc.pid);
48
+ try {
49
+ proc.kill();
50
+ }
51
+ catch {
52
+ // Already exited.
53
+ }
54
+ },
55
+ onData: (cb) => {
56
+ proc.onData((s) => cb(encoder.encode(s)));
57
+ },
58
+ onExit: (cb) => {
59
+ proc.onExit(({ exitCode }) => cb(exitCode));
60
+ },
61
+ };
62
+ }
63
+ }
64
+ /** Fallback backend: a piped child process. Not a real TTY, but streams stdout/stderr ⇄ stdin. */
65
+ export class ChildProcessPtyBackend {
66
+ spawn(opts) {
67
+ const child = spawn(resolveExecutable(opts.shell, opts.env ?? process.env), opts.args ?? [], {
68
+ cwd: opts.cwd,
69
+ env: { ...process.env, ...opts.env },
70
+ windowsHide: true,
71
+ });
72
+ const dataCbs = [];
73
+ const exitCbs = [];
74
+ const emit = (chunk) => {
75
+ const bytes = new Uint8Array(chunk);
76
+ for (const cb of dataCbs)
77
+ cb(bytes);
78
+ };
79
+ child.stdout?.on("data", emit);
80
+ child.stderr?.on("data", emit);
81
+ child.on("exit", (code) => {
82
+ for (const cb of exitCbs)
83
+ cb(code);
84
+ });
85
+ return {
86
+ write: (data) => {
87
+ child.stdin?.write(Buffer.from(data));
88
+ },
89
+ resize: () => {
90
+ // No-op for piped child processes (no TTY); the node-pty backend calls `pty.resize`.
91
+ },
92
+ kill: () => {
93
+ killTree(child.pid);
94
+ child.kill();
95
+ },
96
+ onData: (cb) => {
97
+ dataCbs.push(cb);
98
+ },
99
+ onExit: (cb) => {
100
+ exitCbs.push(cb);
101
+ },
102
+ };
103
+ }
104
+ }
105
+ /**
106
+ * Default backend factory: prefer the real PTY (`node-pty`); if the native module cannot be loaded
107
+ * on this platform, fall back to the piped child-process backend.
108
+ */
109
+ export function createDefaultPtyBackend() {
110
+ try {
111
+ // Touch the native binding so an unsupported platform fails here, not on first spawn.
112
+ void nodePty.spawn;
113
+ return new NodePtyBackend();
114
+ }
115
+ catch {
116
+ return new ChildProcessPtyBackend();
117
+ }
118
+ }
119
+ //# sourceMappingURL=pty-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-backend.js","sourceRoot":"","sources":["../../src/terminal/pty-backend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Server-side terminal screen model backed by `@xterm/headless` (features/terminals.md § capture).
3
+ *
4
+ * The binary Snapshot frame sent to subscribing clients is the raw byte ring (each client's own
5
+ * xterm replays it faithfully). But CLI/MCP `capture()` needs the *visible grid as text* — replaying
6
+ * raw bytes through `strip-ansi` is wrong for anything that moves the cursor, clears the screen, or
7
+ * redraws (progress bars, full-screen apps). Feeding the byte stream through a headless terminal and
8
+ * reading the grid yields the true on-screen text.
9
+ */
10
+ export declare class ScreenBuffer {
11
+ private readonly term;
12
+ constructor(cols: number, rows: number, scrollback?: number);
13
+ write(data: Uint8Array): void;
14
+ resize(cols: number, rows: number): void;
15
+ /** Resolve once the parser has drained all pending writes (used by tests / before capture). */
16
+ flush(): Promise<void>;
17
+ /** The visible viewport as plain text, with trailing blank lines trimmed. */
18
+ snapshotText(): string;
19
+ dispose(): void;
20
+ }
21
+ //# sourceMappingURL=screen-buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen-buffer.d.ts","sourceRoot":"","sources":["../../src/terminal/screen-buffer.ts"],"names":[],"mappings":"AAWA;;;;;;;;GAQG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;gBAE5B,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,SAAO;IAIzD,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAI7B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQxC,+FAA+F;IAC/F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,6EAA6E;IAC7E,YAAY,IAAI,MAAM;IAatB,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,55 @@
1
+ import { createRequire } from "node:module";
2
+ import stripAnsi from "strip-ansi";
3
+ // `@xterm/headless` ships a UMD bundle whose `module.exports` Node's ESM loader cannot statically
4
+ // read, so a named `import { Terminal }` resolves at type-check time but throws at runtime. Load it
5
+ // through `createRequire` (CJS) to get the real `Terminal` constructor.
6
+ const require = createRequire(import.meta.url);
7
+ const { Terminal } = require("@xterm/headless");
8
+ /**
9
+ * Server-side terminal screen model backed by `@xterm/headless` (features/terminals.md § capture).
10
+ *
11
+ * The binary Snapshot frame sent to subscribing clients is the raw byte ring (each client's own
12
+ * xterm replays it faithfully). But CLI/MCP `capture()` needs the *visible grid as text* — replaying
13
+ * raw bytes through `strip-ansi` is wrong for anything that moves the cursor, clears the screen, or
14
+ * redraws (progress bars, full-screen apps). Feeding the byte stream through a headless terminal and
15
+ * reading the grid yields the true on-screen text.
16
+ */
17
+ export class ScreenBuffer {
18
+ term;
19
+ constructor(cols, rows, scrollback = 1000) {
20
+ this.term = new Terminal({ cols, rows, scrollback, allowProposedApi: true });
21
+ }
22
+ write(data) {
23
+ this.term.write(Buffer.from(data));
24
+ }
25
+ resize(cols, rows) {
26
+ try {
27
+ this.term.resize(cols, rows);
28
+ }
29
+ catch {
30
+ // Ignore invalid dimensions (e.g. 0); the live PTY resize already guards this.
31
+ }
32
+ }
33
+ /** Resolve once the parser has drained all pending writes (used by tests / before capture). */
34
+ flush() {
35
+ return new Promise((resolve) => this.term.write("", () => resolve()));
36
+ }
37
+ /** The visible viewport as plain text, with trailing blank lines trimmed. */
38
+ snapshotText() {
39
+ const buf = this.term.buffer.active;
40
+ const top = buf.baseY;
41
+ const lines = [];
42
+ for (let y = top; y < top + this.term.rows; y++) {
43
+ const line = buf.getLine(y);
44
+ lines.push(line ? line.translateToString(true) : "");
45
+ }
46
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "")
47
+ lines.pop();
48
+ // translateToString already yields plain text; strip-ansi defends against any passthrough.
49
+ return stripAnsi(lines.join("\n"));
50
+ }
51
+ dispose() {
52
+ this.term.dispose();
53
+ }
54
+ }
55
+ //# sourceMappingURL=screen-buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen-buffer.js","sourceRoot":"","sources":["../../src/terminal/screen-buffer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,kGAAkG;AAClG,oGAAoG;AACpG,wEAAwE;AACxE,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAqC,CAAC;AAGpF;;;;;;;;GAQG;AACH,MAAM,OAAO,YAAY;IACN,IAAI,CAAmB;IAExC,YAAY,IAAY,EAAE,IAAY,EAAE,UAAU,GAAG,IAAI;QACvD,IAAI,CAAC,IAAI,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,IAAgB;QACpB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,+EAA+E;QACjF,CAAC;IACH,CAAC;IAED,+FAA+F;IAC/F,KAAK;QACH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,6EAA6E;IAC7E,YAAY;QACV,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;QACpC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC;QACtB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,KAAK,CAAC,GAAG,EAAE,CAAC;QAC/E,2FAA2F;QAC3F,OAAO,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;CACF"}
@@ -0,0 +1,79 @@
1
+ import { type PtyBackend } from "./pty-backend.js";
2
+ /**
3
+ * Workspace-scoped PTY terminal manager (features/terminals.md § Behavior, § PTY size ownership,
4
+ * § Output coalescing). PTYs are multiplexed over the binary terminal stream protocol by `slot`.
5
+ *
6
+ * Size ownership is **last-interacting-client-wins** and is a client concern: the manager only
7
+ * resizes the PTY when a client explicitly sends a Resize (genuine viewport change / focus). It
8
+ * NEVER resizes on subscribe/attach, and never broadcasts resize ownership — a resized PTY simply
9
+ * redraws via normal Output, which every attached client renders in its own viewport.
10
+ */
11
+ /** A subscriber sink receives fully-encoded binary terminal frames. */
12
+ export type TerminalFrameSink = (frame: Uint8Array) => void;
13
+ export interface TerminalRuntimeEntry {
14
+ slot: number;
15
+ workspaceId: string;
16
+ name: string;
17
+ cwd?: string;
18
+ shell: string;
19
+ cols: number;
20
+ rows: number;
21
+ /** `true` once the PTY has exited. */
22
+ closed: boolean;
23
+ /** Marks a service-script-backed terminal (handed to the service proxy elsewhere). */
24
+ service?: boolean;
25
+ }
26
+ export interface CreateTerminalOptions {
27
+ workspaceId: string;
28
+ cwd?: string;
29
+ shell?: string;
30
+ args?: string[];
31
+ name?: string;
32
+ cols?: number;
33
+ rows?: number;
34
+ env?: Record<string, string>;
35
+ service?: boolean;
36
+ }
37
+ export interface TerminalManagerOptions {
38
+ backend?: PtyBackend;
39
+ /** Output coalescing window in ms (batch before broadcast). Default 4ms. */
40
+ coalesceMs?: number;
41
+ /** Max bytes retained as the transient "screen" snapshot. Default 64KiB. */
42
+ snapshotBytes?: number;
43
+ defaultShell?: string;
44
+ }
45
+ export declare class TerminalManager {
46
+ private readonly backend;
47
+ private readonly coalesceMs;
48
+ private readonly snapshotBytes;
49
+ private readonly defaultShell;
50
+ private readonly terminals;
51
+ private nextSlot;
52
+ constructor(options?: TerminalManagerOptions);
53
+ /** All live terminal runtime entries. */
54
+ list(): TerminalRuntimeEntry[];
55
+ get(slot: number): TerminalRuntimeEntry | undefined;
56
+ /** Spawn a PTY in the backend, assign a slot, and track the runtime entry. */
57
+ createTerminal(options: CreateTerminalOptions): TerminalRuntimeEntry;
58
+ /**
59
+ * Subscribe to a slot: emit a Snapshot frame (current screen) immediately, then live Output frames.
60
+ * Does NOT resize the PTY (passive attach must not claim size). Returns an unsubscribe fn.
61
+ */
62
+ subscribe(slot: number, sink: TerminalFrameSink): () => void;
63
+ rename(slot: number, name: string): boolean;
64
+ /** Forward input bytes to the PTY. */
65
+ input(slot: number, bytes: Uint8Array): boolean;
66
+ /**
67
+ * Explicit, client-initiated resize (genuine viewport change / focus). The only path that claims
68
+ * PTY size. Never called from subscribe/attach.
69
+ */
70
+ resize(slot: number, cols: number, rows: number): boolean;
71
+ /** One-shot current screen text (for CLI/MCP capture). Does not subscribe. Screen-accurate. */
72
+ capture(slot: number): string | null;
73
+ /** Terminate the PTY and notify subscribers. */
74
+ kill(slot: number): boolean;
75
+ private onOutput;
76
+ private flush;
77
+ private onExit;
78
+ }
79
+ //# sourceMappingURL=terminal-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-manager.d.ts","sourceRoot":"","sources":["../../src/terminal/terminal-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,UAAU,EAGhB,MAAM,kBAAkB,CAAC;AAG1B;;;;;;;;GAQG;AAEH,uEAAuE;AACvE,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,sFAAsF;IACtF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAeD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,QAAQ,CAAK;gBAET,OAAO,GAAE,sBAA2B;IAOhD,yCAAyC;IACzC,IAAI,IAAI,oBAAoB,EAAE;IAI9B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS;IAInD,8EAA8E;IAC9E,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,oBAAoB;IA4CpE;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,IAAI;IAa5D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAO3C,sCAAsC;IACtC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO;IAO/C;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAUzD,+FAA+F;IAC/F,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAMpC,gDAAgD;IAChD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAU3B,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,MAAM;CAaf"}
@@ -0,0 +1,174 @@
1
+ import { encodeTerminalFrame } from "@av-pi-studio/protocol";
2
+ import { createDefaultPtyBackend, resolveExecutable, } from "./pty-backend.js";
3
+ import { ScreenBuffer } from "./screen-buffer.js";
4
+ export class TerminalManager {
5
+ backend;
6
+ coalesceMs;
7
+ snapshotBytes;
8
+ defaultShell;
9
+ terminals = new Map();
10
+ nextSlot = 1;
11
+ constructor(options = {}) {
12
+ this.backend = options.backend ?? createDefaultPtyBackend();
13
+ this.coalesceMs = options.coalesceMs ?? 4;
14
+ this.snapshotBytes = options.snapshotBytes ?? 64 * 1024;
15
+ this.defaultShell = resolveExecutable(options.defaultShell ?? process.env.SHELL ?? "/bin/sh");
16
+ }
17
+ /** All live terminal runtime entries. */
18
+ list() {
19
+ return [...this.terminals.values()].map((t) => t.entry);
20
+ }
21
+ get(slot) {
22
+ return this.terminals.get(slot)?.entry;
23
+ }
24
+ /** Spawn a PTY in the backend, assign a slot, and track the runtime entry. */
25
+ createTerminal(options) {
26
+ const slot = this.nextSlot++;
27
+ const cols = options.cols ?? 80;
28
+ const rows = options.rows ?? 24;
29
+ const shell = options.shell ?? this.defaultShell;
30
+ const pty = this.backend.spawn({
31
+ shell,
32
+ args: options.args,
33
+ cwd: options.cwd,
34
+ env: options.env,
35
+ cols,
36
+ rows,
37
+ });
38
+ const entry = {
39
+ slot,
40
+ workspaceId: options.workspaceId,
41
+ name: options.name ?? `terminal-${slot}`,
42
+ cwd: options.cwd,
43
+ shell,
44
+ cols,
45
+ rows,
46
+ closed: false,
47
+ service: options.service,
48
+ };
49
+ const managed = {
50
+ entry,
51
+ pty,
52
+ subscribers: new Set(),
53
+ screen: new Uint8Array(0),
54
+ screenModel: new ScreenBuffer(cols, rows),
55
+ pending: [],
56
+ flushTimer: null,
57
+ };
58
+ this.terminals.set(slot, managed);
59
+ pty.onData((data) => this.onOutput(managed, data));
60
+ pty.onExit(() => this.onExit(managed));
61
+ return entry;
62
+ }
63
+ /**
64
+ * Subscribe to a slot: emit a Snapshot frame (current screen) immediately, then live Output frames.
65
+ * Does NOT resize the PTY (passive attach must not claim size). Returns an unsubscribe fn.
66
+ */
67
+ subscribe(slot, sink) {
68
+ const managed = this.terminals.get(slot);
69
+ if (!managed)
70
+ throw new Error(`no terminal in slot ${slot}`);
71
+ // Snapshot first (rebuilds screen state), then live output.
72
+ sink(encodeTerminalFrame({ opcode: "Snapshot", slot, data: managed.screen.slice() }));
73
+ managed.subscribers.add(sink);
74
+ return () => {
75
+ managed.subscribers.delete(sink);
76
+ };
77
+ }
78
+ rename(slot, name) {
79
+ const managed = this.terminals.get(slot);
80
+ if (!managed)
81
+ return false;
82
+ managed.entry.name = name;
83
+ return true;
84
+ }
85
+ /** Forward input bytes to the PTY. */
86
+ input(slot, bytes) {
87
+ const managed = this.terminals.get(slot);
88
+ if (!managed || managed.entry.closed)
89
+ return false;
90
+ managed.pty.write(bytes);
91
+ return true;
92
+ }
93
+ /**
94
+ * Explicit, client-initiated resize (genuine viewport change / focus). The only path that claims
95
+ * PTY size. Never called from subscribe/attach.
96
+ */
97
+ resize(slot, cols, rows) {
98
+ const managed = this.terminals.get(slot);
99
+ if (!managed || managed.entry.closed)
100
+ return false;
101
+ managed.entry.cols = cols;
102
+ managed.entry.rows = rows;
103
+ managed.pty.resize(cols, rows);
104
+ managed.screenModel.resize(cols, rows);
105
+ return true;
106
+ }
107
+ /** One-shot current screen text (for CLI/MCP capture). Does not subscribe. Screen-accurate. */
108
+ capture(slot) {
109
+ const managed = this.terminals.get(slot);
110
+ if (!managed)
111
+ return null;
112
+ return managed.screenModel.snapshotText();
113
+ }
114
+ /** Terminate the PTY and notify subscribers. */
115
+ kill(slot) {
116
+ const managed = this.terminals.get(slot);
117
+ if (!managed)
118
+ return false;
119
+ managed.pty.kill();
120
+ this.onExit(managed);
121
+ return true;
122
+ }
123
+ // ─── Internals ───────────────────────────────────────────────────────────
124
+ onOutput(managed, data) {
125
+ // Append to the transient screen snapshot (bounded ring) for binary client replay.
126
+ managed.screen = appendBounded(managed.screen, data, this.snapshotBytes);
127
+ // Feed the headless grid for screen-accurate text capture.
128
+ managed.screenModel.write(data);
129
+ // Coalesce before broadcast.
130
+ managed.pending.push(data);
131
+ if (managed.flushTimer)
132
+ return;
133
+ managed.flushTimer = setTimeout(() => this.flush(managed), this.coalesceMs);
134
+ }
135
+ flush(managed) {
136
+ managed.flushTimer = null;
137
+ if (managed.pending.length === 0)
138
+ return;
139
+ const batch = concat(managed.pending);
140
+ managed.pending = [];
141
+ const frame = encodeTerminalFrame({ opcode: "Output", slot: managed.entry.slot, data: batch });
142
+ for (const sink of managed.subscribers)
143
+ sink(frame);
144
+ }
145
+ onExit(managed) {
146
+ if (managed.entry.closed)
147
+ return;
148
+ if (managed.flushTimer) {
149
+ clearTimeout(managed.flushTimer);
150
+ this.flush(managed);
151
+ }
152
+ managed.entry.closed = true;
153
+ managed.screenModel.dispose();
154
+ this.terminals.delete(managed.entry.slot);
155
+ // Notify subscribers the terminal closed (empty Output then drop). Clients treat an exited
156
+ // terminal as closed; no dedicated close opcode exists in the binary protocol.
157
+ managed.subscribers.clear();
158
+ }
159
+ }
160
+ function appendBounded(buffer, data, max) {
161
+ const combined = concat([buffer, data]);
162
+ return combined.length <= max ? combined : combined.slice(combined.length - max);
163
+ }
164
+ function concat(parts) {
165
+ const total = parts.reduce((n, p) => n + p.length, 0);
166
+ const out = new Uint8Array(total);
167
+ let offset = 0;
168
+ for (const part of parts) {
169
+ out.set(part, offset);
170
+ offset += part.length;
171
+ }
172
+ return out;
173
+ }
174
+ //# sourceMappingURL=terminal-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-manager.js","sourceRoot":"","sources":["../../src/terminal/terminal-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { Session } from "../ws/session.js";
2
+ import type { BinaryHandler, HandlerRegistry } from "../ws/router.js";
3
+ import type { TerminalManager } from "./terminal-manager.js";
4
+ /**
5
+ * Terminal control RPC surface + capture/restore semantics (features/terminals.md § Control RPCs,
6
+ * § Restore / snapshot). Per-slot binary streams are piped to the requesting session; restore modes
7
+ * are gated by `features["terminal-restore-modes"]`.
8
+ */
9
+ export interface TerminalRpcDeps {
10
+ manager: TerminalManager;
11
+ broadcast: (sessions: Iterable<Session>, message: unknown) => void;
12
+ /** Whether the daemon advertises `features["terminal-restore-modes"]`. */
13
+ restoreModesEnabled: boolean;
14
+ /** Resolve a project root → `pi-studio.json` path (for StartWorkspaceScriptRequest). */
15
+ projectConfigPath: (cwd: string) => string;
16
+ }
17
+ export declare function registerTerminalHandlers(registry: HandlerRegistry, deps: TerminalRpcDeps, getActiveSessions: () => Iterable<Session>): void;
18
+ /** Binary terminal-input frame handler for the frame dispatcher (Input/Resize opcodes). */
19
+ export declare function makeTerminalBinaryHandler(manager: TerminalManager): BinaryHandler;
20
+ //# sourceMappingURL=terminal-rpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-rpc.d.ts","sourceRoot":"","sources":["../../src/terminal/terminal-rpc.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;GAIG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACnE,0EAA0E;IAC1E,mBAAmB,EAAE,OAAO,CAAC;IAC7B,wFAAwF;IACxF,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5C;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,eAAe,EACrB,iBAAiB,EAAE,MAAM,QAAQ,CAAC,OAAO,CAAC,GACzC,IAAI,CAsHN;AAED,2FAA2F;AAC3F,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,eAAe,GAAG,aAAa,CAOjF"}
@@ -0,0 +1,122 @@
1
+ import { tryDecodeTerminalFrame } from "@av-pi-studio/protocol";
2
+ import { readProjectConfig } from "../config/project-config.js";
3
+ export function registerTerminalHandlers(registry, deps, getActiveSessions) {
4
+ const { manager } = deps;
5
+ // Per (session, slot) stream unsubscribers.
6
+ const streamUnsubs = new Map();
7
+ const key = (session, slot) => `${session.id}:${slot}`;
8
+ registry.register("list_terminals_request", () => ({
9
+ type: "list_terminals_response",
10
+ terminals: manager.list(),
11
+ }));
12
+ registry.register("subscribe_terminals_request", () => ({
13
+ type: "subscribe_terminals_response",
14
+ terminals: manager.list(),
15
+ }));
16
+ registry.register("unsubscribe_terminals_request", () => ({
17
+ type: "unsubscribe_terminals_response",
18
+ ok: true,
19
+ }));
20
+ registry.register("create_terminal_request", (ctx) => {
21
+ const entry = manager.createTerminal({
22
+ workspaceId: String(ctx.message.workspaceId ?? ""),
23
+ cwd: ctx.message.cwd,
24
+ shell: ctx.message.shell,
25
+ name: ctx.message.name,
26
+ cols: ctx.message.cols,
27
+ rows: ctx.message.rows,
28
+ });
29
+ deps.broadcast(getActiveSessions(), { type: "terminals_update", terminals: manager.list() });
30
+ return { type: "create_terminal_response", terminal: entry };
31
+ });
32
+ registry.register("rename_terminal_request", (ctx) => {
33
+ const slot = Number(ctx.message.slot);
34
+ const ok = manager.rename(slot, String(ctx.message.name ?? ""));
35
+ if (ok)
36
+ deps.broadcast(getActiveSessions(), { type: "terminals_update", terminals: manager.list() });
37
+ return { type: "rename_terminal_response", slot, ok };
38
+ });
39
+ registry.register("subscribe_terminal_request", (ctx) => {
40
+ const slot = Number(ctx.message.slot);
41
+ const session = ctx.session;
42
+ // Restore mode is honored only when the daemon advertises the feature AND the client advertised
43
+ // the reflowable-snapshot capability. Otherwise fall back to the basic snapshot (ignore mode).
44
+ const requestedMode = ctx.message.restoreMode;
45
+ const clientReflowable = session.supports("terminal_reflowable_snapshot");
46
+ const restoreMode = deps.restoreModesEnabled && clientReflowable ? (requestedMode ?? "basic") : "basic";
47
+ streamUnsubs.get(key(session, slot))?.(); // replace existing subscription
48
+ try {
49
+ const unsub = manager.subscribe(slot, (frame) => session.sendBinary(frame));
50
+ streamUnsubs.set(key(session, slot), unsub);
51
+ return { type: "subscribe_terminal_response", slot, ok: true, restoreMode };
52
+ }
53
+ catch {
54
+ return { type: "subscribe_terminal_response", slot, ok: false, error: "no_such_terminal" };
55
+ }
56
+ });
57
+ registry.register("unsubscribe_terminal_request", (ctx) => {
58
+ const slot = Number(ctx.message.slot);
59
+ streamUnsubs.get(key(ctx.session, slot))?.();
60
+ streamUnsubs.delete(key(ctx.session, slot));
61
+ return { type: "unsubscribe_terminal_response", slot, ok: true };
62
+ });
63
+ // Text-RPC input (binary input frames are handled by the binary router below).
64
+ registry.register("terminal_input", (ctx) => {
65
+ const slot = Number(ctx.message.slot);
66
+ const data = typeof ctx.message.data === "string"
67
+ ? Buffer.from(ctx.message.data, "base64")
68
+ : Buffer.from([]);
69
+ manager.input(slot, new Uint8Array(data));
70
+ return undefined; // fire-and-forget
71
+ });
72
+ registry.register("kill_terminal_request", (ctx) => {
73
+ const slot = Number(ctx.message.slot);
74
+ const ok = manager.kill(slot);
75
+ deps.broadcast(getActiveSessions(), { type: "terminals_update", terminals: manager.list() });
76
+ return { type: "kill_terminal_response", slot, ok };
77
+ });
78
+ registry.register("capture_terminal_request", (ctx) => {
79
+ const slot = Number(ctx.message.slot);
80
+ const screen = manager.capture(slot);
81
+ return { type: "capture_terminal_response", slot, ok: screen !== null, screen: screen ?? "" };
82
+ });
83
+ registry.register("start_workspace_script_request", async (ctx) => {
84
+ const cwd = String(ctx.message.cwd ?? "");
85
+ const scriptName = String(ctx.message.script ?? ctx.message.name ?? "");
86
+ const { config } = await readProjectConfig(deps.projectConfigPath(cwd));
87
+ const script = config.scripts[scriptName];
88
+ if (!script) {
89
+ return { type: "start_workspace_script_response", ok: false, error: "script_not_found" };
90
+ }
91
+ const isService = script.type === "service";
92
+ const entry = manager.createTerminal({
93
+ workspaceId: String(ctx.message.workspaceId ?? ""),
94
+ cwd,
95
+ shell: "/bin/sh",
96
+ args: ["-c", script.command],
97
+ name: scriptName,
98
+ service: isService,
99
+ });
100
+ deps.broadcast(getActiveSessions(), { type: "terminals_update", terminals: manager.list() });
101
+ // Service scripts are handed to the service proxy (task-003) by the caller via `entry.service`.
102
+ return {
103
+ type: "start_workspace_script_response",
104
+ ok: true,
105
+ terminal: entry,
106
+ service: isService,
107
+ };
108
+ });
109
+ }
110
+ /** Binary terminal-input frame handler for the frame dispatcher (Input/Resize opcodes). */
111
+ export function makeTerminalBinaryHandler(manager) {
112
+ return (_session, bytes) => {
113
+ const frame = tryDecodeTerminalFrame(bytes);
114
+ if (!frame)
115
+ return;
116
+ if (frame.opcode === "Input")
117
+ manager.input(frame.slot, frame.data);
118
+ else if (frame.opcode === "Resize")
119
+ manager.resize(frame.slot, frame.cols, frame.rows);
120
+ };
121
+ }
122
+ //# sourceMappingURL=terminal-rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-rpc.js","sourceRoot":"","sources":["../../src/terminal/terminal-rpc.ts"],"names":[],"mappings":"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"}