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,327 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { connectionManager } from '../database/connection-manager.js';
|
|
4
|
+
import { discoverLocalFiles } from '../database/discovery/local-files.js';
|
|
5
|
+
import { discoverContainerDatabases, parseComposeFile } from '../database/discovery/container.js';
|
|
6
|
+
import { discoverFromEnvFiles } from '../database/discovery/env-parser.js';
|
|
7
|
+
import { scanLocalPorts } from '../database/discovery/port-scan.js';
|
|
8
|
+
import { db } from '../state/db.js';
|
|
9
|
+
import { obfuscate, deobfuscate } from '../services/obfuscation.js';
|
|
10
|
+
/** Obfuscate sensitive fields before persisting */
|
|
11
|
+
function obfuscateParams(params) {
|
|
12
|
+
const p = { ...params };
|
|
13
|
+
if (p.password)
|
|
14
|
+
p.password = obfuscate(p.password);
|
|
15
|
+
if (p.connection_string)
|
|
16
|
+
p.connection_string = obfuscate(p.connection_string);
|
|
17
|
+
return p;
|
|
18
|
+
}
|
|
19
|
+
/** Deobfuscate sensitive fields after loading from DB */
|
|
20
|
+
function deobfuscateParams(params) {
|
|
21
|
+
const p = { ...params };
|
|
22
|
+
if (p.password)
|
|
23
|
+
try {
|
|
24
|
+
p.password = deobfuscate(p.password);
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
if (p.connection_string)
|
|
28
|
+
try {
|
|
29
|
+
p.connection_string = deobfuscate(p.connection_string);
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
return p;
|
|
33
|
+
}
|
|
34
|
+
export const databasesRouter = Router();
|
|
35
|
+
// --- Discovery ---
|
|
36
|
+
let discoveryCache = null;
|
|
37
|
+
const DISCOVERY_CACHE_TTL = 15_000;
|
|
38
|
+
/** Invalidate discovery cache so next request re-scans */
|
|
39
|
+
function invalidateDiscoveryCache() {
|
|
40
|
+
discoveryCache = null;
|
|
41
|
+
}
|
|
42
|
+
// --- Container event hooks ---
|
|
43
|
+
// Watch for docker/podman container start/stop to auto-invalidate discovery cache.
|
|
44
|
+
function startContainerWatcher() {
|
|
45
|
+
for (const runtime of ['docker', 'podman']) {
|
|
46
|
+
try {
|
|
47
|
+
const child = spawn(runtime, ['events', '--filter', 'type=container', '--filter', 'event=start', '--filter', 'event=stop', '--filter', 'event=die', '--format', '{{.Status}}'], {
|
|
48
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
49
|
+
});
|
|
50
|
+
child.stdout?.on('data', () => {
|
|
51
|
+
// Any container start/stop/die event invalidates cache
|
|
52
|
+
invalidateDiscoveryCache();
|
|
53
|
+
});
|
|
54
|
+
child.on('error', () => { }); // runtime not installed, ignore
|
|
55
|
+
child.unref(); // Don't prevent process exit
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Start watching container events in background
|
|
61
|
+
startContainerWatcher();
|
|
62
|
+
async function runDiscovery(projectDir) {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
if (discoveryCache && now - discoveryCache.time < DISCOVERY_CACHE_TTL) {
|
|
65
|
+
return discoveryCache.result;
|
|
66
|
+
}
|
|
67
|
+
const [localFiles, containerDbs, composeDbs, envDbs] = await Promise.all([
|
|
68
|
+
Promise.resolve(projectDir ? discoverLocalFiles(projectDir) : []),
|
|
69
|
+
discoverContainerDatabases(),
|
|
70
|
+
Promise.resolve(projectDir ? parseComposeFile(projectDir) : []),
|
|
71
|
+
Promise.resolve(projectDir ? discoverFromEnvFiles(projectDir) : []),
|
|
72
|
+
]);
|
|
73
|
+
// Deduplicate compose vs running containers
|
|
74
|
+
const seen = new Set(containerDbs.map(d => `${d.db_type}:${d.params.port}`));
|
|
75
|
+
const uniqueCompose = composeDbs.filter(d => !seen.has(`${d.db_type}:${d.params.port}`));
|
|
76
|
+
// Deduplicate env discoveries vs already-found
|
|
77
|
+
for (const d of [...containerDbs, ...uniqueCompose])
|
|
78
|
+
seen.add(`${d.db_type}:${d.params.port}`);
|
|
79
|
+
const uniqueEnv = envDbs.filter(d => !d.params.port || !seen.has(`${d.db_type}:${d.params.port}`));
|
|
80
|
+
// Collect ports already found to exclude from port scanning
|
|
81
|
+
const knownPorts = new Set();
|
|
82
|
+
for (const d of [...localFiles, ...containerDbs, ...uniqueCompose, ...uniqueEnv]) {
|
|
83
|
+
if (d.params.port)
|
|
84
|
+
knownPorts.add(d.params.port);
|
|
85
|
+
}
|
|
86
|
+
// Port scan as fallback
|
|
87
|
+
const portResults = await scanLocalPorts(knownPorts);
|
|
88
|
+
const result = [...localFiles, ...containerDbs, ...uniqueCompose, ...uniqueEnv, ...portResults];
|
|
89
|
+
discoveryCache = { result, time: now };
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
databasesRouter.get('/api/databases/discover', async (req, res) => {
|
|
93
|
+
try {
|
|
94
|
+
const projectDir = req.query.project_dir;
|
|
95
|
+
const discovered = await runDiscovery(projectDir);
|
|
96
|
+
const savedConnections = db.getSavedDbConnections().map(c => ({
|
|
97
|
+
...c,
|
|
98
|
+
params: deobfuscateParams(c.params),
|
|
99
|
+
}));
|
|
100
|
+
// Strip passwords from the response — frontend doesn't need them for display
|
|
101
|
+
const safeSaved = savedConnections.map(c => ({
|
|
102
|
+
id: c.id, name: c.name, db_type: c.db_type,
|
|
103
|
+
params: { ...c.params, password: c.params.password ? '••••' : undefined, connection_string: undefined },
|
|
104
|
+
}));
|
|
105
|
+
res.json({
|
|
106
|
+
discovered,
|
|
107
|
+
connections: connectionManager.getActiveConnections(),
|
|
108
|
+
saved: safeSaved,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
res.status(500).json({ error: e.message });
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
// --- Connection management ---
|
|
116
|
+
databasesRouter.post('/api/databases/connect', async (req, res) => {
|
|
117
|
+
try {
|
|
118
|
+
const { db_type, connection, name, save } = req.body;
|
|
119
|
+
if (!db_type)
|
|
120
|
+
return res.status(400).json({ error: 'db_type is required' });
|
|
121
|
+
if (!connection)
|
|
122
|
+
return res.status(400).json({ error: 'connection params are required' });
|
|
123
|
+
const connectionId = await connectionManager.connect(db_type, connection, name);
|
|
124
|
+
// Save to persistent storage if requested or always for manual connections
|
|
125
|
+
if (save !== false) {
|
|
126
|
+
const connName = name || `${db_type}@${connection.host || connection.filename || 'localhost'}`;
|
|
127
|
+
db.saveDbConnection(connectionId, connName, db_type, obfuscateParams(connection));
|
|
128
|
+
}
|
|
129
|
+
db.touchDbConnection(connectionId);
|
|
130
|
+
res.json({ connection_id: connectionId });
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
res.status(500).json({ error: e.message });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
databasesRouter.post('/api/databases/disconnect', async (req, res) => {
|
|
137
|
+
try {
|
|
138
|
+
const { connection_id } = req.body;
|
|
139
|
+
if (!connection_id)
|
|
140
|
+
return res.status(400).json({ error: 'connection_id is required' });
|
|
141
|
+
await connectionManager.disconnect(connection_id);
|
|
142
|
+
res.json({ ok: true });
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
res.status(500).json({ error: e.message });
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
// Reconnect a saved connection
|
|
149
|
+
databasesRouter.post('/api/databases/reconnect', async (req, res) => {
|
|
150
|
+
try {
|
|
151
|
+
const { saved_id } = req.body;
|
|
152
|
+
if (!saved_id)
|
|
153
|
+
return res.status(400).json({ error: 'saved_id is required' });
|
|
154
|
+
const saved = db.getSavedDbConnections().find(c => c.id === saved_id);
|
|
155
|
+
if (!saved)
|
|
156
|
+
return res.status(404).json({ error: 'Saved connection not found' });
|
|
157
|
+
const connectionId = await connectionManager.connect(saved.db_type, deobfuscateParams(saved.params), saved.name);
|
|
158
|
+
db.touchDbConnection(saved_id);
|
|
159
|
+
res.json({ connection_id: connectionId });
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
res.status(500).json({ error: e.message });
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// Delete a saved connection
|
|
166
|
+
databasesRouter.delete('/api/databases/saved/:id', async (req, res) => {
|
|
167
|
+
try {
|
|
168
|
+
db.deleteDbConnection(req.params.id);
|
|
169
|
+
res.json({ ok: true });
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
res.status(500).json({ error: e.message });
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
databasesRouter.get('/api/databases/connections', (_req, res) => {
|
|
176
|
+
res.json(connectionManager.getActiveConnections());
|
|
177
|
+
});
|
|
178
|
+
// --- Query & Schema ---
|
|
179
|
+
databasesRouter.post('/api/databases/query', async (req, res) => {
|
|
180
|
+
try {
|
|
181
|
+
const { connection_id, query, limit, timeout_ms } = req.body;
|
|
182
|
+
if (!connection_id)
|
|
183
|
+
return res.status(400).json({ error: 'connection_id is required' });
|
|
184
|
+
if (!query)
|
|
185
|
+
return res.status(400).json({ error: 'query is required' });
|
|
186
|
+
const result = await connectionManager.query(connection_id, query, limit || 100, timeout_ms);
|
|
187
|
+
res.json(result);
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
res.status(500).json({ error: e.message });
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
// Update a single cell value (for inline editing)
|
|
194
|
+
databasesRouter.post('/api/databases/update-cell', async (req, res) => {
|
|
195
|
+
try {
|
|
196
|
+
const { connection_id, table, primary_key, column, value } = req.body;
|
|
197
|
+
if (!connection_id)
|
|
198
|
+
return res.status(400).json({ error: 'connection_id is required' });
|
|
199
|
+
if (!table)
|
|
200
|
+
return res.status(400).json({ error: 'table is required' });
|
|
201
|
+
if (!primary_key || typeof primary_key !== 'object')
|
|
202
|
+
return res.status(400).json({ error: 'primary_key object is required (e.g. {"id": 1})' });
|
|
203
|
+
if (!column)
|
|
204
|
+
return res.status(400).json({ error: 'column is required' });
|
|
205
|
+
// Build WHERE clause from primary key
|
|
206
|
+
const pkEntries = Object.entries(primary_key);
|
|
207
|
+
if (pkEntries.length === 0)
|
|
208
|
+
return res.status(400).json({ error: 'primary_key must have at least one field' });
|
|
209
|
+
const whereClause = pkEntries.map(([k], i) => `"${k}" = $${i + 2}`).join(' AND ');
|
|
210
|
+
const sql = `UPDATE "${table}" SET "${column}" = $1 WHERE ${whereClause}`;
|
|
211
|
+
const params = [value, ...pkEntries.map(([, v]) => v)];
|
|
212
|
+
// Use raw query with parameterized values — for now use a simple approach
|
|
213
|
+
// since our driver interface only supports string queries
|
|
214
|
+
const escapedValue = value === null ? 'NULL' : typeof value === 'number' ? String(value) : `'${String(value).replace(/'/g, "''")}'`;
|
|
215
|
+
const escapedWhere = pkEntries.map(([k, v]) => {
|
|
216
|
+
const ev = v === null ? 'IS NULL' : typeof v === 'number' ? `= ${v}` : `= '${String(v).replace(/'/g, "''")}'`;
|
|
217
|
+
return `"${k}" ${ev}`;
|
|
218
|
+
}).join(' AND ');
|
|
219
|
+
const rawSql = `UPDATE "${table}" SET "${column}" = ${escapedValue} WHERE ${escapedWhere}`;
|
|
220
|
+
const result = await connectionManager.query(connection_id, rawSql, 1);
|
|
221
|
+
res.json({ ok: true, affected: result.row_count });
|
|
222
|
+
}
|
|
223
|
+
catch (e) {
|
|
224
|
+
res.status(500).json({ error: e.message });
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
databasesRouter.get('/api/databases/:connectionId/tables', async (req, res) => {
|
|
228
|
+
try {
|
|
229
|
+
const tables = await connectionManager.getTables(req.params.connectionId);
|
|
230
|
+
res.json(tables);
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
res.status(500).json({ error: e.message });
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
databasesRouter.get('/api/databases/:connectionId/tables/:table', async (req, res) => {
|
|
237
|
+
try {
|
|
238
|
+
const schema = await connectionManager.describeTable(req.params.connectionId, req.params.table);
|
|
239
|
+
res.json(schema);
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
res.status(500).json({ error: e.message });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
databasesRouter.get('/api/databases/:connectionId/databases', async (req, res) => {
|
|
246
|
+
try {
|
|
247
|
+
const databases = await connectionManager.getDatabases(req.params.connectionId);
|
|
248
|
+
res.json(databases);
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
res.status(500).json({ error: e.message });
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
// --- MCP endpoint (called by MCP server process) ---
|
|
255
|
+
// NOTE: This is exported separately and registered in server.ts BEFORE the auth
|
|
256
|
+
// middleware, alongside other /api/mcp/* routes. It must NOT be on databasesRouter
|
|
257
|
+
// because that router is mounted after requireAuth.
|
|
258
|
+
export async function handleMcpConnectDatabase(req, res) {
|
|
259
|
+
try {
|
|
260
|
+
const { action, db_type, connection, query, table, connection_id, options } = req.body;
|
|
261
|
+
const limit = options?.limit || 100;
|
|
262
|
+
const timeoutMs = options?.timeout_ms || 30000;
|
|
263
|
+
const readonly = options?.readonly !== false; // default true
|
|
264
|
+
switch (action) {
|
|
265
|
+
case 'connect': {
|
|
266
|
+
if (!db_type)
|
|
267
|
+
return res.status(400).json({ error: 'db_type is required for connect' });
|
|
268
|
+
const id = await connectionManager.connect(db_type, connection || {}, undefined, readonly);
|
|
269
|
+
const tables = await connectionManager.getTables(id);
|
|
270
|
+
res.json({ connection_id: id, tables, message: `Connected to ${db_type} (${readonly ? 'read-only' : 'read-write'}). ${tables.length} tables found.` });
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case 'disconnect': {
|
|
274
|
+
if (!connection_id)
|
|
275
|
+
return res.status(400).json({ error: 'connection_id is required' });
|
|
276
|
+
await connectionManager.disconnect(connection_id);
|
|
277
|
+
res.json({ message: 'Disconnected' });
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case 'query': {
|
|
281
|
+
if (!connection_id)
|
|
282
|
+
return res.status(400).json({ error: 'connection_id is required' });
|
|
283
|
+
if (!query)
|
|
284
|
+
return res.status(400).json({ error: 'query is required' });
|
|
285
|
+
const result = await connectionManager.query(connection_id, query, limit, timeoutMs);
|
|
286
|
+
res.json(result);
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
case 'tables': {
|
|
290
|
+
if (!connection_id)
|
|
291
|
+
return res.status(400).json({ error: 'connection_id is required' });
|
|
292
|
+
const tables = await connectionManager.getTables(connection_id);
|
|
293
|
+
res.json({ tables });
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case 'describe': {
|
|
297
|
+
if (!connection_id)
|
|
298
|
+
return res.status(400).json({ error: 'connection_id is required' });
|
|
299
|
+
if (!table)
|
|
300
|
+
return res.status(400).json({ error: 'table is required' });
|
|
301
|
+
const schema = await connectionManager.describeTable(connection_id, table);
|
|
302
|
+
res.json(schema);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
case 'schema': {
|
|
306
|
+
if (!connection_id)
|
|
307
|
+
return res.status(400).json({ error: 'connection_id is required' });
|
|
308
|
+
const allTables = await connectionManager.getTables(connection_id);
|
|
309
|
+
const schemas = [];
|
|
310
|
+
for (const t of allTables.slice(0, 50)) {
|
|
311
|
+
try {
|
|
312
|
+
const s = await connectionManager.describeTable(connection_id, t.name);
|
|
313
|
+
schemas.push(s);
|
|
314
|
+
}
|
|
315
|
+
catch { }
|
|
316
|
+
}
|
|
317
|
+
res.json({ tables: allTables, schemas });
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
default:
|
|
321
|
+
res.status(400).json({ error: `Unknown action: ${action}` });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
catch (e) {
|
|
325
|
+
res.status(500).json({ error: e.message });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const environmentsRouter: import("express-serve-static-core").Router;
|
|
2
|
+
export declare let broadcastSettingsChange: (scope: string, key: string, settings: any, excludeWs?: any) => void;
|
|
3
|
+
export declare function setBroadcastSettingsChange(fn: typeof broadcastSettingsChange): void;
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import fsp from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { db } from '../state/db.js';
|
|
6
|
+
import * as gitOps from '../services/git-ops.js';
|
|
7
|
+
import { watchProject } from '../services/project-watcher.js';
|
|
8
|
+
import { isAuthEnabled } from '../auth/middleware.js';
|
|
9
|
+
export const environmentsRouter = Router();
|
|
10
|
+
// Broadcast function — set by server.ts when /ws/settings is initialized
|
|
11
|
+
export let broadcastSettingsChange = () => { };
|
|
12
|
+
export function setBroadcastSettingsChange(fn) {
|
|
13
|
+
broadcastSettingsChange = fn;
|
|
14
|
+
}
|
|
15
|
+
// ═══════════════════════════════════════════════════
|
|
16
|
+
// ENVIRONMENT ENDPOINTS
|
|
17
|
+
// ═══════════════════════════════════════════════════
|
|
18
|
+
// List environments (filtered by user when auth is enabled)
|
|
19
|
+
environmentsRouter.get('/api/environments', (req, res) => {
|
|
20
|
+
if (isAuthEnabled() && req.user) {
|
|
21
|
+
const envs = db.listEnvironmentsForUser(req.user.id);
|
|
22
|
+
return res.json(envs);
|
|
23
|
+
}
|
|
24
|
+
const envs = db.listEnvironments();
|
|
25
|
+
res.json(envs);
|
|
26
|
+
});
|
|
27
|
+
// Create environment
|
|
28
|
+
environmentsRouter.post('/api/environments', (req, res) => {
|
|
29
|
+
const { name } = req.body;
|
|
30
|
+
if (!name)
|
|
31
|
+
return res.status(400).json({ error: 'name is required' });
|
|
32
|
+
if (isAuthEnabled() && req.user) {
|
|
33
|
+
const env = db.createEnvironmentWithOwner(name, req.user.id);
|
|
34
|
+
return res.json(env);
|
|
35
|
+
}
|
|
36
|
+
const env = db.createEnvironment(name);
|
|
37
|
+
res.json(env);
|
|
38
|
+
});
|
|
39
|
+
// Delete environment (owner only when auth is enabled)
|
|
40
|
+
environmentsRouter.delete('/api/environments/:id', (req, res) => {
|
|
41
|
+
if (isAuthEnabled() && req.user) {
|
|
42
|
+
const owner = db.getEnvironmentOwner(req.params.id);
|
|
43
|
+
if (owner !== req.user.id) {
|
|
44
|
+
return res.status(403).json({ error: 'Only the environment owner can delete it' });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const deleted = db.deleteEnvironment(req.params.id);
|
|
48
|
+
if (!deleted)
|
|
49
|
+
return res.status(404).json({ error: 'Environment not found' });
|
|
50
|
+
res.json({ success: true });
|
|
51
|
+
});
|
|
52
|
+
// ═══════════════════════════════════════════════════
|
|
53
|
+
// ENVIRONMENT SHARING
|
|
54
|
+
// ═══════════════════════════════════════════════════
|
|
55
|
+
// List shares for an environment
|
|
56
|
+
environmentsRouter.get('/api/environments/:id/shares', (req, res) => {
|
|
57
|
+
if (isAuthEnabled() && req.user) {
|
|
58
|
+
if (!db.canAccessEnvironment(req.user.id, req.params.id)) {
|
|
59
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const shares = db.listEnvironmentShares(req.params.id);
|
|
63
|
+
res.json(shares);
|
|
64
|
+
});
|
|
65
|
+
// Share environment with a user (owner only)
|
|
66
|
+
environmentsRouter.post('/api/environments/:id/shares', (req, res) => {
|
|
67
|
+
const { user_id } = req.body;
|
|
68
|
+
if (!user_id)
|
|
69
|
+
return res.status(400).json({ error: 'user_id is required' });
|
|
70
|
+
if (isAuthEnabled() && req.user) {
|
|
71
|
+
const owner = db.getEnvironmentOwner(req.params.id);
|
|
72
|
+
if (owner !== req.user.id) {
|
|
73
|
+
return res.status(403).json({ error: 'Only the environment owner can share it' });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const targetUser = db.getUser(user_id);
|
|
77
|
+
if (!targetUser)
|
|
78
|
+
return res.status(404).json({ error: 'User not found' });
|
|
79
|
+
db.shareEnvironment(req.params.id, user_id, req.user?.id || 'system');
|
|
80
|
+
res.json({ ok: true });
|
|
81
|
+
});
|
|
82
|
+
// Revoke share
|
|
83
|
+
environmentsRouter.delete('/api/environments/:id/shares/:userId', (req, res) => {
|
|
84
|
+
if (isAuthEnabled() && req.user) {
|
|
85
|
+
const owner = db.getEnvironmentOwner(req.params.id);
|
|
86
|
+
if (owner !== req.user.id) {
|
|
87
|
+
return res.status(403).json({ error: 'Only the environment owner can manage shares' });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const deleted = db.unshareEnvironment(req.params.id, req.params.userId);
|
|
91
|
+
if (!deleted)
|
|
92
|
+
return res.status(404).json({ error: 'Share not found' });
|
|
93
|
+
res.json({ ok: true });
|
|
94
|
+
});
|
|
95
|
+
// ═══════════════════════════════════════════════════
|
|
96
|
+
// PROJECTS IN ENVIRONMENT
|
|
97
|
+
// ═══════════════════════════════════════════════════
|
|
98
|
+
// Get projects in an environment (with live isGit + pe_id)
|
|
99
|
+
environmentsRouter.get('/api/environments/:id/projects', async (req, res) => {
|
|
100
|
+
if (isAuthEnabled() && req.user && !db.canAccessEnvironment(req.user.id, req.params.id)) {
|
|
101
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
102
|
+
}
|
|
103
|
+
const env = db.getEnvironment(req.params.id);
|
|
104
|
+
if (!env)
|
|
105
|
+
return res.status(404).json({ error: 'Environment not found' });
|
|
106
|
+
const projects = db.getProjectsForEnvironment(req.params.id);
|
|
107
|
+
const withGit = await Promise.all(projects.map(async (p) => {
|
|
108
|
+
if (p.ssh_connection_id) {
|
|
109
|
+
// Remote project — skip local fs checks and watching
|
|
110
|
+
return { ...p, isGit: false };
|
|
111
|
+
}
|
|
112
|
+
let isGit = false;
|
|
113
|
+
try {
|
|
114
|
+
await fsp.access(path.join(p.path, '.git'));
|
|
115
|
+
isGit = true;
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
return { ...p, isGit };
|
|
119
|
+
}));
|
|
120
|
+
res.json(withGit);
|
|
121
|
+
});
|
|
122
|
+
// Create project + link to environment
|
|
123
|
+
environmentsRouter.post('/api/environments/:id/projects', async (req, res) => {
|
|
124
|
+
if (isAuthEnabled() && req.user && !db.canAccessEnvironment(req.user.id, req.params.id)) {
|
|
125
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
126
|
+
}
|
|
127
|
+
const env = db.getEnvironment(req.params.id);
|
|
128
|
+
if (!env)
|
|
129
|
+
return res.status(404).json({ error: 'Environment not found' });
|
|
130
|
+
const { name, path: projectPath, initGit, remoteUrl, ssh_connection_id, remote_path } = req.body;
|
|
131
|
+
if (!name || !projectPath) {
|
|
132
|
+
return res.status(400).json({ error: 'name and path are required' });
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
if (ssh_connection_id) {
|
|
136
|
+
// Remote project — skip local fs checks
|
|
137
|
+
const { sshManager } = await import('../services/ssh-manager.js');
|
|
138
|
+
const remoteGit = await import('../services/remote-git-ops.js');
|
|
139
|
+
if (initGit && remoteUrl) {
|
|
140
|
+
try {
|
|
141
|
+
await remoteGit.gitClone(ssh_connection_id, remoteUrl, projectPath);
|
|
142
|
+
}
|
|
143
|
+
catch { }
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// Ensure remote directory exists
|
|
147
|
+
try {
|
|
148
|
+
await sshManager.exec(ssh_connection_id, `mkdir -p '${projectPath.replace(/'/g, "'\\''")}'`);
|
|
149
|
+
}
|
|
150
|
+
catch { }
|
|
151
|
+
if (initGit) {
|
|
152
|
+
try {
|
|
153
|
+
await remoteGit.gitInit(ssh_connection_id, projectPath);
|
|
154
|
+
}
|
|
155
|
+
catch { }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const project = db.createProject(name, projectPath, {
|
|
159
|
+
sshConnectionId: ssh_connection_id,
|
|
160
|
+
remotePath: remote_path || projectPath,
|
|
161
|
+
});
|
|
162
|
+
const peId = db.linkProject(project.id, req.params.id);
|
|
163
|
+
let isGit = false;
|
|
164
|
+
try {
|
|
165
|
+
await sshManager.exec(ssh_connection_id, `test -d '${projectPath.replace(/'/g, "'\\''")}'/.git`);
|
|
166
|
+
isGit = true;
|
|
167
|
+
}
|
|
168
|
+
catch { }
|
|
169
|
+
res.json({ ...project, pe_id: peId, isGit });
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// Local project
|
|
173
|
+
const resolved = path.resolve(projectPath);
|
|
174
|
+
if (initGit && remoteUrl && !fs.existsSync(path.join(resolved, '.git'))) {
|
|
175
|
+
// Clone into target directory (git clone creates it if needed)
|
|
176
|
+
await gitOps.gitClone(remoteUrl, resolved);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
if (!fs.existsSync(resolved)) {
|
|
180
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
181
|
+
}
|
|
182
|
+
if (initGit && !fs.existsSync(path.join(resolved, '.git'))) {
|
|
183
|
+
await gitOps.gitInit(resolved);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const project = db.createProject(name, projectPath);
|
|
187
|
+
const peId = db.linkProject(project.id, req.params.id);
|
|
188
|
+
let isGit = false;
|
|
189
|
+
try {
|
|
190
|
+
isGit = fs.existsSync(path.join(resolved, '.git'));
|
|
191
|
+
}
|
|
192
|
+
catch { }
|
|
193
|
+
// Start watching the new project
|
|
194
|
+
watchProject(project.id, resolved);
|
|
195
|
+
res.json({ ...project, pe_id: peId, isGit });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
res.status(500).json({ error: err.message });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
// Connect an existing project from another environment
|
|
203
|
+
environmentsRouter.post('/api/environments/:id/connect-project', (req, res) => {
|
|
204
|
+
const env = db.getEnvironment(req.params.id);
|
|
205
|
+
if (!env)
|
|
206
|
+
return res.status(404).json({ error: 'Environment not found' });
|
|
207
|
+
const { project_id } = req.body;
|
|
208
|
+
if (!project_id)
|
|
209
|
+
return res.status(400).json({ error: 'project_id is required' });
|
|
210
|
+
const project = db.getProject(project_id);
|
|
211
|
+
if (!project)
|
|
212
|
+
return res.status(404).json({ error: 'Project not found' });
|
|
213
|
+
const peId = db.linkProject(project_id, req.params.id);
|
|
214
|
+
res.json({ success: true, pe_id: peId });
|
|
215
|
+
});
|
|
216
|
+
// Unlink project from environment by PE ID
|
|
217
|
+
environmentsRouter.delete('/api/project-links/:peId', (req, res) => {
|
|
218
|
+
const pe = db.getProjectEnvironment(req.params.peId);
|
|
219
|
+
if (!pe)
|
|
220
|
+
return res.status(404).json({ error: 'Link not found' });
|
|
221
|
+
const deleteProject = req.query.deleteProject === 'true';
|
|
222
|
+
const deleteFiles = req.query.deleteFiles === 'true';
|
|
223
|
+
db.unlinkProject(req.params.peId);
|
|
224
|
+
// Check remaining links
|
|
225
|
+
const remainingEnvs = db.getEnvironmentsForProject(pe.project_id);
|
|
226
|
+
if (remainingEnvs.length === 0 && deleteProject) {
|
|
227
|
+
// Delete working directory if requested
|
|
228
|
+
if (deleteFiles) {
|
|
229
|
+
const project = db.getProject(pe.project_id);
|
|
230
|
+
if (project && !project.ssh_connection_id) {
|
|
231
|
+
try {
|
|
232
|
+
fs.rmSync(project.path, { recursive: true, force: true });
|
|
233
|
+
}
|
|
234
|
+
catch { }
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
db.deleteProject(pe.project_id);
|
|
238
|
+
}
|
|
239
|
+
res.json({ success: true, remainingLinks: remainingEnvs.length });
|
|
240
|
+
});
|
|
241
|
+
// Resolve a project-environment link (for URL-based routing)
|
|
242
|
+
environmentsRouter.get('/api/project-links/:peId', (req, res) => {
|
|
243
|
+
const pe = db.getProjectEnvironment(req.params.peId);
|
|
244
|
+
if (!pe)
|
|
245
|
+
return res.status(404).json({ error: 'Project link not found' });
|
|
246
|
+
const project = db.getProject(pe.project_id);
|
|
247
|
+
const environment = db.getEnvironment(pe.environment_id);
|
|
248
|
+
res.json({ ...pe, project, environment });
|
|
249
|
+
});
|
|
250
|
+
// ═══════════════════════════════════════════════════
|
|
251
|
+
// ENVIRONMENT SETTINGS
|
|
252
|
+
// ═══════════════════════════════════════════════════
|
|
253
|
+
environmentsRouter.get('/api/environments/:id/settings', (req, res) => {
|
|
254
|
+
const settings = db.getEnvironmentSettings(req.params.id);
|
|
255
|
+
if (!settings)
|
|
256
|
+
return res.status(404).json({ error: 'Environment not found' });
|
|
257
|
+
res.json(settings);
|
|
258
|
+
});
|
|
259
|
+
environmentsRouter.put('/api/environments/:id/settings', (req, res) => {
|
|
260
|
+
db.updateEnvironmentSettings(req.params.id, req.body);
|
|
261
|
+
const settings = db.getEnvironmentSettings(req.params.id);
|
|
262
|
+
broadcastSettingsChange('environment', req.params.id, settings, req._settingsWs);
|
|
263
|
+
res.json(settings);
|
|
264
|
+
});
|
|
265
|
+
// ═══════════════════════════════════════════════════
|
|
266
|
+
// PROJECT SETTINGS (by project-environment link ID)
|
|
267
|
+
// ═══════════════════════════════════════════════════
|
|
268
|
+
environmentsRouter.get('/api/project-links/:peId/settings', (req, res) => {
|
|
269
|
+
const settings = db.getProjectSettings(req.params.peId);
|
|
270
|
+
res.json(settings);
|
|
271
|
+
});
|
|
272
|
+
environmentsRouter.put('/api/project-links/:peId/settings', (req, res) => {
|
|
273
|
+
try {
|
|
274
|
+
db.updateProjectSettings(req.params.peId, req.body);
|
|
275
|
+
}
|
|
276
|
+
catch (e) {
|
|
277
|
+
if (e.code === 'SQLITE_CONSTRAINT_FOREIGNKEY') {
|
|
278
|
+
res.status(404).json({ error: 'Project link not found' });
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
throw e;
|
|
282
|
+
}
|
|
283
|
+
const settings = db.getProjectSettings(req.params.peId);
|
|
284
|
+
broadcastSettingsChange('project', req.params.peId, settings, req._settingsWs);
|
|
285
|
+
res.json(settings);
|
|
286
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const githubRouter: import("express-serve-static-core").Router;
|