atoo-studio 0.0.1 → 0.0.2

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 (408) hide show
  1. package/LICENSE +21 -0
  2. package/README.github.md +322 -0
  3. package/README.md +112 -0
  4. package/README.npm.md +112 -0
  5. package/bin/atoo-studio.js +90 -0
  6. package/dist/src/agents/claude-code-terminal/adapter.d.ts +42 -0
  7. package/dist/src/agents/claude-code-terminal/adapter.js +166 -0
  8. package/dist/src/agents/claude-code-terminal/index.d.ts +13 -0
  9. package/dist/src/agents/claude-code-terminal/index.js +45 -0
  10. package/dist/src/agents/claude-code-terminal/spawner.d.ts +9 -0
  11. package/dist/src/agents/claude-code-terminal/spawner.js +37 -0
  12. package/dist/src/agents/claude-code-terminal-chatro/adapter.d.ts +51 -0
  13. package/dist/src/agents/claude-code-terminal-chatro/adapter.js +301 -0
  14. package/dist/src/agents/claude-code-terminal-chatro/index.d.ts +13 -0
  15. package/dist/src/agents/claude-code-terminal-chatro/index.js +45 -0
  16. package/dist/src/agents/claude-code-terminal-chatro/jsonl-watcher.d.ts +67 -0
  17. package/dist/src/agents/claude-code-terminal-chatro/jsonl-watcher.js +431 -0
  18. package/dist/src/agents/claude-code-terminal-chatro/spawner.d.ts +9 -0
  19. package/dist/src/agents/claude-code-terminal-chatro/spawner.js +37 -0
  20. package/dist/src/agents/codex-terminal/adapter.d.ts +40 -0
  21. package/dist/src/agents/codex-terminal/adapter.js +160 -0
  22. package/dist/src/agents/codex-terminal/index.d.ts +13 -0
  23. package/dist/src/agents/codex-terminal/index.js +47 -0
  24. package/dist/src/agents/codex-terminal/spawner.d.ts +9 -0
  25. package/dist/src/agents/codex-terminal/spawner.js +56 -0
  26. package/dist/src/agents/codex-terminal-chatro/adapter.d.ts +58 -0
  27. package/dist/src/agents/codex-terminal-chatro/adapter.js +266 -0
  28. package/dist/src/agents/codex-terminal-chatro/index.d.ts +13 -0
  29. package/dist/src/agents/codex-terminal-chatro/index.js +50 -0
  30. package/dist/src/agents/codex-terminal-chatro/jsonl-watcher.d.ts +36 -0
  31. package/dist/src/agents/codex-terminal-chatro/jsonl-watcher.js +205 -0
  32. package/dist/src/agents/codex-terminal-chatro/spawner.d.ts +9 -0
  33. package/dist/src/agents/codex-terminal-chatro/spawner.js +57 -0
  34. package/dist/src/agents/lib/chain-builder.d.ts +21 -0
  35. package/dist/src/agents/lib/chain-builder.js +139 -0
  36. package/dist/src/agents/lib/claude/fs-sessions.d.ts +31 -0
  37. package/dist/src/agents/lib/claude/fs-sessions.js +329 -0
  38. package/dist/src/agents/lib/claude/jsonl-writer.d.ts +32 -0
  39. package/dist/src/agents/lib/claude/jsonl-writer.js +342 -0
  40. package/dist/src/agents/lib/claude/workspace-trust.d.ts +1 -0
  41. package/dist/src/agents/lib/claude/workspace-trust.js +29 -0
  42. package/dist/src/agents/lib/codex/fs-sessions.d.ts +34 -0
  43. package/dist/src/agents/lib/codex/fs-sessions.js +255 -0
  44. package/dist/src/agents/lib/codex/jsonl-mapper.d.ts +11 -0
  45. package/dist/src/agents/lib/codex/jsonl-mapper.js +154 -0
  46. package/dist/src/agents/lib/codex/jsonl-writer.d.ts +8 -0
  47. package/dist/src/agents/lib/codex/jsonl-writer.js +440 -0
  48. package/dist/src/agents/lib/fs-tracking.d.ts +36 -0
  49. package/dist/src/agents/lib/fs-tracking.js +109 -0
  50. package/dist/src/agents/lib/pty-activity-tracker.d.ts +37 -0
  51. package/dist/src/agents/lib/pty-activity-tracker.js +105 -0
  52. package/dist/src/agents/lib/session-id-utils.d.ts +46 -0
  53. package/dist/src/agents/lib/session-id-utils.js +147 -0
  54. package/dist/src/agents/lib/session-precreate.d.ts +17 -0
  55. package/dist/src/agents/lib/session-precreate.js +177 -0
  56. package/dist/src/agents/registry.d.ts +72 -0
  57. package/dist/src/agents/registry.js +337 -0
  58. package/dist/src/agents/types.d.ts +135 -0
  59. package/dist/src/agents/types.js +1 -0
  60. package/dist/src/auth/crypto-key.d.ts +6 -0
  61. package/dist/src/auth/crypto-key.js +45 -0
  62. package/dist/src/auth/middleware.d.ts +18 -0
  63. package/dist/src/auth/middleware.js +54 -0
  64. package/dist/src/auth/password.d.ts +2 -0
  65. package/dist/src/auth/password.js +12 -0
  66. package/dist/src/auth/session.d.ts +10 -0
  67. package/dist/src/auth/session.js +33 -0
  68. package/dist/src/auth/totp.d.ts +12 -0
  69. package/dist/src/auth/totp.js +61 -0
  70. package/dist/src/auth/webauthn.d.ts +6 -0
  71. package/dist/src/auth/webauthn.js +117 -0
  72. package/dist/src/config.d.ts +10 -0
  73. package/dist/src/config.js +16 -0
  74. package/dist/src/database/connection-manager.d.ts +25 -0
  75. package/dist/src/database/connection-manager.js +211 -0
  76. package/dist/src/database/discovery/container.d.ts +6 -0
  77. package/dist/src/database/discovery/container.js +226 -0
  78. package/dist/src/database/discovery/env-parser.d.ts +9 -0
  79. package/dist/src/database/discovery/env-parser.js +525 -0
  80. package/dist/src/database/discovery/local-files.d.ts +6 -0
  81. package/dist/src/database/discovery/local-files.js +58 -0
  82. package/dist/src/database/discovery/port-scan.d.ts +7 -0
  83. package/dist/src/database/discovery/port-scan.js +61 -0
  84. package/dist/src/database/drivers/cassandra.d.ts +12 -0
  85. package/dist/src/database/drivers/cassandra.js +91 -0
  86. package/dist/src/database/drivers/clickhouse.d.ts +11 -0
  87. package/dist/src/database/drivers/clickhouse.js +127 -0
  88. package/dist/src/database/drivers/elasticsearch.d.ts +12 -0
  89. package/dist/src/database/drivers/elasticsearch.js +169 -0
  90. package/dist/src/database/drivers/influxdb.d.ts +14 -0
  91. package/dist/src/database/drivers/influxdb.js +194 -0
  92. package/dist/src/database/drivers/memcached.d.ts +11 -0
  93. package/dist/src/database/drivers/memcached.js +117 -0
  94. package/dist/src/database/drivers/mongodb.d.ts +12 -0
  95. package/dist/src/database/drivers/mongodb.js +128 -0
  96. package/dist/src/database/drivers/mysql.d.ts +11 -0
  97. package/dist/src/database/drivers/mysql.js +112 -0
  98. package/dist/src/database/drivers/neo4j.d.ts +11 -0
  99. package/dist/src/database/drivers/neo4j.js +158 -0
  100. package/dist/src/database/drivers/postgresql.d.ts +11 -0
  101. package/dist/src/database/drivers/postgresql.js +133 -0
  102. package/dist/src/database/drivers/redis.d.ts +11 -0
  103. package/dist/src/database/drivers/redis.js +91 -0
  104. package/dist/src/database/drivers/sqlite.d.ts +10 -0
  105. package/dist/src/database/drivers/sqlite.js +100 -0
  106. package/dist/src/database/query-stream.d.ts +5 -0
  107. package/dist/src/database/query-stream.js +75 -0
  108. package/dist/src/database/types.d.ts +71 -0
  109. package/dist/src/database/types.js +1 -0
  110. package/dist/src/events/index.d.ts +3 -0
  111. package/dist/src/events/index.js +3 -0
  112. package/dist/src/events/types.d.ts +214 -0
  113. package/dist/src/events/types.js +22 -0
  114. package/dist/src/events/wire.d.ts +114 -0
  115. package/dist/src/events/wire.js +296 -0
  116. package/dist/src/fs-monitor-types.d.ts +24 -0
  117. package/dist/src/fs-monitor-types.js +1 -0
  118. package/dist/src/fs-monitor.d.ts +80 -0
  119. package/dist/src/fs-monitor.js +637 -0
  120. package/dist/src/handlers/auth.d.ts +1 -0
  121. package/dist/src/handlers/auth.js +170 -0
  122. package/dist/src/handlers/changes.d.ts +1 -0
  123. package/dist/src/handlers/changes.js +203 -0
  124. package/dist/src/handlers/containers.d.ts +12 -0
  125. package/dist/src/handlers/containers.js +379 -0
  126. package/dist/src/handlers/databases.d.ts +3 -0
  127. package/dist/src/handlers/databases.js +327 -0
  128. package/dist/src/handlers/environments.d.ts +3 -0
  129. package/dist/src/handlers/environments.js +286 -0
  130. package/dist/src/handlers/github.d.ts +1 -0
  131. package/dist/src/handlers/github.js +153 -0
  132. package/dist/src/handlers/projects.d.ts +1 -0
  133. package/dist/src/handlers/projects.js +895 -0
  134. package/dist/src/handlers/ssh.d.ts +1 -0
  135. package/dist/src/handlers/ssh.js +162 -0
  136. package/dist/src/handlers/users.d.ts +1 -0
  137. package/dist/src/handlers/users.js +195 -0
  138. package/dist/src/index.d.ts +1 -0
  139. package/dist/src/index.js +228 -0
  140. package/dist/src/mcp/config.d.ts +32 -0
  141. package/dist/src/mcp/config.js +227 -0
  142. package/dist/src/mcp/server.d.ts +1 -0
  143. package/dist/src/mcp/server.js +574 -0
  144. package/dist/src/serial/cuse-device.d.ts +19 -0
  145. package/dist/src/serial/cuse-device.js +260 -0
  146. package/dist/src/serial/manager.d.ts +63 -0
  147. package/dist/src/serial/manager.js +206 -0
  148. package/dist/src/serial/pty-pair.d.ts +16 -0
  149. package/dist/src/serial/pty-pair.js +68 -0
  150. package/dist/src/services/fs-browser.d.ts +14 -0
  151. package/dist/src/services/fs-browser.js +98 -0
  152. package/dist/src/services/git-ops.d.ts +78 -0
  153. package/dist/src/services/git-ops.js +288 -0
  154. package/dist/src/services/github-ops.d.ts +104 -0
  155. package/dist/src/services/github-ops.js +192 -0
  156. package/dist/src/services/obfuscation.d.ts +2 -0
  157. package/dist/src/services/obfuscation.js +16 -0
  158. package/dist/src/services/preview/headless-backend.d.ts +62 -0
  159. package/dist/src/services/preview/headless-backend.js +698 -0
  160. package/dist/src/services/preview/injected-scripts.d.ts +9 -0
  161. package/dist/src/services/preview/injected-scripts.js +232 -0
  162. package/dist/src/services/preview/preview-backend.d.ts +92 -0
  163. package/dist/src/services/preview/preview-backend.js +15 -0
  164. package/dist/src/services/preview/universal-setter.d.ts +7 -0
  165. package/dist/src/services/preview/universal-setter.js +46 -0
  166. package/dist/src/services/preview-manager.d.ts +50 -0
  167. package/dist/src/services/preview-manager.js +216 -0
  168. package/dist/src/services/project-watcher.d.ts +6 -0
  169. package/dist/src/services/project-watcher.js +307 -0
  170. package/dist/src/services/remote-fs-browser.d.ts +11 -0
  171. package/dist/src/services/remote-fs-browser.js +50 -0
  172. package/dist/src/services/remote-git-ops.d.ts +71 -0
  173. package/dist/src/services/remote-git-ops.js +215 -0
  174. package/dist/src/services/session-search.d.ts +56 -0
  175. package/dist/src/services/session-search.js +303 -0
  176. package/dist/src/services/ssh-manager.d.ts +44 -0
  177. package/dist/src/services/ssh-manager.js +359 -0
  178. package/dist/src/session-writer.d.ts +9 -0
  179. package/dist/src/session-writer.js +66 -0
  180. package/dist/src/spawner.d.ts +56 -0
  181. package/dist/src/spawner.js +135 -0
  182. package/dist/src/state/db.d.ts +214 -0
  183. package/dist/src/state/db.js +897 -0
  184. package/dist/src/state/store.d.ts +37 -0
  185. package/dist/src/state/store.js +108 -0
  186. package/dist/src/state/types.d.ts +13 -0
  187. package/dist/src/state/types.js +1 -0
  188. package/dist/src/web/devtools-proxy.d.ts +7 -0
  189. package/dist/src/web/devtools-proxy.js +176 -0
  190. package/dist/src/web/port-proxy.d.ts +15 -0
  191. package/dist/src/web/port-proxy.js +124 -0
  192. package/dist/src/web/preview-ws.d.ts +5 -0
  193. package/dist/src/web/preview-ws.js +207 -0
  194. package/dist/src/web/server.d.ts +6 -0
  195. package/dist/src/web/server.js +1694 -0
  196. package/dist/src/ws/agent-ws.d.ts +5 -0
  197. package/dist/src/ws/agent-ws.js +93 -0
  198. package/frontend/dist/assets/_basePickBy-B-LibQ4-.js +1 -0
  199. package/frontend/dist/assets/_baseUniq-CprifHap.js +1 -0
  200. package/frontend/dist/assets/_createAssigner-ByDUqGii.js +1 -0
  201. package/frontend/dist/assets/abap-DuT-3z4x.js +1 -0
  202. package/frontend/dist/assets/addon-fit-CxQet2ja.js +1 -0
  203. package/frontend/dist/assets/addon-web-links-D_jRkPIl.js +1 -0
  204. package/frontend/dist/assets/apex-B-em86xX.js +1 -0
  205. package/frontend/dist/assets/api-SUPuHhSY.js +2 -0
  206. package/frontend/dist/assets/arc-Z0_eVteO.js +1 -0
  207. package/frontend/dist/assets/architecture-PBZL5I3N-hvVXGhqd.js +1 -0
  208. package/frontend/dist/assets/architectureDiagram-2XIMDMQ5-DiHPxX4j.js +36 -0
  209. package/frontend/dist/assets/array-CwG8vNfn.js +1 -0
  210. package/frontend/dist/assets/auth-store-R7eW5SVu.js +1 -0
  211. package/frontend/dist/assets/azcli-Bg9wQloi.js +1 -0
  212. package/frontend/dist/assets/bat-BM46z99L.js +1 -0
  213. package/frontend/dist/assets/bicep-DcBsJUfh.js +2 -0
  214. package/frontend/dist/assets/blockDiagram-WCTKOSBZ-C40u_hLo.js +132 -0
  215. package/frontend/dist/assets/c4Diagram-IC4MRINW-Ct7LjWFQ.js +10 -0
  216. package/frontend/dist/assets/cameligo-zw7JTtim.js +1 -0
  217. package/frontend/dist/assets/channel-ClCsE6HN.js +1 -0
  218. package/frontend/dist/assets/chunk-4BX2VUAB-zZ6P90VO.js +1 -0
  219. package/frontend/dist/assets/chunk-55IACEB6-DXllTDQl.js +1 -0
  220. package/frontend/dist/assets/chunk-7E7YKBS2-7zRaOLjj.js +1 -0
  221. package/frontend/dist/assets/chunk-7R4GIKGN-Csst1274.js +80 -0
  222. package/frontend/dist/assets/chunk-C72U2L5F-_JbQPbLN.js +1 -0
  223. package/frontend/dist/assets/chunk-CFjPhJqf.js +1 -0
  224. package/frontend/dist/assets/chunk-EGIJ26TM-B--aFyPw.js +1 -0
  225. package/frontend/dist/assets/chunk-FMBD7UC4-DVR34RNb.js +15 -0
  226. package/frontend/dist/assets/chunk-GEFDOKGD-CnmN6cC8.js +2 -0
  227. package/frontend/dist/assets/chunk-JSJVCQXG-CWxHBzeJ.js +1 -0
  228. package/frontend/dist/assets/chunk-KX2RTZJC-DkRk56s7.js +1 -0
  229. package/frontend/dist/assets/chunk-KYZI473N-DCCsG2dK.js +53 -0
  230. package/frontend/dist/assets/chunk-L3YUKLVL-C-DkZTMr.js +1 -0
  231. package/frontend/dist/assets/chunk-MX3YWQON-OUdzv5sZ.js +1 -0
  232. package/frontend/dist/assets/chunk-NQ4KR5QH-Bpu9FsM7.js +220 -0
  233. package/frontend/dist/assets/chunk-O4XLMI2P-BMLK6_ib.js +7 -0
  234. package/frontend/dist/assets/chunk-OZEHJAEY-CNNiJtG0.js +1 -0
  235. package/frontend/dist/assets/chunk-PQ6SQG4A-evVHD3KM.js +1 -0
  236. package/frontend/dist/assets/chunk-PU5JKC2W-DPFTYuvl.js +70 -0
  237. package/frontend/dist/assets/chunk-QZHKN3VN-JRdddPvu.js +1 -0
  238. package/frontend/dist/assets/chunk-R5LLSJPH-CHQzVVOV.js +1 -0
  239. package/frontend/dist/assets/chunk-WL4C6EOR-BNFU6IIi.js +189 -0
  240. package/frontend/dist/assets/chunk-XIRO2GV7-98T93G85.js +1 -0
  241. package/frontend/dist/assets/chunk-XZSTWKYB-BcW3cyNp.js +94 -0
  242. package/frontend/dist/assets/chunk-YBOYWFTD-BgKO1qAJ.js +1 -0
  243. package/frontend/dist/assets/classDiagram-VBA2DB6C-DikXzgcD.js +1 -0
  244. package/frontend/dist/assets/classDiagram-v2-RAHNMMFH-D7E3tQUK.js +1 -0
  245. package/frontend/dist/assets/clojure-FspFoNNQ.js +1 -0
  246. package/frontend/dist/assets/clone-mOXuZa7C.js +1 -0
  247. package/frontend/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  248. package/frontend/dist/assets/coffee-13n8Bk2W.js +1 -0
  249. package/frontend/dist/assets/cose-bilkent-S5V4N54A-zUOWQqLe.js +1 -0
  250. package/frontend/dist/assets/cpp-BVm2xGEs.js +1 -0
  251. package/frontend/dist/assets/csharp-D2kAWmUm.js +1 -0
  252. package/frontend/dist/assets/csp-Ezvgpf0e.js +1 -0
  253. package/frontend/dist/assets/css-CYxRwcFy.js +3 -0
  254. package/frontend/dist/assets/css.worker-Cd5h-ZOL.js +89 -0
  255. package/frontend/dist/assets/cssMode-CrXej49V.js +1 -0
  256. package/frontend/dist/assets/cypher-jg3SGErc.js +1 -0
  257. package/frontend/dist/assets/cytoscape.esm-kyyvzxNV.js +321 -0
  258. package/frontend/dist/assets/dagre-DH4bgZO7.js +1 -0
  259. package/frontend/dist/assets/dagre-KLK3FWXG-DNSqDkwT.js +4 -0
  260. package/frontend/dist/assets/dart-179jqhK4.js +1 -0
  261. package/frontend/dist/assets/defaultLocale-Dda4OpKy.js +1 -0
  262. package/frontend/dist/assets/diagram-E7M64L7V-RqPNT5Vs.js +24 -0
  263. package/frontend/dist/assets/diagram-IFDJBPK2-B-5NRyaE.js +43 -0
  264. package/frontend/dist/assets/diagram-P4PSJMXO-BrP69Hk0.js +24 -0
  265. package/frontend/dist/assets/dist-CU_Nb1G5.js +1 -0
  266. package/frontend/dist/assets/dockerfile-CIAtSGxS.js +1 -0
  267. package/frontend/dist/assets/ecl-CGVKfDxD.js +1 -0
  268. package/frontend/dist/assets/editor-Br_kD0ds.css +1 -0
  269. package/frontend/dist/assets/editor.api2-YXkDn0Gm.js +872 -0
  270. package/frontend/dist/assets/editor.main-fBaXZjJ0.js +6 -0
  271. package/frontend/dist/assets/elixir-BZ-6w0y3.js +1 -0
  272. package/frontend/dist/assets/erDiagram-INFDFZHY-BYiB9NYg.js +70 -0
  273. package/frontend/dist/assets/flow9-CVuOjTMv.js +1 -0
  274. package/frontend/dist/assets/flowDiagram-PKNHOUZH-Cwq47rsR.js +162 -0
  275. package/frontend/dist/assets/freemarker2-DM-pztJU.js +3 -0
  276. package/frontend/dist/assets/fsharp-q0pGJYr6.js +1 -0
  277. package/frontend/dist/assets/ganttDiagram-A5KZAMGK-Dnx3szD9.js +292 -0
  278. package/frontend/dist/assets/gitGraph-HDMCJU4V-COlTQ7bA.js +1 -0
  279. package/frontend/dist/assets/gitGraphDiagram-K3NZZRJ6-BaUxboNc.js +65 -0
  280. package/frontend/dist/assets/go-dzSPfdEO.js +1 -0
  281. package/frontend/dist/assets/graphlib-kEFlkt3U.js +1 -0
  282. package/frontend/dist/assets/graphql-CG4OUoEV.js +1 -0
  283. package/frontend/dist/assets/handlebars-BbK53Vec.js +1 -0
  284. package/frontend/dist/assets/hcl-Cy14JPk3.js +1 -0
  285. package/frontend/dist/assets/html-DYtTQNOG.js +1 -0
  286. package/frontend/dist/assets/html.worker-BjVEKLoU.js +502 -0
  287. package/frontend/dist/assets/htmlMode-C6GTouth.js +1 -0
  288. package/frontend/dist/assets/index-DMLxes_u.js +157 -0
  289. package/frontend/dist/assets/index-DmzeqkB1.css +1 -0
  290. package/frontend/dist/assets/info-3K5VOQVL-DBtHyA4C.js +1 -0
  291. package/frontend/dist/assets/infoDiagram-LFFYTUFH-yBXLgMPI.js +2 -0
  292. package/frontend/dist/assets/ini-Pbg8HGVD.js +1 -0
  293. package/frontend/dist/assets/init-D6KNwrax.js +1 -0
  294. package/frontend/dist/assets/ishikawaDiagram-PHBUUO56-Bld4two_.js +70 -0
  295. package/frontend/dist/assets/java-BmVu6Qrl.js +1 -0
  296. package/frontend/dist/assets/javascript-PbfQEdcJ.js +1 -0
  297. package/frontend/dist/assets/journeyDiagram-4ABVD52K-4HyMd4R2.js +139 -0
  298. package/frontend/dist/assets/json.worker-DqU5Wxnl.js +58 -0
  299. package/frontend/dist/assets/jsonMode-CASsGppE.js +7 -0
  300. package/frontend/dist/assets/julia-3cGnieBq.js +1 -0
  301. package/frontend/dist/assets/kanban-definition-K7BYSVSG-DpgsZmpG.js +89 -0
  302. package/frontend/dist/assets/katex-CEw3x5bf.js +261 -0
  303. package/frontend/dist/assets/kotlin-BuWkVcfV.js +1 -0
  304. package/frontend/dist/assets/less-CJ_VPy2C.js +2 -0
  305. package/frontend/dist/assets/lexon-BygAuZPu.js +1 -0
  306. package/frontend/dist/assets/line-CA_wh_TY.js +1 -0
  307. package/frontend/dist/assets/linear-BAcLW45z.js +1 -0
  308. package/frontend/dist/assets/liquid-kz84dle6.js +1 -0
  309. package/frontend/dist/assets/lspLanguageFeatures-C7hAHFn1.js +4 -0
  310. package/frontend/dist/assets/lua-C8Xs3dCx.js +1 -0
  311. package/frontend/dist/assets/m3-DTJeKBk4.js +1 -0
  312. package/frontend/dist/assets/markdown-QCgx8JqZ.js +1 -0
  313. package/frontend/dist/assets/math-D0YcMJAn.js +1 -0
  314. package/frontend/dist/assets/mdx-yRw0ap-E.js +1 -0
  315. package/frontend/dist/assets/mermaid-parser.core-DAeTodBQ.js +4 -0
  316. package/frontend/dist/assets/mindmap-definition-YRQLILUH-CoNlFyVl.js +68 -0
  317. package/frontend/dist/assets/mips-DopWaYgE.js +1 -0
  318. package/frontend/dist/assets/monaco.contribution-DeY0Qei-.js +2 -0
  319. package/frontend/dist/assets/msdax-BDis4ARV.js +1 -0
  320. package/frontend/dist/assets/mysql-BV6MLsOI.js +1 -0
  321. package/frontend/dist/assets/objective-c-B1UuzKs6.js +1 -0
  322. package/frontend/dist/assets/ordinal-jM7S0YHN.js +1 -0
  323. package/frontend/dist/assets/packet-RMMSAZCW-FF6-Tmai.js +1 -0
  324. package/frontend/dist/assets/pascal-BkvESCrc.js +1 -0
  325. package/frontend/dist/assets/pascaligo-lTy0kZYr.js +1 -0
  326. package/frontend/dist/assets/path-DNPd7Py7.js +1 -0
  327. package/frontend/dist/assets/perl-CrtUPXLV.js +1 -0
  328. package/frontend/dist/assets/pgsql-B9IbNWx2.js +1 -0
  329. package/frontend/dist/assets/php-CXvQBY2p.js +1 -0
  330. package/frontend/dist/assets/pie-UPGHQEXC-CFvXY2o-.js +1 -0
  331. package/frontend/dist/assets/pieDiagram-SKSYHLDU-CM_hbCcn.js +30 -0
  332. package/frontend/dist/assets/pla-DxBxuqWu.js +1 -0
  333. package/frontend/dist/assets/postiats-OkEuT5YF.js +1 -0
  334. package/frontend/dist/assets/powerquery-CMx5Tq4K.js +1 -0
  335. package/frontend/dist/assets/powershell-CstRxrEc.js +1 -0
  336. package/frontend/dist/assets/preload-helper-D4M6sveU.js +1 -0
  337. package/frontend/dist/assets/protobuf-Bx0Z-uRj.js +2 -0
  338. package/frontend/dist/assets/pug--W8vanWl.js +1 -0
  339. package/frontend/dist/assets/python-DA0rnlw3.js +1 -0
  340. package/frontend/dist/assets/qsharp-CRtr0YbN.js +1 -0
  341. package/frontend/dist/assets/quadrantDiagram-337W2JSQ-B3n3IUhC.js +7 -0
  342. package/frontend/dist/assets/r-C6E1d6iv.js +1 -0
  343. package/frontend/dist/assets/radar-KQ55EAFF-MPZu7SdX.js +1 -0
  344. package/frontend/dist/assets/razor-yd73uata.js +1 -0
  345. package/frontend/dist/assets/redis-Dx13voP3.js +1 -0
  346. package/frontend/dist/assets/redshift-D66HwlyV.js +1 -0
  347. package/frontend/dist/assets/requirementDiagram-Z7DCOOCP-CorP7L7F.js +73 -0
  348. package/frontend/dist/assets/restructuredtext-DQT2NKJ2.js +1 -0
  349. package/frontend/dist/assets/rough.esm-DxAX5Vpo.js +1 -0
  350. package/frontend/dist/assets/ruby-iFXI8hwH.js +1 -0
  351. package/frontend/dist/assets/rust-CSKiei34.js +1 -0
  352. package/frontend/dist/assets/sankeyDiagram-WA2Y5GQK-RDx6Bd-B.js +10 -0
  353. package/frontend/dist/assets/sb-Bo3ttdP2.js +1 -0
  354. package/frontend/dist/assets/scala-BC1D-Nxp.js +1 -0
  355. package/frontend/dist/assets/scheme-Z4OAo4Lv.js +1 -0
  356. package/frontend/dist/assets/scss-BvrdPs6B.js +3 -0
  357. package/frontend/dist/assets/sequenceDiagram-2WXFIKYE-JMqJSFq6.js +145 -0
  358. package/frontend/dist/assets/shell-Bh_aCyF-.js +1 -0
  359. package/frontend/dist/assets/solidity-CWHj6tSe.js +1 -0
  360. package/frontend/dist/assets/sophia-raoNtKtm.js +1 -0
  361. package/frontend/dist/assets/sparql-XzmoGnue.js +1 -0
  362. package/frontend/dist/assets/sql-BD0i9Gvg.js +1 -0
  363. package/frontend/dist/assets/src-Bn-kKzs7.js +1 -0
  364. package/frontend/dist/assets/st-DtVKyms6.js +1 -0
  365. package/frontend/dist/assets/stateDiagram-RAJIS63D-CgFfENdy.js +1 -0
  366. package/frontend/dist/assets/stateDiagram-v2-FVOUBMTO-C4Hh2P-U.js +1 -0
  367. package/frontend/dist/assets/swift--UZs77wT.js +1 -0
  368. package/frontend/dist/assets/systemverilog-CDnBSWUd.js +1 -0
  369. package/frontend/dist/assets/tcl-DdCEuTHZ.js +1 -0
  370. package/frontend/dist/assets/timeline-definition-YZTLITO2-BnatPBR5.js +61 -0
  371. package/frontend/dist/assets/treemap-KZPCXAKY-qb1Pl9la.js +1 -0
  372. package/frontend/dist/assets/ts.worker-DyPAEIuH.js +67719 -0
  373. package/frontend/dist/assets/tsMode-iuvyEpyO.js +11 -0
  374. package/frontend/dist/assets/twig-SSL-Altf.js +1 -0
  375. package/frontend/dist/assets/typescript-17918Hud.js +1 -0
  376. package/frontend/dist/assets/typespec-BT7S0ETg.js +1 -0
  377. package/frontend/dist/assets/vb-CrIgucua.js +1 -0
  378. package/frontend/dist/assets/vennDiagram-LZ73GAT5-DygS4Zzd.js +34 -0
  379. package/frontend/dist/assets/wgsl-BeKc3oEp.js +298 -0
  380. package/frontend/dist/assets/workers-DTfwKVoM.js +1 -0
  381. package/frontend/dist/assets/xml-CBMr_Wbw.js +1 -0
  382. package/frontend/dist/assets/xterm-BrP-ENHg.css +1 -0
  383. package/frontend/dist/assets/xterm-CBX2m0YM.js +36 -0
  384. package/frontend/dist/assets/xychartDiagram-JWTSCODW-D6wY1Jwd.js +7 -0
  385. package/frontend/dist/assets/yaml-CTjCH7Bv.js +1 -0
  386. package/frontend/dist/fonts/inter-300.ttf +0 -0
  387. package/frontend/dist/fonts/inter-400.ttf +0 -0
  388. package/frontend/dist/fonts/inter-500.ttf +0 -0
  389. package/frontend/dist/fonts/inter-600.ttf +0 -0
  390. package/frontend/dist/fonts/inter-700.ttf +0 -0
  391. package/frontend/dist/index.html +49 -0
  392. package/frontend/dist/logo_192x192.png +0 -0
  393. package/frontend/dist/logo_32x32.png +0 -0
  394. package/frontend/dist/logo_512x512.png +0 -0
  395. package/frontend/dist/logo_64x64.png +0 -0
  396. package/frontend/dist/logobg_192x192.png +0 -0
  397. package/frontend/dist/logobg_512x512.png +0 -0
  398. package/frontend/dist/logobg_64x64.png +0 -0
  399. package/frontend/dist/manifest.json +25 -0
  400. package/frontend/dist/sw.js +22 -0
  401. package/package.json +74 -7
  402. package/preload/Makefile +12 -0
  403. package/preload/atoo-studio-preload.c +647 -0
  404. package/preload/atoo-studio-preload.so +0 -0
  405. package/setup-cuse.sh +260 -0
  406. package/setup.sh +81 -0
  407. package/src/serial/native/binding.gyp +10 -0
  408. package/src/serial/native/pty_pair.c +222 -0
