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.
- package/LICENSE +21 -0
- package/README.github.md +322 -0
- package/README.md +112 -0
- package/README.npm.md +112 -0
- package/bin/atoo-studio.js +90 -0
- package/dist/src/agents/claude-code-terminal/adapter.d.ts +42 -0
- package/dist/src/agents/claude-code-terminal/adapter.js +166 -0
- package/dist/src/agents/claude-code-terminal/index.d.ts +13 -0
- package/dist/src/agents/claude-code-terminal/index.js +45 -0
- package/dist/src/agents/claude-code-terminal/spawner.d.ts +9 -0
- package/dist/src/agents/claude-code-terminal/spawner.js +37 -0
- package/dist/src/agents/claude-code-terminal-chatro/adapter.d.ts +51 -0
- package/dist/src/agents/claude-code-terminal-chatro/adapter.js +301 -0
- package/dist/src/agents/claude-code-terminal-chatro/index.d.ts +13 -0
- package/dist/src/agents/claude-code-terminal-chatro/index.js +45 -0
- package/dist/src/agents/claude-code-terminal-chatro/jsonl-watcher.d.ts +67 -0
- package/dist/src/agents/claude-code-terminal-chatro/jsonl-watcher.js +431 -0
- package/dist/src/agents/claude-code-terminal-chatro/spawner.d.ts +9 -0
- package/dist/src/agents/claude-code-terminal-chatro/spawner.js +37 -0
- package/dist/src/agents/codex-terminal/adapter.d.ts +40 -0
- package/dist/src/agents/codex-terminal/adapter.js +160 -0
- package/dist/src/agents/codex-terminal/index.d.ts +13 -0
- package/dist/src/agents/codex-terminal/index.js +47 -0
- package/dist/src/agents/codex-terminal/spawner.d.ts +9 -0
- package/dist/src/agents/codex-terminal/spawner.js +56 -0
- package/dist/src/agents/codex-terminal-chatro/adapter.d.ts +58 -0
- package/dist/src/agents/codex-terminal-chatro/adapter.js +266 -0
- package/dist/src/agents/codex-terminal-chatro/index.d.ts +13 -0
- package/dist/src/agents/codex-terminal-chatro/index.js +50 -0
- package/dist/src/agents/codex-terminal-chatro/jsonl-watcher.d.ts +36 -0
- package/dist/src/agents/codex-terminal-chatro/jsonl-watcher.js +205 -0
- package/dist/src/agents/codex-terminal-chatro/spawner.d.ts +9 -0
- package/dist/src/agents/codex-terminal-chatro/spawner.js +57 -0
- package/dist/src/agents/lib/chain-builder.d.ts +21 -0
- package/dist/src/agents/lib/chain-builder.js +139 -0
- package/dist/src/agents/lib/claude/fs-sessions.d.ts +31 -0
- package/dist/src/agents/lib/claude/fs-sessions.js +329 -0
- package/dist/src/agents/lib/claude/jsonl-writer.d.ts +32 -0
- package/dist/src/agents/lib/claude/jsonl-writer.js +342 -0
- package/dist/src/agents/lib/claude/workspace-trust.d.ts +1 -0
- package/dist/src/agents/lib/claude/workspace-trust.js +29 -0
- package/dist/src/agents/lib/codex/fs-sessions.d.ts +34 -0
- package/dist/src/agents/lib/codex/fs-sessions.js +255 -0
- package/dist/src/agents/lib/codex/jsonl-mapper.d.ts +11 -0
- package/dist/src/agents/lib/codex/jsonl-mapper.js +154 -0
- package/dist/src/agents/lib/codex/jsonl-writer.d.ts +8 -0
- package/dist/src/agents/lib/codex/jsonl-writer.js +440 -0
- package/dist/src/agents/lib/fs-tracking.d.ts +36 -0
- package/dist/src/agents/lib/fs-tracking.js +109 -0
- package/dist/src/agents/lib/pty-activity-tracker.d.ts +37 -0
- package/dist/src/agents/lib/pty-activity-tracker.js +105 -0
- package/dist/src/agents/lib/session-id-utils.d.ts +46 -0
- package/dist/src/agents/lib/session-id-utils.js +147 -0
- package/dist/src/agents/lib/session-precreate.d.ts +17 -0
- package/dist/src/agents/lib/session-precreate.js +177 -0
- package/dist/src/agents/registry.d.ts +72 -0
- package/dist/src/agents/registry.js +337 -0
- package/dist/src/agents/types.d.ts +135 -0
- package/dist/src/agents/types.js +1 -0
- package/dist/src/auth/crypto-key.d.ts +6 -0
- package/dist/src/auth/crypto-key.js +45 -0
- package/dist/src/auth/middleware.d.ts +18 -0
- package/dist/src/auth/middleware.js +54 -0
- package/dist/src/auth/password.d.ts +2 -0
- package/dist/src/auth/password.js +12 -0
- package/dist/src/auth/session.d.ts +10 -0
- package/dist/src/auth/session.js +33 -0
- package/dist/src/auth/totp.d.ts +12 -0
- package/dist/src/auth/totp.js +61 -0
- package/dist/src/auth/webauthn.d.ts +6 -0
- package/dist/src/auth/webauthn.js +117 -0
- package/dist/src/config.d.ts +10 -0
- package/dist/src/config.js +16 -0
- package/dist/src/database/connection-manager.d.ts +25 -0
- package/dist/src/database/connection-manager.js +211 -0
- package/dist/src/database/discovery/container.d.ts +6 -0
- package/dist/src/database/discovery/container.js +226 -0
- package/dist/src/database/discovery/env-parser.d.ts +9 -0
- package/dist/src/database/discovery/env-parser.js +525 -0
- package/dist/src/database/discovery/local-files.d.ts +6 -0
- package/dist/src/database/discovery/local-files.js +58 -0
- package/dist/src/database/discovery/port-scan.d.ts +7 -0
- package/dist/src/database/discovery/port-scan.js +61 -0
- package/dist/src/database/drivers/cassandra.d.ts +12 -0
- package/dist/src/database/drivers/cassandra.js +91 -0
- package/dist/src/database/drivers/clickhouse.d.ts +11 -0
- package/dist/src/database/drivers/clickhouse.js +127 -0
- package/dist/src/database/drivers/elasticsearch.d.ts +12 -0
- package/dist/src/database/drivers/elasticsearch.js +169 -0
- package/dist/src/database/drivers/influxdb.d.ts +14 -0
- package/dist/src/database/drivers/influxdb.js +194 -0
- package/dist/src/database/drivers/memcached.d.ts +11 -0
- package/dist/src/database/drivers/memcached.js +117 -0
- package/dist/src/database/drivers/mongodb.d.ts +12 -0
- package/dist/src/database/drivers/mongodb.js +128 -0
- package/dist/src/database/drivers/mysql.d.ts +11 -0
- package/dist/src/database/drivers/mysql.js +112 -0
- package/dist/src/database/drivers/neo4j.d.ts +11 -0
- package/dist/src/database/drivers/neo4j.js +158 -0
- package/dist/src/database/drivers/postgresql.d.ts +11 -0
- package/dist/src/database/drivers/postgresql.js +133 -0
- package/dist/src/database/drivers/redis.d.ts +11 -0
- package/dist/src/database/drivers/redis.js +91 -0
- package/dist/src/database/drivers/sqlite.d.ts +10 -0
- package/dist/src/database/drivers/sqlite.js +100 -0
- package/dist/src/database/query-stream.d.ts +5 -0
- package/dist/src/database/query-stream.js +75 -0
- package/dist/src/database/types.d.ts +71 -0
- package/dist/src/database/types.js +1 -0
- package/dist/src/events/index.d.ts +3 -0
- package/dist/src/events/index.js +3 -0
- package/dist/src/events/types.d.ts +214 -0
- package/dist/src/events/types.js +22 -0
- package/dist/src/events/wire.d.ts +114 -0
- package/dist/src/events/wire.js +296 -0
- package/dist/src/fs-monitor-types.d.ts +24 -0
- package/dist/src/fs-monitor-types.js +1 -0
- package/dist/src/fs-monitor.d.ts +80 -0
- package/dist/src/fs-monitor.js +637 -0
- package/dist/src/handlers/auth.d.ts +1 -0
- package/dist/src/handlers/auth.js +170 -0
- package/dist/src/handlers/changes.d.ts +1 -0
- package/dist/src/handlers/changes.js +203 -0
- package/dist/src/handlers/containers.d.ts +12 -0
- package/dist/src/handlers/containers.js +379 -0
- package/dist/src/handlers/databases.d.ts +3 -0
- package/dist/src/handlers/databases.js +327 -0
- package/dist/src/handlers/environments.d.ts +3 -0
- package/dist/src/handlers/environments.js +286 -0
- package/dist/src/handlers/github.d.ts +1 -0
- package/dist/src/handlers/github.js +153 -0
- package/dist/src/handlers/projects.d.ts +1 -0
- package/dist/src/handlers/projects.js +895 -0
- package/dist/src/handlers/ssh.d.ts +1 -0
- package/dist/src/handlers/ssh.js +162 -0
- package/dist/src/handlers/users.d.ts +1 -0
- package/dist/src/handlers/users.js +195 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +228 -0
- package/dist/src/mcp/config.d.ts +32 -0
- package/dist/src/mcp/config.js +227 -0
- package/dist/src/mcp/server.d.ts +1 -0
- package/dist/src/mcp/server.js +574 -0
- package/dist/src/serial/cuse-device.d.ts +19 -0
- package/dist/src/serial/cuse-device.js +260 -0
- package/dist/src/serial/manager.d.ts +63 -0
- package/dist/src/serial/manager.js +206 -0
- package/dist/src/serial/pty-pair.d.ts +16 -0
- package/dist/src/serial/pty-pair.js +68 -0
- package/dist/src/services/fs-browser.d.ts +14 -0
- package/dist/src/services/fs-browser.js +98 -0
- package/dist/src/services/git-ops.d.ts +78 -0
- package/dist/src/services/git-ops.js +288 -0
- package/dist/src/services/github-ops.d.ts +104 -0
- package/dist/src/services/github-ops.js +192 -0
- package/dist/src/services/obfuscation.d.ts +2 -0
- package/dist/src/services/obfuscation.js +16 -0
- package/dist/src/services/preview/headless-backend.d.ts +62 -0
- package/dist/src/services/preview/headless-backend.js +698 -0
- package/dist/src/services/preview/injected-scripts.d.ts +9 -0
- package/dist/src/services/preview/injected-scripts.js +232 -0
- package/dist/src/services/preview/preview-backend.d.ts +92 -0
- package/dist/src/services/preview/preview-backend.js +15 -0
- package/dist/src/services/preview/universal-setter.d.ts +7 -0
- package/dist/src/services/preview/universal-setter.js +46 -0
- package/dist/src/services/preview-manager.d.ts +50 -0
- package/dist/src/services/preview-manager.js +216 -0
- package/dist/src/services/project-watcher.d.ts +6 -0
- package/dist/src/services/project-watcher.js +307 -0
- package/dist/src/services/remote-fs-browser.d.ts +11 -0
- package/dist/src/services/remote-fs-browser.js +50 -0
- package/dist/src/services/remote-git-ops.d.ts +71 -0
- package/dist/src/services/remote-git-ops.js +215 -0
- package/dist/src/services/session-search.d.ts +56 -0
- package/dist/src/services/session-search.js +303 -0
- package/dist/src/services/ssh-manager.d.ts +44 -0
- package/dist/src/services/ssh-manager.js +359 -0
- package/dist/src/session-writer.d.ts +9 -0
- package/dist/src/session-writer.js +66 -0
- package/dist/src/spawner.d.ts +56 -0
- package/dist/src/spawner.js +135 -0
- package/dist/src/state/db.d.ts +214 -0
- package/dist/src/state/db.js +897 -0
- package/dist/src/state/store.d.ts +37 -0
- package/dist/src/state/store.js +108 -0
- package/dist/src/state/types.d.ts +13 -0
- package/dist/src/state/types.js +1 -0
- package/dist/src/web/devtools-proxy.d.ts +7 -0
- package/dist/src/web/devtools-proxy.js +176 -0
- package/dist/src/web/port-proxy.d.ts +15 -0
- package/dist/src/web/port-proxy.js +124 -0
- package/dist/src/web/preview-ws.d.ts +5 -0
- package/dist/src/web/preview-ws.js +207 -0
- package/dist/src/web/server.d.ts +6 -0
- package/dist/src/web/server.js +1694 -0
- package/dist/src/ws/agent-ws.d.ts +5 -0
- package/dist/src/ws/agent-ws.js +93 -0
- package/frontend/dist/assets/_basePickBy-B-LibQ4-.js +1 -0
- package/frontend/dist/assets/_baseUniq-CprifHap.js +1 -0
- package/frontend/dist/assets/_createAssigner-ByDUqGii.js +1 -0
- package/frontend/dist/assets/abap-DuT-3z4x.js +1 -0
- package/frontend/dist/assets/addon-fit-CxQet2ja.js +1 -0
- package/frontend/dist/assets/addon-web-links-D_jRkPIl.js +1 -0
- package/frontend/dist/assets/apex-B-em86xX.js +1 -0
- package/frontend/dist/assets/api-SUPuHhSY.js +2 -0
- package/frontend/dist/assets/arc-Z0_eVteO.js +1 -0
- package/frontend/dist/assets/architecture-PBZL5I3N-hvVXGhqd.js +1 -0
- package/frontend/dist/assets/architectureDiagram-2XIMDMQ5-DiHPxX4j.js +36 -0
- package/frontend/dist/assets/array-CwG8vNfn.js +1 -0
- package/frontend/dist/assets/auth-store-R7eW5SVu.js +1 -0
- package/frontend/dist/assets/azcli-Bg9wQloi.js +1 -0
- package/frontend/dist/assets/bat-BM46z99L.js +1 -0
- package/frontend/dist/assets/bicep-DcBsJUfh.js +2 -0
- package/frontend/dist/assets/blockDiagram-WCTKOSBZ-C40u_hLo.js +132 -0
- package/frontend/dist/assets/c4Diagram-IC4MRINW-Ct7LjWFQ.js +10 -0
- package/frontend/dist/assets/cameligo-zw7JTtim.js +1 -0
- package/frontend/dist/assets/channel-ClCsE6HN.js +1 -0
- package/frontend/dist/assets/chunk-4BX2VUAB-zZ6P90VO.js +1 -0
- package/frontend/dist/assets/chunk-55IACEB6-DXllTDQl.js +1 -0
- package/frontend/dist/assets/chunk-7E7YKBS2-7zRaOLjj.js +1 -0
- package/frontend/dist/assets/chunk-7R4GIKGN-Csst1274.js +80 -0
- package/frontend/dist/assets/chunk-C72U2L5F-_JbQPbLN.js +1 -0
- package/frontend/dist/assets/chunk-CFjPhJqf.js +1 -0
- package/frontend/dist/assets/chunk-EGIJ26TM-B--aFyPw.js +1 -0
- package/frontend/dist/assets/chunk-FMBD7UC4-DVR34RNb.js +15 -0
- package/frontend/dist/assets/chunk-GEFDOKGD-CnmN6cC8.js +2 -0
- package/frontend/dist/assets/chunk-JSJVCQXG-CWxHBzeJ.js +1 -0
- package/frontend/dist/assets/chunk-KX2RTZJC-DkRk56s7.js +1 -0
- package/frontend/dist/assets/chunk-KYZI473N-DCCsG2dK.js +53 -0
- package/frontend/dist/assets/chunk-L3YUKLVL-C-DkZTMr.js +1 -0
- package/frontend/dist/assets/chunk-MX3YWQON-OUdzv5sZ.js +1 -0
- package/frontend/dist/assets/chunk-NQ4KR5QH-Bpu9FsM7.js +220 -0
- package/frontend/dist/assets/chunk-O4XLMI2P-BMLK6_ib.js +7 -0
- package/frontend/dist/assets/chunk-OZEHJAEY-CNNiJtG0.js +1 -0
- package/frontend/dist/assets/chunk-PQ6SQG4A-evVHD3KM.js +1 -0
- package/frontend/dist/assets/chunk-PU5JKC2W-DPFTYuvl.js +70 -0
- package/frontend/dist/assets/chunk-QZHKN3VN-JRdddPvu.js +1 -0
- package/frontend/dist/assets/chunk-R5LLSJPH-CHQzVVOV.js +1 -0
- package/frontend/dist/assets/chunk-WL4C6EOR-BNFU6IIi.js +189 -0
- package/frontend/dist/assets/chunk-XIRO2GV7-98T93G85.js +1 -0
- package/frontend/dist/assets/chunk-XZSTWKYB-BcW3cyNp.js +94 -0
- package/frontend/dist/assets/chunk-YBOYWFTD-BgKO1qAJ.js +1 -0
- package/frontend/dist/assets/classDiagram-VBA2DB6C-DikXzgcD.js +1 -0
- package/frontend/dist/assets/classDiagram-v2-RAHNMMFH-D7E3tQUK.js +1 -0
- package/frontend/dist/assets/clojure-FspFoNNQ.js +1 -0
- package/frontend/dist/assets/clone-mOXuZa7C.js +1 -0
- package/frontend/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/frontend/dist/assets/coffee-13n8Bk2W.js +1 -0
- package/frontend/dist/assets/cose-bilkent-S5V4N54A-zUOWQqLe.js +1 -0
- package/frontend/dist/assets/cpp-BVm2xGEs.js +1 -0
- package/frontend/dist/assets/csharp-D2kAWmUm.js +1 -0
- package/frontend/dist/assets/csp-Ezvgpf0e.js +1 -0
- package/frontend/dist/assets/css-CYxRwcFy.js +3 -0
- package/frontend/dist/assets/css.worker-Cd5h-ZOL.js +89 -0
- package/frontend/dist/assets/cssMode-CrXej49V.js +1 -0
- package/frontend/dist/assets/cypher-jg3SGErc.js +1 -0
- package/frontend/dist/assets/cytoscape.esm-kyyvzxNV.js +321 -0
- package/frontend/dist/assets/dagre-DH4bgZO7.js +1 -0
- package/frontend/dist/assets/dagre-KLK3FWXG-DNSqDkwT.js +4 -0
- package/frontend/dist/assets/dart-179jqhK4.js +1 -0
- package/frontend/dist/assets/defaultLocale-Dda4OpKy.js +1 -0
- package/frontend/dist/assets/diagram-E7M64L7V-RqPNT5Vs.js +24 -0
- package/frontend/dist/assets/diagram-IFDJBPK2-B-5NRyaE.js +43 -0
- package/frontend/dist/assets/diagram-P4PSJMXO-BrP69Hk0.js +24 -0
- package/frontend/dist/assets/dist-CU_Nb1G5.js +1 -0
- package/frontend/dist/assets/dockerfile-CIAtSGxS.js +1 -0
- package/frontend/dist/assets/ecl-CGVKfDxD.js +1 -0
- package/frontend/dist/assets/editor-Br_kD0ds.css +1 -0
- package/frontend/dist/assets/editor.api2-YXkDn0Gm.js +872 -0
- package/frontend/dist/assets/editor.main-fBaXZjJ0.js +6 -0
- package/frontend/dist/assets/elixir-BZ-6w0y3.js +1 -0
- package/frontend/dist/assets/erDiagram-INFDFZHY-BYiB9NYg.js +70 -0
- package/frontend/dist/assets/flow9-CVuOjTMv.js +1 -0
- package/frontend/dist/assets/flowDiagram-PKNHOUZH-Cwq47rsR.js +162 -0
- package/frontend/dist/assets/freemarker2-DM-pztJU.js +3 -0
- package/frontend/dist/assets/fsharp-q0pGJYr6.js +1 -0
- package/frontend/dist/assets/ganttDiagram-A5KZAMGK-Dnx3szD9.js +292 -0
- package/frontend/dist/assets/gitGraph-HDMCJU4V-COlTQ7bA.js +1 -0
- package/frontend/dist/assets/gitGraphDiagram-K3NZZRJ6-BaUxboNc.js +65 -0
- package/frontend/dist/assets/go-dzSPfdEO.js +1 -0
- package/frontend/dist/assets/graphlib-kEFlkt3U.js +1 -0
- package/frontend/dist/assets/graphql-CG4OUoEV.js +1 -0
- package/frontend/dist/assets/handlebars-BbK53Vec.js +1 -0
- package/frontend/dist/assets/hcl-Cy14JPk3.js +1 -0
- package/frontend/dist/assets/html-DYtTQNOG.js +1 -0
- package/frontend/dist/assets/html.worker-BjVEKLoU.js +502 -0
- package/frontend/dist/assets/htmlMode-C6GTouth.js +1 -0
- package/frontend/dist/assets/index-DMLxes_u.js +157 -0
- package/frontend/dist/assets/index-DmzeqkB1.css +1 -0
- package/frontend/dist/assets/info-3K5VOQVL-DBtHyA4C.js +1 -0
- package/frontend/dist/assets/infoDiagram-LFFYTUFH-yBXLgMPI.js +2 -0
- package/frontend/dist/assets/ini-Pbg8HGVD.js +1 -0
- package/frontend/dist/assets/init-D6KNwrax.js +1 -0
- package/frontend/dist/assets/ishikawaDiagram-PHBUUO56-Bld4two_.js +70 -0
- package/frontend/dist/assets/java-BmVu6Qrl.js +1 -0
- package/frontend/dist/assets/javascript-PbfQEdcJ.js +1 -0
- package/frontend/dist/assets/journeyDiagram-4ABVD52K-4HyMd4R2.js +139 -0
- package/frontend/dist/assets/json.worker-DqU5Wxnl.js +58 -0
- package/frontend/dist/assets/jsonMode-CASsGppE.js +7 -0
- package/frontend/dist/assets/julia-3cGnieBq.js +1 -0
- package/frontend/dist/assets/kanban-definition-K7BYSVSG-DpgsZmpG.js +89 -0
- package/frontend/dist/assets/katex-CEw3x5bf.js +261 -0
- package/frontend/dist/assets/kotlin-BuWkVcfV.js +1 -0
- package/frontend/dist/assets/less-CJ_VPy2C.js +2 -0
- package/frontend/dist/assets/lexon-BygAuZPu.js +1 -0
- package/frontend/dist/assets/line-CA_wh_TY.js +1 -0
- package/frontend/dist/assets/linear-BAcLW45z.js +1 -0
- package/frontend/dist/assets/liquid-kz84dle6.js +1 -0
- package/frontend/dist/assets/lspLanguageFeatures-C7hAHFn1.js +4 -0
- package/frontend/dist/assets/lua-C8Xs3dCx.js +1 -0
- package/frontend/dist/assets/m3-DTJeKBk4.js +1 -0
- package/frontend/dist/assets/markdown-QCgx8JqZ.js +1 -0
- package/frontend/dist/assets/math-D0YcMJAn.js +1 -0
- package/frontend/dist/assets/mdx-yRw0ap-E.js +1 -0
- package/frontend/dist/assets/mermaid-parser.core-DAeTodBQ.js +4 -0
- package/frontend/dist/assets/mindmap-definition-YRQLILUH-CoNlFyVl.js +68 -0
- package/frontend/dist/assets/mips-DopWaYgE.js +1 -0
- package/frontend/dist/assets/monaco.contribution-DeY0Qei-.js +2 -0
- package/frontend/dist/assets/msdax-BDis4ARV.js +1 -0
- package/frontend/dist/assets/mysql-BV6MLsOI.js +1 -0
- package/frontend/dist/assets/objective-c-B1UuzKs6.js +1 -0
- package/frontend/dist/assets/ordinal-jM7S0YHN.js +1 -0
- package/frontend/dist/assets/packet-RMMSAZCW-FF6-Tmai.js +1 -0
- package/frontend/dist/assets/pascal-BkvESCrc.js +1 -0
- package/frontend/dist/assets/pascaligo-lTy0kZYr.js +1 -0
- package/frontend/dist/assets/path-DNPd7Py7.js +1 -0
- package/frontend/dist/assets/perl-CrtUPXLV.js +1 -0
- package/frontend/dist/assets/pgsql-B9IbNWx2.js +1 -0
- package/frontend/dist/assets/php-CXvQBY2p.js +1 -0
- package/frontend/dist/assets/pie-UPGHQEXC-CFvXY2o-.js +1 -0
- package/frontend/dist/assets/pieDiagram-SKSYHLDU-CM_hbCcn.js +30 -0
- package/frontend/dist/assets/pla-DxBxuqWu.js +1 -0
- package/frontend/dist/assets/postiats-OkEuT5YF.js +1 -0
- package/frontend/dist/assets/powerquery-CMx5Tq4K.js +1 -0
- package/frontend/dist/assets/powershell-CstRxrEc.js +1 -0
- package/frontend/dist/assets/preload-helper-D4M6sveU.js +1 -0
- package/frontend/dist/assets/protobuf-Bx0Z-uRj.js +2 -0
- package/frontend/dist/assets/pug--W8vanWl.js +1 -0
- package/frontend/dist/assets/python-DA0rnlw3.js +1 -0
- package/frontend/dist/assets/qsharp-CRtr0YbN.js +1 -0
- package/frontend/dist/assets/quadrantDiagram-337W2JSQ-B3n3IUhC.js +7 -0
- package/frontend/dist/assets/r-C6E1d6iv.js +1 -0
- package/frontend/dist/assets/radar-KQ55EAFF-MPZu7SdX.js +1 -0
- package/frontend/dist/assets/razor-yd73uata.js +1 -0
- package/frontend/dist/assets/redis-Dx13voP3.js +1 -0
- package/frontend/dist/assets/redshift-D66HwlyV.js +1 -0
- package/frontend/dist/assets/requirementDiagram-Z7DCOOCP-CorP7L7F.js +73 -0
- package/frontend/dist/assets/restructuredtext-DQT2NKJ2.js +1 -0
- package/frontend/dist/assets/rough.esm-DxAX5Vpo.js +1 -0
- package/frontend/dist/assets/ruby-iFXI8hwH.js +1 -0
- package/frontend/dist/assets/rust-CSKiei34.js +1 -0
- package/frontend/dist/assets/sankeyDiagram-WA2Y5GQK-RDx6Bd-B.js +10 -0
- package/frontend/dist/assets/sb-Bo3ttdP2.js +1 -0
- package/frontend/dist/assets/scala-BC1D-Nxp.js +1 -0
- package/frontend/dist/assets/scheme-Z4OAo4Lv.js +1 -0
- package/frontend/dist/assets/scss-BvrdPs6B.js +3 -0
- package/frontend/dist/assets/sequenceDiagram-2WXFIKYE-JMqJSFq6.js +145 -0
- package/frontend/dist/assets/shell-Bh_aCyF-.js +1 -0
- package/frontend/dist/assets/solidity-CWHj6tSe.js +1 -0
- package/frontend/dist/assets/sophia-raoNtKtm.js +1 -0
- package/frontend/dist/assets/sparql-XzmoGnue.js +1 -0
- package/frontend/dist/assets/sql-BD0i9Gvg.js +1 -0
- package/frontend/dist/assets/src-Bn-kKzs7.js +1 -0
- package/frontend/dist/assets/st-DtVKyms6.js +1 -0
- package/frontend/dist/assets/stateDiagram-RAJIS63D-CgFfENdy.js +1 -0
- package/frontend/dist/assets/stateDiagram-v2-FVOUBMTO-C4Hh2P-U.js +1 -0
- package/frontend/dist/assets/swift--UZs77wT.js +1 -0
- package/frontend/dist/assets/systemverilog-CDnBSWUd.js +1 -0
- package/frontend/dist/assets/tcl-DdCEuTHZ.js +1 -0
- package/frontend/dist/assets/timeline-definition-YZTLITO2-BnatPBR5.js +61 -0
- package/frontend/dist/assets/treemap-KZPCXAKY-qb1Pl9la.js +1 -0
- package/frontend/dist/assets/ts.worker-DyPAEIuH.js +67719 -0
- package/frontend/dist/assets/tsMode-iuvyEpyO.js +11 -0
- package/frontend/dist/assets/twig-SSL-Altf.js +1 -0
- package/frontend/dist/assets/typescript-17918Hud.js +1 -0
- package/frontend/dist/assets/typespec-BT7S0ETg.js +1 -0
- package/frontend/dist/assets/vb-CrIgucua.js +1 -0
- package/frontend/dist/assets/vennDiagram-LZ73GAT5-DygS4Zzd.js +34 -0
- package/frontend/dist/assets/wgsl-BeKc3oEp.js +298 -0
- package/frontend/dist/assets/workers-DTfwKVoM.js +1 -0
- package/frontend/dist/assets/xml-CBMr_Wbw.js +1 -0
- package/frontend/dist/assets/xterm-BrP-ENHg.css +1 -0
- package/frontend/dist/assets/xterm-CBX2m0YM.js +36 -0
- package/frontend/dist/assets/xychartDiagram-JWTSCODW-D6wY1Jwd.js +7 -0
- package/frontend/dist/assets/yaml-CTjCH7Bv.js +1 -0
- package/frontend/dist/fonts/inter-300.ttf +0 -0
- package/frontend/dist/fonts/inter-400.ttf +0 -0
- package/frontend/dist/fonts/inter-500.ttf +0 -0
- package/frontend/dist/fonts/inter-600.ttf +0 -0
- package/frontend/dist/fonts/inter-700.ttf +0 -0
- package/frontend/dist/index.html +49 -0
- package/frontend/dist/logo_192x192.png +0 -0
- package/frontend/dist/logo_32x32.png +0 -0
- package/frontend/dist/logo_512x512.png +0 -0
- package/frontend/dist/logo_64x64.png +0 -0
- package/frontend/dist/logobg_192x192.png +0 -0
- package/frontend/dist/logobg_512x512.png +0 -0
- package/frontend/dist/logobg_64x64.png +0 -0
- package/frontend/dist/manifest.json +25 -0
- package/frontend/dist/sw.js +22 -0
- package/package.json +74 -7
- package/preload/Makefile +12 -0
- package/preload/atoo-studio-preload.c +647 -0
- package/preload/atoo-studio-preload.so +0 -0
- package/setup-cuse.sh +260 -0
- package/setup.sh +81 -0
- package/src/serial/native/binding.gyp +10 -0
- package/src/serial/native/pty_pair.c +222 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { Client } from 'ssh2';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import net from 'net';
|
|
4
|
+
import { CA_CERT_PATH, PROXY_PORT } from '../config.js';
|
|
5
|
+
import { deobfuscate } from './obfuscation.js';
|
|
6
|
+
class SshManager {
|
|
7
|
+
connections = new Map();
|
|
8
|
+
async connect(config) {
|
|
9
|
+
if (this.connections.has(config.id) && this.connections.get(config.id).connected) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const client = new Client();
|
|
13
|
+
const connectConfig = {
|
|
14
|
+
host: config.host,
|
|
15
|
+
port: config.port,
|
|
16
|
+
username: config.username,
|
|
17
|
+
};
|
|
18
|
+
if (config.auth_method === 'password' && config.password_obfuscated) {
|
|
19
|
+
connectConfig.password = deobfuscate(config.password_obfuscated);
|
|
20
|
+
}
|
|
21
|
+
else if (config.auth_method === 'privatekey' && config.private_key_obfuscated) {
|
|
22
|
+
connectConfig.privateKey = deobfuscate(config.private_key_obfuscated);
|
|
23
|
+
if (config.passphrase_obfuscated) {
|
|
24
|
+
connectConfig.passphrase = deobfuscate(config.passphrase_obfuscated);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else if (config.auth_method === 'systemkey' && config.system_key_path) {
|
|
28
|
+
connectConfig.privateKey = fs.readFileSync(config.system_key_path, 'utf-8');
|
|
29
|
+
if (config.passphrase_obfuscated) {
|
|
30
|
+
connectConfig.passphrase = deobfuscate(config.passphrase_obfuscated);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const active = {
|
|
34
|
+
id: config.id,
|
|
35
|
+
client,
|
|
36
|
+
config,
|
|
37
|
+
connected: false,
|
|
38
|
+
reverseTunnelServers: [],
|
|
39
|
+
forwardTunnels: new Map(),
|
|
40
|
+
};
|
|
41
|
+
this.connections.set(config.id, active);
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
client.on('ready', async () => {
|
|
44
|
+
console.log(`[ssh] Connected to ${config.host}:${config.port} as ${config.username}`);
|
|
45
|
+
active.connected = true;
|
|
46
|
+
try {
|
|
47
|
+
// Setup reverse tunnels so remote claude can reach our proxy
|
|
48
|
+
await this.setupReverseTunnel(config.id, 3000, 3010);
|
|
49
|
+
await this.setupReverseTunnel(config.id, PROXY_PORT, PROXY_PORT);
|
|
50
|
+
// Upload CA cert to remote
|
|
51
|
+
await this.uploadCaCert(config.id);
|
|
52
|
+
resolve();
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
console.error(`[ssh] Post-connect setup failed:`, err.message);
|
|
56
|
+
resolve(); // still connected, just tunnel setup failed
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
client.on('error', (err) => {
|
|
60
|
+
console.error(`[ssh] Connection error for ${config.id}:`, err.message);
|
|
61
|
+
active.connected = false;
|
|
62
|
+
active.error = err.message;
|
|
63
|
+
if (!active.connected)
|
|
64
|
+
reject(err);
|
|
65
|
+
});
|
|
66
|
+
client.on('end', () => {
|
|
67
|
+
console.log(`[ssh] Connection ended for ${config.id}`);
|
|
68
|
+
active.connected = false;
|
|
69
|
+
this.cleanupConnection(config.id);
|
|
70
|
+
});
|
|
71
|
+
client.on('close', () => {
|
|
72
|
+
active.connected = false;
|
|
73
|
+
this.cleanupConnection(config.id);
|
|
74
|
+
});
|
|
75
|
+
client.connect(connectConfig);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async disconnect(id) {
|
|
79
|
+
const active = this.connections.get(id);
|
|
80
|
+
if (!active)
|
|
81
|
+
return;
|
|
82
|
+
this.cleanupConnection(id);
|
|
83
|
+
active.client.end();
|
|
84
|
+
this.connections.delete(id);
|
|
85
|
+
console.log(`[ssh] Disconnected ${id}`);
|
|
86
|
+
}
|
|
87
|
+
async disconnectAll() {
|
|
88
|
+
for (const id of Array.from(this.connections.keys())) {
|
|
89
|
+
await this.disconnect(id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
isConnected(id) {
|
|
93
|
+
return this.connections.get(id)?.connected ?? false;
|
|
94
|
+
}
|
|
95
|
+
getStatus(id) {
|
|
96
|
+
const active = this.connections.get(id);
|
|
97
|
+
if (!active)
|
|
98
|
+
return { connected: false };
|
|
99
|
+
return { connected: active.connected, error: active.error };
|
|
100
|
+
}
|
|
101
|
+
// SFTP operations
|
|
102
|
+
async getSftp(id) {
|
|
103
|
+
const active = this.connections.get(id);
|
|
104
|
+
if (!active || !active.connected)
|
|
105
|
+
throw new Error('SSH not connected');
|
|
106
|
+
if (active.sftp)
|
|
107
|
+
return active.sftp;
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
active.client.sftp((err, sftp) => {
|
|
110
|
+
if (err)
|
|
111
|
+
return reject(err);
|
|
112
|
+
active.sftp = sftp;
|
|
113
|
+
resolve(sftp);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async sftpReaddir(id, dirPath) {
|
|
118
|
+
const sftp = await this.getSftp(id);
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
sftp.readdir(dirPath, (err, list) => {
|
|
121
|
+
if (err)
|
|
122
|
+
return reject(err);
|
|
123
|
+
const entries = list
|
|
124
|
+
.filter(item => !item.filename.startsWith('.'))
|
|
125
|
+
.map(item => ({
|
|
126
|
+
name: item.filename,
|
|
127
|
+
type: item.attrs.isDirectory() ? 'dir' : 'file',
|
|
128
|
+
size: item.attrs.size,
|
|
129
|
+
}))
|
|
130
|
+
.sort((a, b) => {
|
|
131
|
+
if (a.type !== b.type)
|
|
132
|
+
return a.type === 'dir' ? -1 : 1;
|
|
133
|
+
return a.name.localeCompare(b.name);
|
|
134
|
+
});
|
|
135
|
+
resolve(entries);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async sftpMkdir(id, dirPath) {
|
|
140
|
+
const sftp = await this.getSftp(id);
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
sftp.mkdir(dirPath, (err) => {
|
|
143
|
+
if (err)
|
|
144
|
+
return reject(err);
|
|
145
|
+
resolve();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async sftpReadFile(id, filePath) {
|
|
150
|
+
const sftp = await this.getSftp(id);
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
let data = '';
|
|
153
|
+
const stream = sftp.createReadStream(filePath, { encoding: 'utf8' });
|
|
154
|
+
stream.on('data', (chunk) => { data += chunk; });
|
|
155
|
+
stream.on('end', () => resolve(data));
|
|
156
|
+
stream.on('error', reject);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async sftpWriteFile(id, filePath, content) {
|
|
160
|
+
const sftp = await this.getSftp(id);
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
const stream = sftp.createWriteStream(filePath);
|
|
163
|
+
stream.on('close', () => resolve());
|
|
164
|
+
stream.on('error', reject);
|
|
165
|
+
stream.end(content);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async sftpStat(id, filePath) {
|
|
169
|
+
const sftp = await this.getSftp(id);
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
sftp.stat(filePath, (err, stats) => {
|
|
172
|
+
if (err)
|
|
173
|
+
return reject(err);
|
|
174
|
+
resolve(stats);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
async sftpUnlink(id, filePath) {
|
|
179
|
+
const sftp = await this.getSftp(id);
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
sftp.unlink(filePath, (err) => {
|
|
182
|
+
if (err)
|
|
183
|
+
return reject(err);
|
|
184
|
+
resolve();
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async sftpRmdir(id, dirPath) {
|
|
189
|
+
const sftp = await this.getSftp(id);
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
sftp.rmdir(dirPath, (err) => {
|
|
192
|
+
if (err)
|
|
193
|
+
return reject(err);
|
|
194
|
+
resolve();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
async sftpRename(id, oldPath, newPath) {
|
|
199
|
+
const sftp = await this.getSftp(id);
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
sftp.rename(oldPath, newPath, (err) => {
|
|
202
|
+
if (err)
|
|
203
|
+
return reject(err);
|
|
204
|
+
resolve();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
// Exec
|
|
209
|
+
async exec(id, cmd, opts) {
|
|
210
|
+
const active = this.connections.get(id);
|
|
211
|
+
if (!active || !active.connected)
|
|
212
|
+
throw new Error('SSH not connected');
|
|
213
|
+
const fullCmd = opts?.cwd ? `cd ${shellEscape(opts.cwd)} && ${cmd}` : cmd;
|
|
214
|
+
return new Promise((resolve, reject) => {
|
|
215
|
+
active.client.exec(fullCmd, (err, stream) => {
|
|
216
|
+
if (err)
|
|
217
|
+
return reject(err);
|
|
218
|
+
let stdout = '';
|
|
219
|
+
let stderr = '';
|
|
220
|
+
stream.on('data', (data) => { stdout += data.toString(); });
|
|
221
|
+
stream.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
222
|
+
stream.on('close', (code) => {
|
|
223
|
+
if (code !== 0 && stderr) {
|
|
224
|
+
reject(new Error(stderr.trim() || `Command failed with code ${code}`));
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
resolve(stdout);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async execPty(id, cmd, opts) {
|
|
234
|
+
const active = this.connections.get(id);
|
|
235
|
+
if (!active || !active.connected)
|
|
236
|
+
throw new Error('SSH not connected');
|
|
237
|
+
const fullCmd = opts.cwd ? `cd ${shellEscape(opts.cwd)} && ${cmd}` : cmd;
|
|
238
|
+
const envVars = opts.env || {};
|
|
239
|
+
return new Promise((resolve, reject) => {
|
|
240
|
+
active.client.exec(fullCmd, {
|
|
241
|
+
pty: {
|
|
242
|
+
rows: opts.rows || 30,
|
|
243
|
+
cols: opts.cols || 120,
|
|
244
|
+
term: 'xterm-256color',
|
|
245
|
+
},
|
|
246
|
+
env: envVars,
|
|
247
|
+
}, (err, stream) => {
|
|
248
|
+
if (err)
|
|
249
|
+
return reject(err);
|
|
250
|
+
resolve(stream);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// Reverse tunnel: remote:remotePort -> local:localPort
|
|
255
|
+
async setupReverseTunnel(id, remotePort, localPort) {
|
|
256
|
+
const active = this.connections.get(id);
|
|
257
|
+
if (!active || !active.connected)
|
|
258
|
+
throw new Error('SSH not connected');
|
|
259
|
+
// Register tcp connection handler before requesting forwarding
|
|
260
|
+
active.client.on('tcp connection', (info, accept, _reject) => {
|
|
261
|
+
if (info.destPort === remotePort) {
|
|
262
|
+
const channel = accept();
|
|
263
|
+
const socket = net.createConnection(localPort, '127.0.0.1', () => {
|
|
264
|
+
channel.pipe(socket);
|
|
265
|
+
socket.pipe(channel);
|
|
266
|
+
});
|
|
267
|
+
socket.on('error', () => channel.close());
|
|
268
|
+
channel.on('error', () => socket.destroy());
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
return new Promise((resolve, reject) => {
|
|
272
|
+
active.client.forwardIn('127.0.0.1', remotePort, (err) => {
|
|
273
|
+
if (err) {
|
|
274
|
+
console.warn(`[ssh] Failed to set up reverse tunnel ${remotePort} -> ${localPort}:`, err.message);
|
|
275
|
+
return reject(err);
|
|
276
|
+
}
|
|
277
|
+
console.log(`[ssh] Reverse tunnel: remote:${remotePort} -> local:${localPort}`);
|
|
278
|
+
resolve();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
// Forward tunnel: on-demand, for port proxy
|
|
283
|
+
async getOrCreateForwardTunnel(id, remotePort) {
|
|
284
|
+
const active = this.connections.get(id);
|
|
285
|
+
if (!active || !active.connected)
|
|
286
|
+
throw new Error('SSH not connected');
|
|
287
|
+
const existing = active.forwardTunnels.get(remotePort);
|
|
288
|
+
if (existing)
|
|
289
|
+
return existing.localPort;
|
|
290
|
+
// Create a local TCP server that pipes through SSH forwardOut
|
|
291
|
+
return new Promise((resolve, reject) => {
|
|
292
|
+
const server = net.createServer((localSocket) => {
|
|
293
|
+
active.client.forwardOut('127.0.0.1', 0, '127.0.0.1', remotePort, (err, stream) => {
|
|
294
|
+
if (err) {
|
|
295
|
+
localSocket.destroy();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
localSocket.pipe(stream);
|
|
299
|
+
stream.pipe(localSocket);
|
|
300
|
+
localSocket.on('error', () => stream.close());
|
|
301
|
+
stream.on('error', () => localSocket.destroy());
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
server.listen(0, '127.0.0.1', () => {
|
|
305
|
+
const localPort = server.address().port;
|
|
306
|
+
active.forwardTunnels.set(remotePort, { localPort, server });
|
|
307
|
+
console.log(`[ssh] Forward tunnel: local:${localPort} -> remote:${remotePort}`);
|
|
308
|
+
resolve(localPort);
|
|
309
|
+
});
|
|
310
|
+
server.on('error', reject);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
// Upload CA cert to remote
|
|
314
|
+
async uploadCaCert(id) {
|
|
315
|
+
try {
|
|
316
|
+
const certContent = fs.readFileSync(CA_CERT_PATH, 'utf-8');
|
|
317
|
+
await this.exec(id, 'mkdir -p ~/.atoo-studio');
|
|
318
|
+
await this.sftpWriteFile(id, '.atoo-studio/ca.pem', certContent);
|
|
319
|
+
console.log(`[ssh] Uploaded CA cert to remote ~/.atoo-studio/ca.pem`);
|
|
320
|
+
}
|
|
321
|
+
catch (err) {
|
|
322
|
+
console.warn(`[ssh] Failed to upload CA cert:`, err.message);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// Verify claude CLI exists on remote
|
|
326
|
+
async verifyClaudeCli(id) {
|
|
327
|
+
try {
|
|
328
|
+
await this.exec(id, 'which claude');
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
cleanupConnection(id) {
|
|
336
|
+
const active = this.connections.get(id);
|
|
337
|
+
if (!active)
|
|
338
|
+
return;
|
|
339
|
+
for (const server of active.reverseTunnelServers) {
|
|
340
|
+
try {
|
|
341
|
+
server.close();
|
|
342
|
+
}
|
|
343
|
+
catch { }
|
|
344
|
+
}
|
|
345
|
+
active.reverseTunnelServers = [];
|
|
346
|
+
for (const [, tunnel] of active.forwardTunnels) {
|
|
347
|
+
try {
|
|
348
|
+
tunnel.server.close();
|
|
349
|
+
}
|
|
350
|
+
catch { }
|
|
351
|
+
}
|
|
352
|
+
active.forwardTunnels.clear();
|
|
353
|
+
active.sftp = undefined;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function shellEscape(s) {
|
|
357
|
+
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
358
|
+
}
|
|
359
|
+
export const sshManager = new SshManager();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Session } from './state/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Write forked session events as a JSONL file to Claude's session storage.
|
|
4
|
+
* Path: ~/.claude/projects/<project-dir-hash>/<session-uuid>.jsonl
|
|
5
|
+
*
|
|
6
|
+
* Rewrites sessionId fields to match the new session, and generates fresh
|
|
7
|
+
* UUIDs for each event while maintaining the parentUuid chain.
|
|
8
|
+
*/
|
|
9
|
+
export declare function writeSessionJsonl(session: Session, directory: string): string;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import crypto from 'crypto';
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
/**
|
|
7
|
+
* Convert an absolute directory path to the Claude project dir hash format.
|
|
8
|
+
* Replaces all non-alphanumeric chars with '-'.
|
|
9
|
+
* e.g. /home/furti/myproject → -home-furti-myproject
|
|
10
|
+
* Truncates at 200 chars with a hash suffix for very long paths.
|
|
11
|
+
*/
|
|
12
|
+
function projectDirHash(directory) {
|
|
13
|
+
const hashed = directory.replace(/[^a-zA-Z0-9]/g, '-');
|
|
14
|
+
if (hashed.length <= 200)
|
|
15
|
+
return hashed;
|
|
16
|
+
const suffix = crypto.createHash('sha256').update(directory).digest('hex').slice(0, 12);
|
|
17
|
+
return `${hashed.slice(0, 200)}-${suffix}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Extract the UUID portion from a session ID (strip "sess_" prefix).
|
|
21
|
+
*/
|
|
22
|
+
function sessionUuid(sessionId) {
|
|
23
|
+
return sessionId.replace(/^sess_/, '');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Write forked session events as a JSONL file to Claude's session storage.
|
|
27
|
+
* Path: ~/.claude/projects/<project-dir-hash>/<session-uuid>.jsonl
|
|
28
|
+
*
|
|
29
|
+
* Rewrites sessionId fields to match the new session, and generates fresh
|
|
30
|
+
* UUIDs for each event while maintaining the parentUuid chain.
|
|
31
|
+
*/
|
|
32
|
+
export function writeSessionJsonl(session, directory) {
|
|
33
|
+
const dirHash = projectDirHash(path.resolve(directory));
|
|
34
|
+
const projectDir = path.join(os.homedir(), '.claude', 'projects', dirHash);
|
|
35
|
+
const uuid = sessionUuid(session.id);
|
|
36
|
+
const jsonlPath = path.join(projectDir, `${uuid}.jsonl`);
|
|
37
|
+
// Ensure directory exists
|
|
38
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
39
|
+
// Build UUID remap: old UUID → new UUID (preserves parentUuid chain)
|
|
40
|
+
const uuidMap = new Map();
|
|
41
|
+
for (const event of session.events) {
|
|
42
|
+
if (event.uuid) {
|
|
43
|
+
uuidMap.set(event.uuid, uuidv4());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const lines = [];
|
|
47
|
+
for (const event of session.events) {
|
|
48
|
+
const rewritten = { ...event };
|
|
49
|
+
// Rewrite sessionId to the new forked session UUID (Claude Code uses camelCase, no "sess_" prefix)
|
|
50
|
+
rewritten.sessionId = uuid;
|
|
51
|
+
// Remove legacy underscore field if present
|
|
52
|
+
delete rewritten.session_id;
|
|
53
|
+
// Rewrite uuid to fresh UUID
|
|
54
|
+
if (rewritten.uuid && uuidMap.has(rewritten.uuid)) {
|
|
55
|
+
rewritten.uuid = uuidMap.get(rewritten.uuid);
|
|
56
|
+
}
|
|
57
|
+
// Rewrite parentUuid chain
|
|
58
|
+
if (rewritten.parentUuid && uuidMap.has(rewritten.parentUuid)) {
|
|
59
|
+
rewritten.parentUuid = uuidMap.get(rewritten.parentUuid);
|
|
60
|
+
}
|
|
61
|
+
lines.push(JSON.stringify(rewritten));
|
|
62
|
+
}
|
|
63
|
+
fs.writeFileSync(jsonlPath, lines.join('\n') + '\n');
|
|
64
|
+
console.log(`[session-writer] Wrote ${lines.length} events to ${jsonlPath}`);
|
|
65
|
+
return jsonlPath;
|
|
66
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export interface ITerminal {
|
|
2
|
+
write(data: string): void;
|
|
3
|
+
resize(cols: number, rows: number): void;
|
|
4
|
+
kill(): void;
|
|
5
|
+
onData(handler: (data: string) => void): {
|
|
6
|
+
dispose(): void;
|
|
7
|
+
};
|
|
8
|
+
onExit(handler: (exit: {
|
|
9
|
+
exitCode: number;
|
|
10
|
+
}) => void): {
|
|
11
|
+
dispose(): void;
|
|
12
|
+
};
|
|
13
|
+
pid?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface SpawnOptions {
|
|
16
|
+
command: string;
|
|
17
|
+
args?: string[];
|
|
18
|
+
cwd?: string;
|
|
19
|
+
env?: Record<string, string | undefined>;
|
|
20
|
+
cols?: number;
|
|
21
|
+
rows?: number;
|
|
22
|
+
envId?: string;
|
|
23
|
+
preloadSessionId?: string;
|
|
24
|
+
logPrefix?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generic PTY spawn. Returns an envId immediately.
|
|
28
|
+
* Scrollback and process tracking are managed automatically.
|
|
29
|
+
*/
|
|
30
|
+
export declare function spawnProcess(options: SpawnOptions): {
|
|
31
|
+
envId: string;
|
|
32
|
+
term: ITerminal;
|
|
33
|
+
pid: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Spawn a remote process via SSH. Returns the same tracked structure.
|
|
37
|
+
*/
|
|
38
|
+
export declare function spawnRemoteProcess(options: {
|
|
39
|
+
sshConnectionId: string;
|
|
40
|
+
command: string;
|
|
41
|
+
cwd: string;
|
|
42
|
+
cols?: number;
|
|
43
|
+
rows?: number;
|
|
44
|
+
envId?: string;
|
|
45
|
+
logPrefix?: string;
|
|
46
|
+
}): Promise<{
|
|
47
|
+
envId: string;
|
|
48
|
+
term: ITerminal;
|
|
49
|
+
}>;
|
|
50
|
+
export declare function getScrollback(envId: string): string;
|
|
51
|
+
export declare function getProcessPid(envId: string): number | undefined;
|
|
52
|
+
export declare function getPreloadSessionId(envId: string): string | undefined;
|
|
53
|
+
export declare function killCliProcess(envId: string): boolean;
|
|
54
|
+
export declare function killAllCliProcesses(): void;
|
|
55
|
+
export declare function getPty(envId: string): ITerminal | undefined;
|
|
56
|
+
export declare function getEnvIdForSession(sessionId: string): string | undefined;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import * as pty from 'node-pty';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
4
|
+
import { sshManager } from './services/ssh-manager.js';
|
|
5
|
+
import { store } from './state/store.js';
|
|
6
|
+
class SshTerminalAdapter {
|
|
7
|
+
channel;
|
|
8
|
+
pid = undefined;
|
|
9
|
+
constructor(channel) {
|
|
10
|
+
this.channel = channel;
|
|
11
|
+
}
|
|
12
|
+
write(data) { this.channel.write(data); }
|
|
13
|
+
resize(cols, rows) { this.channel.setWindow(rows, cols, rows * 16, cols * 8); }
|
|
14
|
+
kill() { this.channel.close(); }
|
|
15
|
+
onData(handler) {
|
|
16
|
+
const cb = (data) => handler(data.toString());
|
|
17
|
+
this.channel.on('data', cb);
|
|
18
|
+
return { dispose: () => { this.channel.off('data', cb); } };
|
|
19
|
+
}
|
|
20
|
+
onExit(handler) {
|
|
21
|
+
const cb = (code) => handler({ exitCode: code ?? 1 });
|
|
22
|
+
this.channel.on('exit', cb);
|
|
23
|
+
return { dispose: () => { } };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const spawnedProcesses = new Map();
|
|
27
|
+
const MAX_SPAWNER_SCROLLBACK = 200_000;
|
|
28
|
+
const spawnerScrollback = new Map();
|
|
29
|
+
/**
|
|
30
|
+
* Generic PTY spawn. Returns an envId immediately.
|
|
31
|
+
* Scrollback and process tracking are managed automatically.
|
|
32
|
+
*/
|
|
33
|
+
export function spawnProcess(options) {
|
|
34
|
+
const cwd = options.cwd || process.env.HOME || os.homedir();
|
|
35
|
+
const envId = options.envId || `proc_${uuidv4()}`;
|
|
36
|
+
const logPrefix = options.logPrefix || options.command;
|
|
37
|
+
const term = pty.spawn(options.command, options.args || [], {
|
|
38
|
+
name: 'xterm-256color',
|
|
39
|
+
cols: options.cols || 120,
|
|
40
|
+
rows: options.rows || 30,
|
|
41
|
+
cwd,
|
|
42
|
+
env: (options.env || { ...process.env }),
|
|
43
|
+
});
|
|
44
|
+
const pid = term.pid;
|
|
45
|
+
console.log(`[spawner] Started ${logPrefix} (pid=${pid}, envId=${envId}): ${options.command} ${(options.args || []).join(' ')}`);
|
|
46
|
+
spawnedProcesses.set(envId, { pty: term, envId, pid, preloadSessionId: options.preloadSessionId });
|
|
47
|
+
spawnerScrollback.set(envId, '');
|
|
48
|
+
// Scrollback buffer (activity tracking is handled by agent adapters via PtyActivityTracker)
|
|
49
|
+
term.onData((data) => {
|
|
50
|
+
let buf = spawnerScrollback.get(envId) || '';
|
|
51
|
+
buf += data;
|
|
52
|
+
if (buf.length > MAX_SPAWNER_SCROLLBACK)
|
|
53
|
+
buf = buf.slice(-MAX_SPAWNER_SCROLLBACK);
|
|
54
|
+
spawnerScrollback.set(envId, buf);
|
|
55
|
+
});
|
|
56
|
+
term.onExit(({ exitCode }) => {
|
|
57
|
+
console.log(`[spawner] ${logPrefix} (pid=${pid}) exited with code ${exitCode}`);
|
|
58
|
+
spawnedProcesses.delete(envId);
|
|
59
|
+
spawnerScrollback.delete(envId);
|
|
60
|
+
});
|
|
61
|
+
return { envId, term, pid };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Spawn a remote process via SSH. Returns the same tracked structure.
|
|
65
|
+
*/
|
|
66
|
+
export async function spawnRemoteProcess(options) {
|
|
67
|
+
const envId = options.envId || `remote_${uuidv4()}`;
|
|
68
|
+
const logPrefix = options.logPrefix || 'remote';
|
|
69
|
+
if (!sshManager.isConnected(options.sshConnectionId)) {
|
|
70
|
+
throw new Error('SSH connection not active');
|
|
71
|
+
}
|
|
72
|
+
const channel = await sshManager.execPty(options.sshConnectionId, options.command, {
|
|
73
|
+
cwd: options.cwd,
|
|
74
|
+
rows: options.rows || 30,
|
|
75
|
+
cols: options.cols || 120,
|
|
76
|
+
});
|
|
77
|
+
const term = new SshTerminalAdapter(channel);
|
|
78
|
+
console.log(`[spawner] Started ${logPrefix} via SSH (envId=${envId}): ${options.command}`);
|
|
79
|
+
spawnedProcesses.set(envId, { pty: term, envId, pid: 0 });
|
|
80
|
+
spawnerScrollback.set(envId, '');
|
|
81
|
+
term.onData((data) => {
|
|
82
|
+
let buf = spawnerScrollback.get(envId) || '';
|
|
83
|
+
buf += data;
|
|
84
|
+
if (buf.length > MAX_SPAWNER_SCROLLBACK)
|
|
85
|
+
buf = buf.slice(-MAX_SPAWNER_SCROLLBACK);
|
|
86
|
+
spawnerScrollback.set(envId, buf);
|
|
87
|
+
});
|
|
88
|
+
term.onExit(({ exitCode }) => {
|
|
89
|
+
console.log(`[spawner] ${logPrefix} via SSH exited with code ${exitCode}`);
|
|
90
|
+
spawnedProcesses.delete(envId);
|
|
91
|
+
spawnerScrollback.delete(envId);
|
|
92
|
+
});
|
|
93
|
+
return { envId, term };
|
|
94
|
+
}
|
|
95
|
+
// ═══════════════════════════════════════════════════════
|
|
96
|
+
// Process registry queries
|
|
97
|
+
// ═══════════════════════════════════════════════════════
|
|
98
|
+
export function getScrollback(envId) {
|
|
99
|
+
return spawnerScrollback.get(envId) || '';
|
|
100
|
+
}
|
|
101
|
+
export function getProcessPid(envId) {
|
|
102
|
+
return spawnedProcesses.get(envId)?.pid;
|
|
103
|
+
}
|
|
104
|
+
export function getPreloadSessionId(envId) {
|
|
105
|
+
return spawnedProcesses.get(envId)?.preloadSessionId;
|
|
106
|
+
}
|
|
107
|
+
export function killCliProcess(envId) {
|
|
108
|
+
const proc = spawnedProcesses.get(envId);
|
|
109
|
+
if (proc) {
|
|
110
|
+
proc.pty.kill();
|
|
111
|
+
spawnedProcesses.delete(envId);
|
|
112
|
+
spawnerScrollback.delete(envId);
|
|
113
|
+
console.log(`[spawner] Killed process for ${envId}`);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
export function killAllCliProcesses() {
|
|
119
|
+
for (const [id, proc] of Array.from(spawnedProcesses.entries())) {
|
|
120
|
+
console.log(`[spawner] Killing process for ${id}`);
|
|
121
|
+
proc.pty.kill();
|
|
122
|
+
}
|
|
123
|
+
spawnedProcesses.clear();
|
|
124
|
+
spawnerScrollback.clear();
|
|
125
|
+
}
|
|
126
|
+
export function getPty(envId) {
|
|
127
|
+
return spawnedProcesses.get(envId)?.pty;
|
|
128
|
+
}
|
|
129
|
+
export function getEnvIdForSession(sessionId) {
|
|
130
|
+
const session = store.sessions.get(sessionId);
|
|
131
|
+
if (session && spawnedProcesses.has(session.environmentId)) {
|
|
132
|
+
return session.environmentId;
|
|
133
|
+
}
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|