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,637 @@
|
|
|
1
|
+
import net from 'net';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import crypto from 'crypto';
|
|
6
|
+
import readline from 'readline';
|
|
7
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
8
|
+
const ATOO_DIR = path.join(os.homedir(), '.atoo-studio');
|
|
9
|
+
const SOCKET_PATH = path.join(ATOO_DIR, 'preload.sock');
|
|
10
|
+
const OBJECTS_DIR = path.join(ATOO_DIR, 'objects');
|
|
11
|
+
/** Map a preload op to a FileChange operation. */
|
|
12
|
+
function mapOperation(op, fileExisted) {
|
|
13
|
+
switch (op) {
|
|
14
|
+
case 'write':
|
|
15
|
+
return fileExisted ? 'modify' : 'create';
|
|
16
|
+
case 'rename':
|
|
17
|
+
return 'rename';
|
|
18
|
+
case 'delete':
|
|
19
|
+
return 'delete';
|
|
20
|
+
case 'truncate':
|
|
21
|
+
return 'modify';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Detect if a buffer is likely binary (contains null bytes). */
|
|
25
|
+
function isBinaryBuffer(buf) {
|
|
26
|
+
for (let i = 0; i < Math.min(buf.length, 8192); i++) {
|
|
27
|
+
if (buf[i] === 0)
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
/** Paths to exclude from inotify watching. */
|
|
33
|
+
function shouldExcludeInotify(filePath) {
|
|
34
|
+
const base = path.basename(filePath);
|
|
35
|
+
// Skip hidden dirs/files (except specific ones), node_modules, etc.
|
|
36
|
+
if (base === 'node_modules')
|
|
37
|
+
return true;
|
|
38
|
+
if (base === '.git')
|
|
39
|
+
return true;
|
|
40
|
+
if (base === '.atoo-studio')
|
|
41
|
+
return true;
|
|
42
|
+
if (filePath.includes('/node_modules/'))
|
|
43
|
+
return true;
|
|
44
|
+
if (filePath.includes('/.git/'))
|
|
45
|
+
return true;
|
|
46
|
+
if (filePath.includes('/.atoo-studio/'))
|
|
47
|
+
return true;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
/** Store a buffer in the content-addressed object store. Returns the hash. */
|
|
51
|
+
function storeObject(buf) {
|
|
52
|
+
const hash = crypto.createHash('sha256').update(buf).digest('hex');
|
|
53
|
+
const objectPath = path.join(OBJECTS_DIR, hash);
|
|
54
|
+
if (!fs.existsSync(objectPath)) {
|
|
55
|
+
fs.writeFileSync(objectPath, buf);
|
|
56
|
+
}
|
|
57
|
+
return hash;
|
|
58
|
+
}
|
|
59
|
+
export class FsMonitor {
|
|
60
|
+
server = null;
|
|
61
|
+
clients = new Set();
|
|
62
|
+
listening = false;
|
|
63
|
+
changes = new Map(); // sessionId → changes
|
|
64
|
+
changeListeners = [];
|
|
65
|
+
/** Mapping from preload tracking UUID → real session ID */
|
|
66
|
+
sessionMap = new Map();
|
|
67
|
+
/** Active inotify watchers per session */
|
|
68
|
+
inotifySessions = new Map();
|
|
69
|
+
/** Track paths already reported by preload (to dedup with inotify) */
|
|
70
|
+
preloadReportedPaths = new Map(); // sessionId → Set<path>
|
|
71
|
+
/** Start the Unix socket server for preload library connections. */
|
|
72
|
+
async connect() {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
// Ensure directories exist
|
|
75
|
+
fs.mkdirSync(ATOO_DIR, { recursive: true });
|
|
76
|
+
fs.mkdirSync(OBJECTS_DIR, { recursive: true });
|
|
77
|
+
// Remove stale socket
|
|
78
|
+
try {
|
|
79
|
+
fs.unlinkSync(SOCKET_PATH);
|
|
80
|
+
}
|
|
81
|
+
catch { }
|
|
82
|
+
const server = net.createServer((client) => {
|
|
83
|
+
this.clients.add(client);
|
|
84
|
+
const rl = readline.createInterface({ input: client });
|
|
85
|
+
rl.on('line', (line) => {
|
|
86
|
+
this.handlePreloadEvent(line);
|
|
87
|
+
});
|
|
88
|
+
client.on('error', () => {
|
|
89
|
+
this.clients.delete(client);
|
|
90
|
+
});
|
|
91
|
+
client.on('close', () => {
|
|
92
|
+
this.clients.delete(client);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
server.on('error', (err) => {
|
|
96
|
+
console.error(`[fs-monitor] Socket server error: ${err.message}`);
|
|
97
|
+
reject(err);
|
|
98
|
+
});
|
|
99
|
+
server.listen(SOCKET_PATH, () => {
|
|
100
|
+
this.listening = true;
|
|
101
|
+
console.log(`[fs-monitor] Preload socket listening on ${SOCKET_PATH}`);
|
|
102
|
+
resolve();
|
|
103
|
+
});
|
|
104
|
+
this.server = server;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** Register a listener for real-time change events. */
|
|
108
|
+
onChangeEvent(listener) {
|
|
109
|
+
this.changeListeners.push(listener);
|
|
110
|
+
}
|
|
111
|
+
/** Remove a change event listener. */
|
|
112
|
+
offChangeEvent(listener) {
|
|
113
|
+
const idx = this.changeListeners.indexOf(listener);
|
|
114
|
+
if (idx >= 0)
|
|
115
|
+
this.changeListeners.splice(idx, 1);
|
|
116
|
+
}
|
|
117
|
+
/** Handle a JSON line from a preload library instance. */
|
|
118
|
+
handlePreloadEvent(line) {
|
|
119
|
+
const trimmed = line.trim();
|
|
120
|
+
if (!trimmed)
|
|
121
|
+
return;
|
|
122
|
+
let event;
|
|
123
|
+
try {
|
|
124
|
+
event = JSON.parse(trimmed);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
console.warn('[fs-monitor] Invalid JSON from preload:', trimmed.substring(0, 100));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Remap tracking UUID to real session ID
|
|
131
|
+
const realSessionId = this.sessionMap.get(event.session_id) || event.session_id;
|
|
132
|
+
// Mark this path as reported by preload (for inotify dedup)
|
|
133
|
+
if (!this.preloadReportedPaths.has(realSessionId)) {
|
|
134
|
+
this.preloadReportedPaths.set(realSessionId, new Set());
|
|
135
|
+
}
|
|
136
|
+
this.preloadReportedPaths.get(realSessionId).add(event.path);
|
|
137
|
+
// Process snapshot: hash and move to object store
|
|
138
|
+
let beforeHash = null;
|
|
139
|
+
let fileSize = 0;
|
|
140
|
+
let binary = false;
|
|
141
|
+
if (event.snapshot) {
|
|
142
|
+
try {
|
|
143
|
+
const snapBuf = fs.readFileSync(event.snapshot);
|
|
144
|
+
fileSize = snapBuf.length;
|
|
145
|
+
binary = isBinaryBuffer(snapBuf);
|
|
146
|
+
const hash = crypto.createHash('sha256').update(snapBuf).digest('hex');
|
|
147
|
+
const objectPath = path.join(OBJECTS_DIR, hash);
|
|
148
|
+
// Move snapshot to object store (content-addressed, skip if exists)
|
|
149
|
+
if (!fs.existsSync(objectPath)) {
|
|
150
|
+
fs.renameSync(event.snapshot, objectPath);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
try {
|
|
154
|
+
fs.unlinkSync(event.snapshot);
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
157
|
+
}
|
|
158
|
+
beforeHash = hash;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
console.warn(`[fs-monitor] Failed to process snapshot ${event.snapshot}: ${err.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const operation = mapOperation(event.op, event.file_existed);
|
|
165
|
+
this.recordChange({
|
|
166
|
+
sessionId: realSessionId,
|
|
167
|
+
timestamp: event.ts,
|
|
168
|
+
operation,
|
|
169
|
+
filePath: event.path,
|
|
170
|
+
oldPath: event.old_path,
|
|
171
|
+
beforeHash,
|
|
172
|
+
fileSize,
|
|
173
|
+
isBinary: binary,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/** Handle an inotify event from fs.watch. */
|
|
177
|
+
handleInotifyEvent(sessionId, baseDir, _eventType, filename) {
|
|
178
|
+
if (!filename)
|
|
179
|
+
return;
|
|
180
|
+
const filePath = path.resolve(baseDir, filename);
|
|
181
|
+
// Skip excluded paths
|
|
182
|
+
if (shouldExcludeInotify(filePath))
|
|
183
|
+
return;
|
|
184
|
+
// Skip if already reported by preload recently
|
|
185
|
+
const preloadPaths = this.preloadReportedPaths.get(sessionId);
|
|
186
|
+
if (preloadPaths?.has(filePath))
|
|
187
|
+
return;
|
|
188
|
+
const inoSession = this.inotifySessions.get(sessionId);
|
|
189
|
+
if (!inoSession)
|
|
190
|
+
return;
|
|
191
|
+
this.processInotifyChange(sessionId, filePath, inoSession);
|
|
192
|
+
}
|
|
193
|
+
/** Process a debounced inotify change. */
|
|
194
|
+
processInotifyChange(sessionId, filePath, inoSession) {
|
|
195
|
+
// Re-check preload dedup after debounce
|
|
196
|
+
const preloadPaths = this.preloadReportedPaths.get(sessionId);
|
|
197
|
+
if (preloadPaths?.has(filePath))
|
|
198
|
+
return;
|
|
199
|
+
const fileExists = fs.existsSync(filePath);
|
|
200
|
+
const initialHash = inoSession.initialHashes.get(filePath);
|
|
201
|
+
let operation;
|
|
202
|
+
let beforeHash = initialHash || null;
|
|
203
|
+
let fileSize = 0;
|
|
204
|
+
let binary = false;
|
|
205
|
+
if (!fileExists) {
|
|
206
|
+
if (!initialHash) {
|
|
207
|
+
// File never existed at session start and is now gone — transient, skip
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
operation = 'delete';
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
try {
|
|
214
|
+
const stat = fs.statSync(filePath);
|
|
215
|
+
if (!stat.isFile())
|
|
216
|
+
return; // Skip directories
|
|
217
|
+
fileSize = stat.size;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (initialHash) {
|
|
223
|
+
// File existed before — check if actually changed
|
|
224
|
+
try {
|
|
225
|
+
const buf = fs.readFileSync(filePath);
|
|
226
|
+
const currentHash = crypto.createHash('sha256').update(buf).digest('hex');
|
|
227
|
+
if (currentHash === initialHash)
|
|
228
|
+
return; // No actual change
|
|
229
|
+
binary = isBinaryBuffer(buf);
|
|
230
|
+
fileSize = buf.length;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
operation = 'modify';
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
// File didn't exist at session start — it's a create
|
|
239
|
+
operation = 'create';
|
|
240
|
+
try {
|
|
241
|
+
const buf = fs.readFileSync(filePath);
|
|
242
|
+
binary = isBinaryBuffer(buf);
|
|
243
|
+
fileSize = buf.length;
|
|
244
|
+
}
|
|
245
|
+
catch { }
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Update initial hash for subsequent changes
|
|
249
|
+
if (fileExists) {
|
|
250
|
+
try {
|
|
251
|
+
const buf = fs.readFileSync(filePath);
|
|
252
|
+
const newHash = storeObject(buf);
|
|
253
|
+
inoSession.initialHashes.set(filePath, newHash);
|
|
254
|
+
}
|
|
255
|
+
catch { }
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
inoSession.initialHashes.delete(filePath);
|
|
259
|
+
}
|
|
260
|
+
this.recordChange({
|
|
261
|
+
sessionId,
|
|
262
|
+
timestamp: Date.now() / 1000,
|
|
263
|
+
operation,
|
|
264
|
+
filePath,
|
|
265
|
+
beforeHash,
|
|
266
|
+
fileSize,
|
|
267
|
+
isBinary: binary,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/** Record a FileChange and notify listeners. */
|
|
271
|
+
recordChange(opts) {
|
|
272
|
+
const change = {
|
|
273
|
+
changeId: uuidv4(),
|
|
274
|
+
sessionId: opts.sessionId,
|
|
275
|
+
timestamp: opts.timestamp,
|
|
276
|
+
pid: 0,
|
|
277
|
+
operation: opts.operation,
|
|
278
|
+
path: opts.filePath,
|
|
279
|
+
oldPath: opts.oldPath,
|
|
280
|
+
beforeHash: opts.beforeHash,
|
|
281
|
+
afterHash: null, // lazy — materialized on demand
|
|
282
|
+
fileSize: opts.fileSize,
|
|
283
|
+
isBinary: opts.isBinary,
|
|
284
|
+
};
|
|
285
|
+
if (!this.changes.has(opts.sessionId)) {
|
|
286
|
+
this.changes.set(opts.sessionId, []);
|
|
287
|
+
}
|
|
288
|
+
this.changes.get(opts.sessionId).push(change);
|
|
289
|
+
// Notify listeners
|
|
290
|
+
for (const listener of this.changeListeners) {
|
|
291
|
+
try {
|
|
292
|
+
listener(change);
|
|
293
|
+
}
|
|
294
|
+
catch (err) {
|
|
295
|
+
console.error('[fs-monitor] Listener error:', err);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Start watching a session's working directory.
|
|
301
|
+
* Combines preload session mapping + inotify watching.
|
|
302
|
+
*/
|
|
303
|
+
watchPid(sessionId, _pid, baseDir) {
|
|
304
|
+
console.log(`[fs-monitor] watchPid: session=${sessionId}, dir=${baseDir}`);
|
|
305
|
+
if (!this.changes.has(sessionId)) {
|
|
306
|
+
this.changes.set(sessionId, []);
|
|
307
|
+
}
|
|
308
|
+
// Start inotify watcher for the working directory
|
|
309
|
+
this.startInotifyWatch(sessionId, baseDir);
|
|
310
|
+
}
|
|
311
|
+
/** Start an inotify (fs.watch) watcher for a session's working directory. */
|
|
312
|
+
startInotifyWatch(sessionId, baseDir) {
|
|
313
|
+
// Don't double-watch
|
|
314
|
+
if (this.inotifySessions.has(sessionId))
|
|
315
|
+
return;
|
|
316
|
+
try {
|
|
317
|
+
// Snapshot initial file hashes for before-state tracking
|
|
318
|
+
const initialHashes = new Map();
|
|
319
|
+
this.snapshotDirectory(baseDir, initialHashes);
|
|
320
|
+
console.log(`[fs-monitor] Snapshotted ${initialHashes.size} files in ${baseDir}`);
|
|
321
|
+
const watcher = fs.watch(baseDir, { recursive: true }, (eventType, filename) => {
|
|
322
|
+
this.handleInotifyEvent(sessionId, baseDir, eventType, filename);
|
|
323
|
+
});
|
|
324
|
+
watcher.on('error', (err) => {
|
|
325
|
+
console.warn(`[fs-monitor] Inotify watcher error for ${sessionId}: ${err.message}`);
|
|
326
|
+
});
|
|
327
|
+
const inoSession = {
|
|
328
|
+
sessionId,
|
|
329
|
+
baseDir,
|
|
330
|
+
watcher,
|
|
331
|
+
initialHashes,
|
|
332
|
+
};
|
|
333
|
+
this.inotifySessions.set(sessionId, inoSession);
|
|
334
|
+
console.log(`[fs-monitor] Inotify watching ${baseDir} for session ${sessionId}`);
|
|
335
|
+
}
|
|
336
|
+
catch (err) {
|
|
337
|
+
console.warn(`[fs-monitor] Failed to start inotify for ${baseDir}: ${err.message}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/** Recursively snapshot file hashes in a directory. */
|
|
341
|
+
snapshotDirectory(dir, hashes, depth = 0) {
|
|
342
|
+
if (depth > 10)
|
|
343
|
+
return; // Limit recursion depth
|
|
344
|
+
if (shouldExcludeInotify(dir))
|
|
345
|
+
return;
|
|
346
|
+
try {
|
|
347
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
348
|
+
for (const entry of entries) {
|
|
349
|
+
const fullPath = path.join(dir, entry.name);
|
|
350
|
+
if (shouldExcludeInotify(fullPath))
|
|
351
|
+
continue;
|
|
352
|
+
if (entry.isDirectory()) {
|
|
353
|
+
this.snapshotDirectory(fullPath, hashes, depth + 1);
|
|
354
|
+
}
|
|
355
|
+
else if (entry.isFile()) {
|
|
356
|
+
try {
|
|
357
|
+
const buf = fs.readFileSync(fullPath);
|
|
358
|
+
const hash = storeObject(buf);
|
|
359
|
+
hashes.set(fullPath, hash);
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
// Skip unreadable files
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
// Skip unreadable directories
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/** Register the mapping from a preload tracking UUID to the real session ID. */
|
|
372
|
+
registerSessionMapping(preloadSessionId, realSessionId) {
|
|
373
|
+
this.sessionMap.set(preloadSessionId, realSessionId);
|
|
374
|
+
// Remap any changes already received with the tracking UUID
|
|
375
|
+
const pending = this.changes.get(preloadSessionId);
|
|
376
|
+
if (pending && pending.length > 0) {
|
|
377
|
+
const existing = this.changes.get(realSessionId) || [];
|
|
378
|
+
for (const c of pending) {
|
|
379
|
+
c.sessionId = realSessionId;
|
|
380
|
+
existing.push(c);
|
|
381
|
+
}
|
|
382
|
+
this.changes.set(realSessionId, existing);
|
|
383
|
+
this.changes.delete(preloadSessionId);
|
|
384
|
+
}
|
|
385
|
+
// Also remap preload reported paths
|
|
386
|
+
const preloadPaths = this.preloadReportedPaths.get(preloadSessionId);
|
|
387
|
+
if (preloadPaths) {
|
|
388
|
+
const existing = this.preloadReportedPaths.get(realSessionId) || new Set();
|
|
389
|
+
for (const p of preloadPaths)
|
|
390
|
+
existing.add(p);
|
|
391
|
+
this.preloadReportedPaths.set(realSessionId, existing);
|
|
392
|
+
this.preloadReportedPaths.delete(preloadSessionId);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/** Stop watching a session. */
|
|
396
|
+
unwatchPid(sessionId) {
|
|
397
|
+
const inoSession = this.inotifySessions.get(sessionId);
|
|
398
|
+
if (inoSession) {
|
|
399
|
+
inoSession.watcher.close();
|
|
400
|
+
this.inotifySessions.delete(sessionId);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
/** Get all changes for a session. */
|
|
404
|
+
getChanges(sessionId) {
|
|
405
|
+
return this.changes.get(sessionId) || [];
|
|
406
|
+
}
|
|
407
|
+
/** Get changes for a session within a time range. */
|
|
408
|
+
getChangesInRange(sessionId, from, to) {
|
|
409
|
+
const all = this.getChanges(sessionId);
|
|
410
|
+
if (from === undefined && to === undefined)
|
|
411
|
+
return all;
|
|
412
|
+
return all.filter(c => {
|
|
413
|
+
if (from !== undefined && c.timestamp < from)
|
|
414
|
+
return false;
|
|
415
|
+
if (to !== undefined && c.timestamp > to)
|
|
416
|
+
return false;
|
|
417
|
+
return true;
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
/** Get the change count for a session. */
|
|
421
|
+
getChangeCount(sessionId) {
|
|
422
|
+
return this.changes.get(sessionId)?.length || 0;
|
|
423
|
+
}
|
|
424
|
+
/** Read file content by hash from the object store. Returns base64-encoded content. */
|
|
425
|
+
async getFileContent(hash) {
|
|
426
|
+
const objectPath = path.join(OBJECTS_DIR, hash);
|
|
427
|
+
try {
|
|
428
|
+
const buf = fs.readFileSync(objectPath);
|
|
429
|
+
return buf.toString('base64');
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Materialize the after_hash for a change by reading the file's current content.
|
|
437
|
+
* Returns the hash, or null if the file doesn't exist.
|
|
438
|
+
*/
|
|
439
|
+
async materializeAfterHash(change) {
|
|
440
|
+
if (change.afterHash !== null)
|
|
441
|
+
return change.afterHash;
|
|
442
|
+
if (change.operation === 'delete')
|
|
443
|
+
return null;
|
|
444
|
+
try {
|
|
445
|
+
const buf = fs.readFileSync(change.path);
|
|
446
|
+
const hash = storeObject(buf);
|
|
447
|
+
// Cache on the change record
|
|
448
|
+
change.afterHash = hash;
|
|
449
|
+
change.fileSize = Math.max(change.fileSize, buf.length);
|
|
450
|
+
change.isBinary = change.isBinary || isBinaryBuffer(buf);
|
|
451
|
+
return hash;
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
// File may have been deleted since the change
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/** Revert a single change by restoring the file to its before state. */
|
|
459
|
+
async revertChange(change) {
|
|
460
|
+
try {
|
|
461
|
+
switch (change.operation) {
|
|
462
|
+
case 'modify': {
|
|
463
|
+
if (!change.beforeHash) {
|
|
464
|
+
return { success: false, message: 'No before snapshot available' };
|
|
465
|
+
}
|
|
466
|
+
const content = await this.getFileContent(change.beforeHash);
|
|
467
|
+
if (!content) {
|
|
468
|
+
return { success: false, message: 'Failed to read before content from object store' };
|
|
469
|
+
}
|
|
470
|
+
const buf = Buffer.from(content, 'base64');
|
|
471
|
+
fs.writeFileSync(change.path, buf);
|
|
472
|
+
return { success: true };
|
|
473
|
+
}
|
|
474
|
+
case 'create': {
|
|
475
|
+
// Undo creation by deleting the file
|
|
476
|
+
if (fs.existsSync(change.path)) {
|
|
477
|
+
fs.unlinkSync(change.path);
|
|
478
|
+
}
|
|
479
|
+
return { success: true };
|
|
480
|
+
}
|
|
481
|
+
case 'delete': {
|
|
482
|
+
if (!change.beforeHash) {
|
|
483
|
+
return { success: false, message: 'No before snapshot available for deleted file' };
|
|
484
|
+
}
|
|
485
|
+
const content = await this.getFileContent(change.beforeHash);
|
|
486
|
+
if (!content) {
|
|
487
|
+
return { success: false, message: 'Failed to read before content from object store' };
|
|
488
|
+
}
|
|
489
|
+
const buf = Buffer.from(content, 'base64');
|
|
490
|
+
// Ensure parent directory exists
|
|
491
|
+
const dir = path.dirname(change.path);
|
|
492
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
493
|
+
fs.writeFileSync(change.path, buf);
|
|
494
|
+
return { success: true };
|
|
495
|
+
}
|
|
496
|
+
case 'rename': {
|
|
497
|
+
if (change.oldPath) {
|
|
498
|
+
fs.renameSync(change.path, change.oldPath);
|
|
499
|
+
}
|
|
500
|
+
return { success: true };
|
|
501
|
+
}
|
|
502
|
+
default:
|
|
503
|
+
return { success: false, message: `Unknown operation: ${change.operation}` };
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
catch (err) {
|
|
507
|
+
return { success: false, message: err.message };
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/** Revert all changes for a session in reverse chronological order. */
|
|
511
|
+
async revertAll(sessionId) {
|
|
512
|
+
const changes = this.getChanges(sessionId);
|
|
513
|
+
if (changes.length === 0) {
|
|
514
|
+
return { success: true, reverted: 0, failed: 0 };
|
|
515
|
+
}
|
|
516
|
+
// Process in reverse order
|
|
517
|
+
const reversed = [...changes].reverse();
|
|
518
|
+
let reverted = 0;
|
|
519
|
+
let failed = 0;
|
|
520
|
+
for (const change of reversed) {
|
|
521
|
+
const result = await this.revertChange(change);
|
|
522
|
+
if (result.success) {
|
|
523
|
+
reverted++;
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
failed++;
|
|
527
|
+
console.warn(`[fs-monitor] Failed to revert ${change.changeId}: ${result.message}`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return { success: failed === 0, reverted, failed };
|
|
531
|
+
}
|
|
532
|
+
/** Find a specific change by ID across all sessions. */
|
|
533
|
+
findChange(changeId) {
|
|
534
|
+
for (const changes of this.changes.values()) {
|
|
535
|
+
const found = changes.find(c => c.changeId === changeId);
|
|
536
|
+
if (found)
|
|
537
|
+
return found;
|
|
538
|
+
}
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Third fallback: notify that a tool call modified a file.
|
|
543
|
+
* Called when a Write/Edit tool_result arrives. Checks if the change
|
|
544
|
+
* was already captured by preload or inotify; if not, records it.
|
|
545
|
+
*/
|
|
546
|
+
notifyToolChange(sessionId, filePath, toolName) {
|
|
547
|
+
const absPath = path.resolve(filePath);
|
|
548
|
+
// Dedup: check if this path was already captured recently (within 3s)
|
|
549
|
+
const existing = this.changes.get(sessionId);
|
|
550
|
+
if (existing) {
|
|
551
|
+
const now = Date.now() / 1000;
|
|
552
|
+
const alreadyCaptured = existing.some(c => c.path === absPath && (now - c.timestamp) < 3);
|
|
553
|
+
if (alreadyCaptured) {
|
|
554
|
+
console.log(`[fs-monitor] Tool change for ${absPath} already captured, skipping`);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// Also check preload reported paths
|
|
559
|
+
const preloadPaths = this.preloadReportedPaths.get(sessionId);
|
|
560
|
+
if (preloadPaths?.has(absPath)) {
|
|
561
|
+
console.log(`[fs-monitor] Tool change for ${absPath} already reported by preload, skipping`);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
// Determine before hash from inotify session snapshot
|
|
565
|
+
const inoSession = this.inotifySessions.get(sessionId);
|
|
566
|
+
let beforeHash = null;
|
|
567
|
+
if (inoSession) {
|
|
568
|
+
beforeHash = inoSession.initialHashes.get(absPath) || null;
|
|
569
|
+
}
|
|
570
|
+
// Determine operation
|
|
571
|
+
let operation;
|
|
572
|
+
if (toolName === 'Write') {
|
|
573
|
+
operation = beforeHash ? 'modify' : 'create';
|
|
574
|
+
}
|
|
575
|
+
else if (toolName === 'Edit') {
|
|
576
|
+
operation = 'modify';
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
// Read current file state
|
|
582
|
+
let fileSize = 0;
|
|
583
|
+
let binary = false;
|
|
584
|
+
const fileExists = fs.existsSync(absPath);
|
|
585
|
+
if (fileExists) {
|
|
586
|
+
try {
|
|
587
|
+
const buf = fs.readFileSync(absPath);
|
|
588
|
+
fileSize = buf.length;
|
|
589
|
+
binary = isBinaryBuffer(buf);
|
|
590
|
+
// Update inotify initial hash for future change detection
|
|
591
|
+
if (inoSession) {
|
|
592
|
+
const hash = storeObject(buf);
|
|
593
|
+
inoSession.initialHashes.set(absPath, hash);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
catch { }
|
|
597
|
+
}
|
|
598
|
+
console.log(`[fs-monitor] Tool change: ${toolName} → ${operation} ${absPath}`);
|
|
599
|
+
this.recordChange({
|
|
600
|
+
sessionId,
|
|
601
|
+
timestamp: Date.now() / 1000,
|
|
602
|
+
operation,
|
|
603
|
+
filePath: absPath,
|
|
604
|
+
beforeHash,
|
|
605
|
+
fileSize,
|
|
606
|
+
isBinary: binary,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/** Whether the monitor is available (socket server listening). */
|
|
610
|
+
isAvailable() {
|
|
611
|
+
return this.listening;
|
|
612
|
+
}
|
|
613
|
+
/** Close the socket server, inotify watchers, and all client connections. */
|
|
614
|
+
disconnect() {
|
|
615
|
+
// Close inotify watchers
|
|
616
|
+
for (const [, inoSession] of this.inotifySessions) {
|
|
617
|
+
inoSession.watcher.close();
|
|
618
|
+
}
|
|
619
|
+
this.inotifySessions.clear();
|
|
620
|
+
// Close preload socket clients
|
|
621
|
+
for (const client of this.clients) {
|
|
622
|
+
client.destroy();
|
|
623
|
+
}
|
|
624
|
+
this.clients.clear();
|
|
625
|
+
if (this.server) {
|
|
626
|
+
this.server.close();
|
|
627
|
+
this.server = null;
|
|
628
|
+
}
|
|
629
|
+
this.listening = false;
|
|
630
|
+
// Clean up socket file
|
|
631
|
+
try {
|
|
632
|
+
fs.unlinkSync(SOCKET_PATH);
|
|
633
|
+
}
|
|
634
|
+
catch { }
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
export const fsMonitor = new FsMonitor();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const authRouter: import("express-serve-static-core").Router;
|