@@ -0,0 +1 @@
1
+ export declare const sshRouter: import("express-serve-static-core").Router;
@@ -0,0 +1,162 @@
1
+ import { Router } from 'express';
2
+ import crypto from 'crypto';
3
+ import { db } from '../state/db.js';
4
+ import { sshManager } from '../services/ssh-manager.js';
5
+ import { obfuscate } from '../services/obfuscation.js';
6
+ export const sshRouter = Router();
7
+ // Connect to SSH host (creates DB record + live connection)
8
+ sshRouter.post('/api/ssh/connect', async (req, res) => {
9
+ const { label, host, port, username, auth_method, password, privateKey, passphrase, systemKeyPath } = req.body;
10
+ if (!host || !username || !auth_method) {
11
+ return res.status(400).json({ error: 'host, username, and auth_method are required' });
12
+ }
13
+ try {
14
+ const config = {
15
+ label: label || `${username}@${host}`,
16
+ host,
17
+ port: port || 22,
18
+ username,
19
+ auth_method,
20
+ };
21
+ if (auth_method === 'password' && password) {
22
+ config.password_obfuscated = obfuscate(password);
23
+ }
24
+ else if (auth_method === 'privatekey' && privateKey) {
25
+ config.private_key_obfuscated = obfuscate(privateKey);
26
+ if (passphrase)
27
+ config.passphrase_obfuscated = obfuscate(passphrase);
28
+ }
29
+ else if (auth_method === 'systemkey' && systemKeyPath) {
30
+ config.system_key_path = systemKeyPath;
31
+ if (passphrase)
32
+ config.passphrase_obfuscated = obfuscate(passphrase);
33
+ }
34
+ const dbRecord = db.createSshConnection(config);
35
+ // Attempt live connection
36
+ await sshManager.connect(dbRecord);
37
+ // Verify claude CLI
38
+ const hasClaude = await sshManager.verifyClaudeCli(dbRecord.id);
39
+ res.json({
40
+ id: dbRecord.id,
41
+ label: dbRecord.label,
42
+ host: dbRecord.host,
43
+ port: dbRecord.port,
44
+ username: dbRecord.username,
45
+ auth_method: dbRecord.auth_method,
46
+ connected: true,
47
+ has_claude: hasClaude,
48
+ });
49
+ }
50
+ catch (err) {
51
+ console.error('[ssh] Connect failed:', err.message);
52
+ res.status(500).json({ error: err.message });
53
+ }
54
+ });
55
+ // Generate ed25519 keypair
56
+ sshRouter.post('/api/ssh/generate-keypair', (_req, res) => {
57
+ try {
58
+ const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519', {
59
+ publicKeyEncoding: { type: 'spki', format: 'pem' },
60
+ privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
61
+ });
62
+ // Convert PEM public key to OpenSSH format for authorized_keys
63
+ const pubKeyObj = crypto.createPublicKey(publicKey);
64
+ const sshPublicKey = pubKeyObj.export({ type: 'spki', format: 'der' });
65
+ const opensshPubKey = `ssh-ed25519 ${Buffer.from(sshPublicKey).toString('base64')} atoo-studio-generated`;
66
+ res.json({ privateKey, publicKey: opensshPubKey });
67
+ }
68
+ catch (err) {
69
+ res.status(500).json({ error: err.message });
70
+ }
71
+ });
72
+ // Disconnect
73
+ sshRouter.post('/api/ssh/:id/disconnect', async (req, res) => {
74
+ try {
75
+ await sshManager.disconnect(req.params.id);
76
+ res.json({ success: true });
77
+ }
78
+ catch (err) {
79
+ res.status(500).json({ error: err.message });
80
+ }
81
+ });
82
+ // Connection status
83
+ sshRouter.get('/api/ssh/:id/status', (req, res) => {
84
+ const status = sshManager.getStatus(req.params.id);
85
+ const dbRecord = db.getSshConnection(req.params.id);
86
+ res.json({
87
+ ...status,
88
+ label: dbRecord?.label,
89
+ host: dbRecord?.host,
90
+ port: dbRecord?.port,
91
+ username: dbRecord?.username,
92
+ });
93
+ });
94
+ // List all connections (with live status)
95
+ sshRouter.get('/api/ssh/connections', (_req, res) => {
96
+ const connections = db.listSshConnections();
97
+ const result = connections.map(c => ({
98
+ id: c.id,
99
+ label: c.label,
100
+ host: c.host,
101
+ port: c.port,
102
+ username: c.username,
103
+ auth_method: c.auth_method,
104
+ created_at: c.created_at,
105
+ ...sshManager.getStatus(c.id),
106
+ }));
107
+ res.json(result);
108
+ });
109
+ // Remote folder browser (SFTP)
110
+ sshRouter.get('/api/ssh/:id/browse', async (req, res) => {
111
+ const browsePath = req.query.path || '/home';
112
+ try {
113
+ const entries = await sshManager.sftpReaddir(req.params.id, browsePath);
114
+ const dirs = entries.filter(e => e.type === 'dir').map(e => ({
115
+ name: e.name,
116
+ path: browsePath === '/' ? `/${e.name}` : `${browsePath}/${e.name}`,
117
+ }));
118
+ const parent = browsePath === '/' ? null : browsePath.substring(0, browsePath.lastIndexOf('/')) || '/';
119
+ res.json({ current: browsePath, parent, dirs });
120
+ }
121
+ catch (err) {
122
+ res.status(400).json({ error: err.message });
123
+ }
124
+ });
125
+ // Create remote directory
126
+ sshRouter.post('/api/ssh/:id/browse/mkdir', async (req, res) => {
127
+ const { path: dirPath } = req.body;
128
+ if (!dirPath)
129
+ return res.status(400).json({ error: 'path is required' });
130
+ try {
131
+ await sshManager.sftpMkdir(req.params.id, dirPath);
132
+ res.json({ success: true, path: dirPath });
133
+ }
134
+ catch (err) {
135
+ res.status(400).json({ error: err.message });
136
+ }
137
+ });
138
+ // Delete SSH connection
139
+ sshRouter.delete('/api/ssh/:id', async (req, res) => {
140
+ try {
141
+ await sshManager.disconnect(req.params.id);
142
+ db.deleteSshConnection(req.params.id);
143
+ res.json({ success: true });
144
+ }
145
+ catch (err) {
146
+ res.status(500).json({ error: err.message });
147
+ }
148
+ });
149
+ // Reconnect to a saved connection
150
+ sshRouter.post('/api/ssh/:id/reconnect', async (req, res) => {
151
+ const dbRecord = db.getSshConnection(req.params.id);
152
+ if (!dbRecord)
153
+ return res.status(404).json({ error: 'Connection not found' });
154
+ try {
155
+ await sshManager.connect(dbRecord);
156
+ const hasClaude = await sshManager.verifyClaudeCli(dbRecord.id);
157
+ res.json({ connected: true, has_claude: hasClaude });
158
+ }
159
+ catch (err) {
160
+ res.status(500).json({ error: err.message });
161
+ }
162
+ });
@@ -0,0 +1 @@
1
+ export declare const usersRouter: import("express-serve-static-core").Router;
@@ -0,0 +1,195 @@
1
+ import { Router } from 'express';
2
+ import { z } from 'zod';
3
+ import { db } from '../state/db.js';
4
+ import { hashPassword, verifyPassword } from '../auth/password.js';
5
+ import { requireAuth, requireAdmin } from '../auth/middleware.js';
6
+ import { destroyAllUserSessions } from '../auth/session.js';
7
+ import { generateTotpSecret, verifyTotpToken, saveTotpSecret, markTotpVerified, removeTotpSecret, getUnverifiedTotpSecret, hasTotpEnabled } from '../auth/totp.js';
8
+ import { getRegistrationOptions, verifyAndSaveRegistration } from '../auth/webauthn.js';
9
+ export const usersRouter = Router();
10
+ // ═══════════════════════════════════════════════════
11
+ // ADMIN: User management
12
+ // ═══════════════════════════════════════════════════
13
+ // GET /api/users — list all users (admin only)
14
+ usersRouter.get('/api/users', requireAdmin, (_req, res) => {
15
+ const users = db.listUsers();
16
+ const usersWithMfa = users.map(u => ({
17
+ ...u,
18
+ hasTOTP: hasTotpEnabled(u.id),
19
+ passkeyCount: db.listPasskeys(u.id).length,
20
+ }));
21
+ res.json(usersWithMfa);
22
+ });
23
+ // POST /api/users — create user (admin only)
24
+ const createUserSchema = z.object({
25
+ username: z.string().min(1).max(64),
26
+ display_name: z.string().min(1).max(128),
27
+ role: z.enum(['admin', 'basic']),
28
+ password: z.string().min(8).max(256),
29
+ });
30
+ usersRouter.post('/api/users', requireAdmin, async (req, res) => {
31
+ const parsed = createUserSchema.safeParse(req.body);
32
+ if (!parsed.success) {
33
+ return res.status(400).json({ error: 'Invalid input', details: parsed.error.flatten() });
34
+ }
35
+ const { username, display_name, role, password } = parsed.data;
36
+ if (db.getUserByUsername(username)) {
37
+ return res.status(409).json({ error: 'Username already exists' });
38
+ }
39
+ const passwordHash = await hashPassword(password);
40
+ const user = db.createUser(username, display_name, role, passwordHash);
41
+ res.json({ id: user.id, username: user.username, display_name: user.display_name, role: user.role });
42
+ });
43
+ // PUT /api/users/:id — update user (admin only)
44
+ const updateUserSchema = z.object({
45
+ display_name: z.string().min(1).max(128).optional(),
46
+ role: z.enum(['admin', 'basic']).optional(),
47
+ });
48
+ usersRouter.put('/api/users/:id', requireAdmin, (req, res) => {
49
+ const parsed = updateUserSchema.safeParse(req.body);
50
+ if (!parsed.success) {
51
+ return res.status(400).json({ error: 'Invalid input' });
52
+ }
53
+ const user = db.getUser(req.params.id);
54
+ if (!user) {
55
+ return res.status(404).json({ error: 'User not found' });
56
+ }
57
+ db.updateUser(req.params.id, parsed.data);
58
+ res.json({ ok: true });
59
+ });
60
+ // DELETE /api/users/:id — delete user (admin only, cannot delete self)
61
+ usersRouter.delete('/api/users/:id', requireAdmin, (req, res) => {
62
+ if (req.params.id === req.user.id) {
63
+ return res.status(400).json({ error: 'Cannot delete yourself' });
64
+ }
65
+ const user = db.getUser(req.params.id);
66
+ if (!user) {
67
+ return res.status(404).json({ error: 'User not found' });
68
+ }
69
+ destroyAllUserSessions(req.params.id);
70
+ db.deleteUser(req.params.id);
71
+ res.json({ ok: true });
72
+ });
73
+ // POST /api/users/:id/reset-password — admin resets password
74
+ const resetPasswordSchema = z.object({
75
+ password: z.string().min(8).max(256),
76
+ });
77
+ usersRouter.post('/api/users/:id/reset-password', requireAdmin, async (req, res) => {
78
+ const parsed = resetPasswordSchema.safeParse(req.body);
79
+ if (!parsed.success) {
80
+ return res.status(400).json({ error: 'Invalid input' });
81
+ }
82
+ const user = db.getUser(req.params.id);
83
+ if (!user) {
84
+ return res.status(404).json({ error: 'User not found' });
85
+ }
86
+ const hash = await hashPassword(parsed.data.password);
87
+ db.updateUserPassword(req.params.id, hash);
88
+ destroyAllUserSessions(req.params.id);
89
+ res.json({ ok: true });
90
+ });
91
+ // POST /api/users/:id/reset-totp — admin removes TOTP
92
+ usersRouter.post('/api/users/:id/reset-totp', requireAdmin, (req, res) => {
93
+ removeTotpSecret(req.params.id);
94
+ res.json({ ok: true });
95
+ });
96
+ // POST /api/users/:id/reset-passkeys — admin removes all passkeys
97
+ usersRouter.post('/api/users/:id/reset-passkeys', requireAdmin, (req, res) => {
98
+ db.deleteAllUserPasskeys(req.params.id);
99
+ res.json({ ok: true });
100
+ });
101
+ // ═══════════════════════════════════════════════════
102
+ // SELF: Profile & security
103
+ // ═══════════════════════════════════════════════════
104
+ // PUT /api/users/me/password — change own password
105
+ const changePasswordSchema = z.object({
106
+ current_password: z.string().min(1),
107
+ new_password: z.string().min(8).max(256),
108
+ });
109
+ usersRouter.put('/api/users/me/password', requireAuth, async (req, res) => {
110
+ const parsed = changePasswordSchema.safeParse(req.body);
111
+ if (!parsed.success) {
112
+ return res.status(400).json({ error: 'Invalid input' });
113
+ }
114
+ const { current_password, new_password } = parsed.data;
115
+ const user = db.getUser(req.user.id);
116
+ const valid = await verifyPassword(current_password, user.password_hash);
117
+ if (!valid) {
118
+ return res.status(401).json({ error: 'Current password is incorrect' });
119
+ }
120
+ const hash = await hashPassword(new_password);
121
+ db.updateUserPassword(user.id, hash);
122
+ res.json({ ok: true });
123
+ });
124
+ // POST /api/users/me/totp/setup — generate TOTP secret + QR
125
+ usersRouter.post('/api/users/me/totp/setup', requireAuth, async (req, res) => {
126
+ const user = req.user;
127
+ const result = await generateTotpSecret(user.username);
128
+ saveTotpSecret(user.id, result.secret);
129
+ res.json({ otpauthUri: result.otpauthUri, qrDataUrl: result.qrDataUrl });
130
+ });
131
+ // POST /api/users/me/totp/verify — activate TOTP with verification code
132
+ const totpVerifySchema = z.object({
133
+ token: z.string().length(6),
134
+ });
135
+ usersRouter.post('/api/users/me/totp/verify', requireAuth, (req, res) => {
136
+ const parsed = totpVerifySchema.safeParse(req.body);
137
+ if (!parsed.success) {
138
+ return res.status(400).json({ error: 'Invalid input' });
139
+ }
140
+ const secret = getUnverifiedTotpSecret(req.user.id);
141
+ if (!secret) {
142
+ return res.status(400).json({ error: 'No TOTP setup in progress' });
143
+ }
144
+ if (!verifyTotpToken(secret, parsed.data.token)) {
145
+ return res.status(400).json({ error: 'Invalid TOTP code' });
146
+ }
147
+ markTotpVerified(req.user.id);
148
+ res.json({ ok: true });
149
+ });
150
+ // DELETE /api/users/me/totp — remove own TOTP
151
+ usersRouter.delete('/api/users/me/totp', requireAuth, (req, res) => {
152
+ removeTotpSecret(req.user.id);
153
+ res.json({ ok: true });
154
+ });
155
+ // POST /api/users/me/passkey/register-options — get WebAuthn registration options
156
+ usersRouter.post('/api/users/me/passkey/register-options', requireAuth, async (req, res) => {
157
+ const rpID = req.hostname;
158
+ const options = await getRegistrationOptions(req.user, rpID);
159
+ res.json(options);
160
+ });
161
+ // POST /api/users/me/passkey/register — complete passkey registration
162
+ const passkeyRegisterSchema = z.object({
163
+ response: z.any(),
164
+ device_name: z.string().max(128).optional(),
165
+ });
166
+ usersRouter.post('/api/users/me/passkey/register', requireAuth, async (req, res) => {
167
+ const parsed = passkeyRegisterSchema.safeParse(req.body);
168
+ if (!parsed.success) {
169
+ return res.status(400).json({ error: 'Invalid input' });
170
+ }
171
+ const rpID = req.hostname;
172
+ const origin = `${req.protocol}://${req.headers.host}`;
173
+ const verified = await verifyAndSaveRegistration(req.user, rpID, origin, parsed.data.response, parsed.data.device_name);
174
+ if (!verified) {
175
+ return res.status(400).json({ error: 'Passkey registration failed' });
176
+ }
177
+ res.json({ ok: true });
178
+ });
179
+ // GET /api/users/me/passkeys — list own passkeys
180
+ usersRouter.get('/api/users/me/passkeys', requireAuth, (req, res) => {
181
+ const passkeys = db.listPasskeys(req.user.id);
182
+ res.json(passkeys.map(pk => ({
183
+ id: pk.id,
184
+ device_name: pk.device_name,
185
+ created_at: pk.created_at,
186
+ })));
187
+ });
188
+ // DELETE /api/users/me/passkeys/:id — remove a passkey
189
+ usersRouter.delete('/api/users/me/passkeys/:id', requireAuth, (req, res) => {
190
+ const deleted = db.deletePasskey(req.params.id, req.user.id);
191
+ if (!deleted) {
192
+ return res.status(404).json({ error: 'Passkey not found' });
193
+ }
194
+ res.json({ ok: true });
195
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,228 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import os from 'os';
4
+ import https from 'https';
5
+ import forge from 'node-forge';
6
+ import { createWebServer } from './web/server.js';
7
+ import { killAllCliProcesses } from './spawner.js';
8
+ import { WEB_PORT, CA_CERT_PATH, CA_KEY_PATH, WEB_CERT_PATH, WEB_KEY_PATH } from './config.js';
9
+ import { cleanupStaleMcpConfigs } from './mcp/config.js';
10
+ import { fsMonitor } from './fs-monitor.js';
11
+ import { db } from './state/db.js';
12
+ import { agentRegistry } from './agents/registry.js';
13
+ import { ClaudeCodeTerminalAgentFactory } from './agents/claude-code-terminal/index.js';
14
+ import { ClaudeCodeTerminalChatROAgentFactory } from './agents/claude-code-terminal-chatro/index.js';
15
+ import { CodexTerminalAgentFactory } from './agents/codex-terminal/index.js';
16
+ import { sshManager } from './services/ssh-manager.js';
17
+ import { previewManager } from './services/preview-manager.js';
18
+ async function main() {
19
+ console.log('=== Atoo Studio ===');
20
+ console.log('');
21
+ // 1. Connect to filesystem monitor (graceful — no-op if unavailable)
22
+ fsMonitor.connect().catch(err => console.warn('[init] FS monitor not available:', err.message));
23
+ // 2. Register agent factories
24
+ agentRegistry.registerFactory(new ClaudeCodeTerminalAgentFactory());
25
+ agentRegistry.registerFactory(new ClaudeCodeTerminalChatROAgentFactory());
26
+ // agentRegistry.registerFactory(new CodexTerminalChatROAgentFactory());
27
+ agentRegistry.registerFactory(new CodexTerminalAgentFactory());
28
+ // 3. Clean up stale per-session MCP config files (older than 24h)
29
+ cleanupStaleMcpConfigs();
30
+ // 3.5. Generate CA-signed TLS cert for web frontend
31
+ const tlsOptions = getOrCreateWebCert();
32
+ // 4. Create and start the web frontend server over HTTPS (bind to 0.0.0.0 for WSL access from Windows)
33
+ const webServer = createWebServer(tlsOptions);
34
+ // Dynamic SAN expansion: detect new hosts from incoming requests and hot-swap cert
35
+ if (webServer instanceof https.Server) {
36
+ setupDynamicSan(webServer);
37
+ }
38
+ webServer.listen(WEB_PORT, '0.0.0.0', () => {
39
+ console.log(`[init] Web frontend listening on 0.0.0.0:${WEB_PORT} (HTTPS)`);
40
+ });
41
+ // Detect local IPs for display
42
+ const localIps = getLocalIps();
43
+ console.log('');
44
+ for (const ip of localIps) {
45
+ console.log(`Web frontend: https://${ip}:${WEB_PORT}`);
46
+ }
47
+ console.log('');
48
+ // Auto-reconnect saved SSH connections
49
+ const savedSshConns = db.listSshConnections();
50
+ for (const conn of savedSshConns) {
51
+ sshManager.connect(conn).then(() => {
52
+ console.log(`[init] SSH auto-reconnected: ${conn.label}`);
53
+ }).catch(err => {
54
+ console.warn(`[init] SSH auto-reconnect failed for ${conn.label}: ${err.message}`);
55
+ });
56
+ }
57
+ // Graceful shutdown
58
+ process.on('SIGINT', async () => {
59
+ console.log('\n[shutdown] Stopping...');
60
+ await previewManager.shutdown();
61
+ fsMonitor.disconnect();
62
+ sshManager.disconnectAll();
63
+ killAllCliProcesses();
64
+ db.close();
65
+ webServer.close();
66
+ process.exit(0);
67
+ });
68
+ process.on('SIGTERM', async () => {
69
+ await previewManager.shutdown();
70
+ fsMonitor.disconnect();
71
+ sshManager.disconnectAll();
72
+ killAllCliProcesses();
73
+ db.close();
74
+ webServer.close();
75
+ process.exit(0);
76
+ });
77
+ }
78
+ // Track known SANs so we can detect new ones
79
+ const knownSans = new Set();
80
+ let webKeyPem = '';
81
+ function ensureCACert() {
82
+ if (fs.existsSync(CA_CERT_PATH))
83
+ return;
84
+ console.log('[tls] First run — generating CA certificate...');
85
+ fs.mkdirSync(path.dirname(CA_CERT_PATH), { recursive: true });
86
+ const keys = forge.pki.rsa.generateKeyPair(2048);
87
+ const cert = forge.pki.createCertificate();
88
+ cert.publicKey = keys.publicKey;
89
+ cert.serialNumber = '01';
90
+ cert.validity.notBefore = new Date();
91
+ cert.validity.notAfter = new Date();
92
+ cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 10);
93
+ const attrs = [
94
+ { name: 'commonName', value: 'Atoo Studio Local CA' },
95
+ { name: 'organizationName', value: 'Atoo Studio' },
96
+ ];
97
+ cert.setSubject(attrs);
98
+ cert.setIssuer(attrs);
99
+ cert.setExtensions([
100
+ { name: 'basicConstraints', cA: true, critical: true },
101
+ { name: 'keyUsage', keyCertSign: true, cRLSign: true, critical: true },
102
+ { name: 'subjectKeyIdentifier' },
103
+ ]);
104
+ cert.sign(keys.privateKey, forge.md.sha256.create());
105
+ fs.writeFileSync(CA_CERT_PATH, forge.pki.certificateToPem(cert));
106
+ fs.writeFileSync(CA_KEY_PATH, forge.pki.privateKeyToPem(keys.privateKey), { mode: 0o600 });
107
+ console.log('[tls] CA certificate generated.');
108
+ }
109
+ function getOrCreateWebCert() {
110
+ ensureCACert();
111
+ // Seed known SANs with local IPs
112
+ knownSans.add('localhost');
113
+ knownSans.add('127.0.0.1');
114
+ for (const ip of getLocalIps())
115
+ knownSans.add(ip);
116
+ // If existing cert was CA-signed and covers all current SANs, reuse it
117
+ if (fs.existsSync(WEB_CERT_PATH) && fs.existsSync(WEB_KEY_PATH)) {
118
+ try {
119
+ const existingCert = forge.pki.certificateFromPem(fs.readFileSync(WEB_CERT_PATH, 'utf-8'));
120
+ const sanExt = existingCert.getExtension('subjectAltName');
121
+ const existingSans = new Set();
122
+ if (sanExt?.altNames) {
123
+ for (const an of sanExt.altNames)
124
+ existingSans.add(an.value || an.ip);
125
+ }
126
+ // Check issuer is our CA (not self-signed)
127
+ const caCert = forge.pki.certificateFromPem(fs.readFileSync(CA_CERT_PATH, 'utf-8'));
128
+ const issuedByCA = existingCert.issuer.hash === caCert.subject.hash;
129
+ const allCovered = [...knownSans].every(s => existingSans.has(s));
130
+ if (issuedByCA && allCovered) {
131
+ webKeyPem = fs.readFileSync(WEB_KEY_PATH, 'utf-8');
132
+ // Also add any extra SANs from existing cert to our tracking set
133
+ for (const s of existingSans)
134
+ knownSans.add(s);
135
+ console.log('[init] Reusing existing CA-signed web certificate');
136
+ return { key: webKeyPem, cert: fs.readFileSync(WEB_CERT_PATH, 'utf-8') };
137
+ }
138
+ }
139
+ catch { }
140
+ }
141
+ return regenerateWebCert('initial generation');
142
+ }
143
+ function regenerateWebCert(reason) {
144
+ console.log(`[tls] Generating CA-signed web certificate (${reason})...`);
145
+ const caCertPem = fs.readFileSync(CA_CERT_PATH, 'utf-8');
146
+ const caKeyPem = fs.readFileSync(CA_KEY_PATH, 'utf-8');
147
+ const caCert = forge.pki.certificateFromPem(caCertPem);
148
+ const caKey = forge.pki.privateKeyFromPem(caKeyPem);
149
+ // Reuse existing key if we have one, otherwise generate new
150
+ let privateKey;
151
+ let publicKey;
152
+ if (webKeyPem) {
153
+ privateKey = forge.pki.privateKeyFromPem(webKeyPem);
154
+ publicKey = forge.pki.setRsaPublicKey(privateKey.n, privateKey.e);
155
+ }
156
+ else {
157
+ const keys = forge.pki.rsa.generateKeyPair(2048);
158
+ privateKey = keys.privateKey;
159
+ publicKey = keys.publicKey;
160
+ webKeyPem = forge.pki.privateKeyToPem(privateKey);
161
+ }
162
+ const cert = forge.pki.createCertificate();
163
+ cert.publicKey = publicKey;
164
+ cert.serialNumber = Date.now().toString(16);
165
+ cert.validity.notBefore = new Date();
166
+ cert.validity.notAfter = new Date();
167
+ cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 1);
168
+ cert.setSubject([{ name: 'commonName', value: 'Atoo Studio Web' }]);
169
+ cert.setIssuer(caCert.subject.attributes);
170
+ // Build SAN list from all known hosts
171
+ const altNames = [];
172
+ for (const san of knownSans) {
173
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(san)) {
174
+ altNames.push({ type: 7, ip: san });
175
+ }
176
+ else {
177
+ altNames.push({ type: 2, value: san });
178
+ }
179
+ }
180
+ cert.setExtensions([
181
+ { name: 'basicConstraints', cA: false },
182
+ { name: 'keyUsage', digitalSignature: true, keyEncipherment: true },
183
+ { name: 'extKeyUsage', serverAuth: true },
184
+ { name: 'subjectAltName', altNames },
185
+ ]);
186
+ cert.sign(caKey, forge.md.sha256.create());
187
+ const certPem = forge.pki.certificateToPem(cert);
188
+ fs.writeFileSync(WEB_KEY_PATH, webKeyPem, { mode: 0o600 });
189
+ fs.writeFileSync(WEB_CERT_PATH, certPem);
190
+ console.log(`[tls] Web certificate issued with SANs: ${[...knownSans].join(', ')}`);
191
+ return { key: webKeyPem, cert: certPem };
192
+ }
193
+ function setupDynamicSan(server) {
194
+ // Intercept connections to check the Host header for unknown SANs
195
+ const origEmit = server.emit.bind(server);
196
+ server.emit = function (event, ...args) {
197
+ if (event === 'request') {
198
+ const req = args[0];
199
+ const host = req.headers.host;
200
+ if (host) {
201
+ const hostname = host.replace(/:\d+$/, '');
202
+ if (hostname && !knownSans.has(hostname)) {
203
+ knownSans.add(hostname);
204
+ console.log(`[tls] New host detected: ${hostname} — regenerating certificate`);
205
+ const newTls = regenerateWebCert(`adding ${hostname}`);
206
+ server.setSecureContext({ key: newTls.key, cert: newTls.cert });
207
+ }
208
+ }
209
+ }
210
+ return origEmit(event, ...args);
211
+ };
212
+ }
213
+ function getLocalIps() {
214
+ const ips = ['localhost'];
215
+ const interfaces = os.networkInterfaces();
216
+ for (const name of Object.keys(interfaces)) {
217
+ for (const iface of interfaces[name] || []) {
218
+ if (iface.family === 'IPv4' && !iface.internal) {
219
+ ips.push(iface.address);
220
+ }
221
+ }
222
+ }
223
+ return ips;
224
+ }
225
+ main().catch((err) => {
226
+ console.error('Fatal error:', err);
227
+ process.exit(1);
228
+ });
@@ -0,0 +1,32 @@
1
+ /** Register an MCP token as valid. */
2
+ export declare function registerMcpToken(token: string): void;
3
+ /** Remove an MCP token (called on session cleanup). */
4
+ export declare function removeMcpToken(token: string): void;
5
+ /** Check if an MCP token is valid. */
6
+ export declare function validateMcpToken(token: string): boolean;
7
+ /** Returns the command, args, and env needed to launch the MCP server process. */
8
+ export declare function getMcpServerDef(): {
9
+ command: string;
10
+ args: string[];
11
+ env: Record<string, string>;
12
+ };
13
+ export declare const MCP_SYSTEM_PROMPT: string;
14
+ /**
15
+ * Additional system prompt appended for chain continuation sessions.
16
+ * Tells the LLM that this is a continuation and how to search previous chain links.
17
+ */
18
+ export declare const CHAIN_SYSTEM_PROMPT: string;
19
+ /**
20
+ * Get or create an MCP config file.
21
+ * When sessionUuid is provided, creates a per-session config that includes
22
+ * ATOO_CURRENT_SESSION_UUID in the env — enabling chain-scoped search.
23
+ */
24
+ export declare function getMcpConfigPath(sessionUuid?: string): string;
25
+ /**
26
+ * Clean up a per-session MCP config file.
27
+ */
28
+ export declare function cleanupMcpConfig(sessionUuid: string): void;
29
+ /**
30
+ * Clean up stale per-session MCP config files older than maxAgeMs.
31
+ */
32
+ export declare function cleanupStaleMcpConfigs(maxAgeMs?: number): void;