@cloudcli-ai/cloudcli 1.37.2 → 1.37.4
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/README.md +269 -0
- package/dist/assets/{abnfDiagram-N423BO3Z-C4UAoHJb.js → abnfDiagram-N423BO3Z-Dmkwx38H.js} +1 -1
- package/dist/assets/{arc-B8jF-ja9.js → arc-DLe9AYuO.js} +1 -1
- package/dist/assets/{architectureDiagram-T3A2C74G-C_Wur_CU.js → architectureDiagram-T3A2C74G-CBbbbNUW.js} +1 -1
- package/dist/assets/{blockDiagram-VBNYF7ZC-Cq4P1i8t.js → blockDiagram-VBNYF7ZC-CaMEaWu4.js} +1 -1
- package/dist/assets/{c4Diagram-5PPSVZJV-C1wG4eUj.js → c4Diagram-5PPSVZJV-DhjMgNnR.js} +1 -1
- package/dist/assets/channel-CWrmwsir.js +1 -0
- package/dist/assets/{chunk-2GRJ4B5K-B8fnBG6E.js → chunk-2GRJ4B5K-yjl3-F-i.js} +1 -1
- package/dist/assets/{chunk-2Q5K7J3B-WdfPooPJ.js → chunk-2Q5K7J3B-DsAYaWEP.js} +1 -1
- package/dist/assets/{chunk-5RXB4S5H-DhZJuG2E.js → chunk-5RXB4S5H-Bt5qmqOX.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-B0rjslal.js → chunk-5VM5RSS4-yWrWVvUt.js} +1 -1
- package/dist/assets/{chunk-6Q2QTUOP-CG2Oj4jL.js → chunk-6Q2QTUOP-Dt66K5xb.js} +1 -1
- package/dist/assets/{chunk-GF5L2VYU-rrl2lRxY.js → chunk-GF5L2VYU-DA0vzaj4.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CjVbrVMA.js → chunk-JWPE2WC7-CUkbx96V.js} +1 -1
- package/dist/assets/{chunk-KBJHAD2P-CRzKgU8S.js → chunk-KBJHAD2P-DWKxx1WH.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-DBz4GTZF.js → chunk-RYQCIY6F-BqluplN0.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-wHxalsXk.js → chunk-XXDRQBXY-DelKg3Xf.js} +1 -1
- package/dist/assets/classDiagram-JCYQIIEL-COAGCC6w.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-COAGCC6w.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-s1hygY8e.js → cose-bilkent-JH36ORCC-DHV93-Em.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-C4LGV15i.js → cynefin-VYW2F7L2-B9HZgoGX.js} +1 -1
- package/dist/assets/{cynefinDiagram-MW4NZA55-C5uhu7B3.js → cynefinDiagram-MW4NZA55-hFjjEQg8.js} +1 -1
- package/dist/assets/{dagre-VZM6K2ZE-B-OBTewm.js → dagre-VZM6K2ZE-CpeVOC7V.js} +1 -1
- package/dist/assets/{diagram-7IWD3JNH-BjGKQxih.js → diagram-7IWD3JNH-D0H1UoRA.js} +1 -1
- package/dist/assets/{diagram-B4RE2ZJO-BMKz1X2L.js → diagram-B4RE2ZJO-6Y7BQUup.js} +1 -1
- package/dist/assets/{diagram-LBJQPF4R-DNh-GhKC.js → diagram-LBJQPF4R-C0nNOwht.js} +1 -1
- package/dist/assets/{diagram-Q27KOJAE-BaTG9jPT.js → diagram-Q27KOJAE-En6QlOVX.js} +1 -1
- package/dist/assets/{diagram-UB23O5K3-BUwo2OvJ.js → diagram-UB23O5K3-CmPiNeGD.js} +1 -1
- package/dist/assets/{ebnfDiagram-BXEA7PRR-DAZgfzX2.js → ebnfDiagram-BXEA7PRR-Bygzp1Qt.js} +1 -1
- package/dist/assets/{erDiagram-JOGREHBK-1w1CcGEb.js → erDiagram-JOGREHBK-CwScRfzl.js} +1 -1
- package/dist/assets/{flowDiagram-UKHOOZJN-D4EII8Jj.js → flowDiagram-UKHOOZJN-bKX-0xP6.js} +1 -1
- package/dist/assets/{ganttDiagram-PKOTCBZU-ChvJZAgB.js → ganttDiagram-PKOTCBZU-DtZPKXGd.js} +1 -1
- package/dist/assets/{gitGraphDiagram-DS77QQ5N-DtOn41S8.js → gitGraphDiagram-DS77QQ5N-CUOtdL1o.js} +1 -1
- package/dist/assets/index-CdjIFxM2.js +5004 -0
- package/dist/assets/index-DgafhGTF.css +32 -0
- package/dist/assets/{infoDiagram-6WML65LV-DN_PVEJZ.js → infoDiagram-6WML65LV-ciRQTS0h.js} +1 -1
- package/dist/assets/{ishikawaDiagram-WSZJBQD7-DbkF1kXb.js → ishikawaDiagram-WSZJBQD7-CgjhQZ84.js} +1 -1
- package/dist/assets/{journeyDiagram-NVQOT4AX-pJwmhddE.js → journeyDiagram-NVQOT4AX-BsbQSeIW.js} +1 -1
- package/dist/assets/{kanban-definition-27J2QSJJ-PPAPgNp5.js → kanban-definition-27J2QSJJ-CrwzY9Jw.js} +1 -1
- package/dist/assets/{linear-iPR-J9f7.js → linear-BUaCDr6l.js} +1 -1
- package/dist/assets/{mermaid.core-BEUL86Z6.js → mermaid.core-ClgdUU0j.js} +5 -5
- package/dist/assets/{mindmap-definition-FAOFIHXS-BvZoBib7.js → mindmap-definition-FAOFIHXS-BictljY1.js} +1 -1
- package/dist/assets/{pegDiagram-VL7TDLO6-DhYreaUd.js → pegDiagram-VL7TDLO6-BXdByy_U.js} +1 -1
- package/dist/assets/{pieDiagram-7S7Q4E2Y-CrOHlyD0.js → pieDiagram-7S7Q4E2Y-uZsMfkRX.js} +1 -1
- package/dist/assets/{quadrantDiagram-CIZ2JOQS-D4g5yH3t.js → quadrantDiagram-CIZ2JOQS-7lCv4QIj.js} +1 -1
- package/dist/assets/{railroadDiagram-AXF67PYL-Dj39lHlf.js → railroadDiagram-AXF67PYL-07dxIWbi.js} +1 -1
- package/dist/assets/{requirementDiagram-LRYGKXZP-1AS4Zgm_.js → requirementDiagram-LRYGKXZP-Cb31Yv1_.js} +1 -1
- package/dist/assets/{sankeyDiagram-W5VNT64P-CKlMn19P.js → sankeyDiagram-W5VNT64P-X0YPSyF9.js} +1 -1
- package/dist/assets/{sequenceDiagram-SI44F4Z6-B_j3qyvo.js → sequenceDiagram-SI44F4Z6-BdBxSIlP.js} +1 -1
- package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
- package/dist/assets/{sizeCapture-X5ZJPWSS-Bp2Op16v.js → sizeCapture-X5ZJPWSS-DQM5xAyD.js} +1 -1
- package/dist/assets/{stateDiagram-OKZ733FA-C0cN6DoY.js → stateDiagram-OKZ733FA-DJ3xkLUF.js} +1 -1
- package/dist/assets/stateDiagram-v2-UEYNNEHI-vAaE4zNo.js +1 -0
- package/dist/assets/{swimlanes-SLNWSIFB-BEvXOV9b.js → swimlanes-SLNWSIFB-rjfYdW0w.js} +2 -2
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-TV2IF4Nq.js +8 -0
- package/dist/assets/{timeline-definition-Z64GVDOM-zdJQinvZ.js → timeline-definition-Z64GVDOM-DZMra5do.js} +1 -1
- package/dist/assets/{vendor-codemirror-OwyKSvPE.js → vendor-codemirror-CYrSt7-U.js} +18 -18
- package/dist/assets/{vennDiagram-T6HMQDX7-DuF1uZMY.js → vennDiagram-T6HMQDX7-BevD8uu0.js} +1 -1
- package/dist/assets/{wardleyDiagram-T6FBY63Y-B9AlLQup.js → wardleyDiagram-T6FBY63Y-DsIyXB7z.js} +1 -1
- package/dist/assets/{xychartDiagram-ELKLHX3M-DTJX48Qg.js → xychartDiagram-ELKLHX3M-G3PlF3ko.js} +1 -1
- package/dist/index.html +3 -3
- package/dist-server/server/index.js +11 -4
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/agent/agent.module.js +9 -2
- package/dist-server/server/modules/agent/agent.module.js.map +1 -1
- package/dist-server/server/modules/agent/agent.routes.js +177 -130
- package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
- package/dist-server/server/modules/agent/tests/agent-run-registration.test.js +306 -0
- package/dist-server/server/modules/agent/tests/agent-run-registration.test.js.map +1 -0
- package/dist-server/server/modules/agent/tests/agent.routes.test.js +48 -2
- package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
- package/dist-server/server/modules/browser-use/browser-use.routes.js +39 -33
- package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -1
- package/dist-server/server/modules/database/connection.js +1 -1
- package/dist-server/server/modules/database/connection.js.map +1 -1
- package/dist-server/server/modules/database/index.js +5 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +45 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/provider-models.js +60 -6
- package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -1
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js +71 -0
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js +105 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +144 -5
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/user-preferences.db.js +65 -0
- package/dist-server/server/modules/database/repositories/user-preferences.db.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +99 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +99 -0
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +39 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js +105 -0
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/file-tree/file-tree.module.js +2 -1
- package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -1
- package/dist-server/server/modules/file-tree/file-tree.service.js +44 -7
- package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +126 -1
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
- package/dist-server/server/modules/git/git-branch-diff.service.js +142 -0
- package/dist-server/server/modules/git/git-branch-diff.service.js.map +1 -0
- package/dist-server/server/modules/git/git-branch.service.js.map +1 -1
- package/dist-server/server/modules/git/git.routes.js +92 -5
- package/dist-server/server/modules/git/git.routes.js.map +1 -1
- package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js +160 -0
- package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/project-clone.routes.js +108 -0
- package/dist-server/server/modules/projects/project-clone.routes.js.map +1 -0
- package/dist-server/server/modules/projects/projects.routes.js +13 -66
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-clone-request.service.js +36 -0
- package/dist-server/server/modules/projects/services/project-clone-request.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js +86 -32
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js +12 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-star.service.js +2 -2
- package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -1
- package/dist-server/server/modules/projects/tests/project-clone.routes.test.js +191 -0
- package/dist-server/server/modules/projects/tests/project-clone.routes.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js +275 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +3 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +62 -9
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js +46 -0
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +31 -12
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +472 -53
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +38 -11
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +1007 -60
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +4 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +204 -0
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js +32 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +47 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +71 -53
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +8 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +1621 -431
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +9 -2
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js +16 -8
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +274 -9
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
- package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +19 -2
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +134 -2
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js +100 -0
- package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js +97 -0
- package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +23 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +59 -5
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-runtime.service.js +8 -0
- package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js +139 -35
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +2 -4
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-history-cache.service.js +91 -0
- package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +105 -32
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +3 -60
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +252 -12
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-auth.test.js +183 -0
- package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-background-work.test.js +172 -0
- package/dist-server/server/modules/providers/tests/claude-background-work.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-compaction.test.js +84 -0
- package/dist-server/server/modules/providers/tests/claude-compaction.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-message-model.test.js +123 -0
- package/dist-server/server/modules/providers/tests/claude-message-model.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-models.test.js +39 -0
- package/dist-server/server/modules/providers/tests/claude-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js +265 -0
- package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js +1815 -33
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js +228 -0
- package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js +16 -0
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js +85 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js +338 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-models.test.js +25 -0
- package/dist-server/server/modules/providers/tests/codex-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js +83 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js +54 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +354 -15
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/cursor-auth.test.js +167 -0
- package/dist-server/server/modules/providers/tests/cursor-auth.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +196 -4
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +19 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js +30 -0
- package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +79 -1
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +164 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js +190 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/session-fork.test.js +113 -0
- package/dist-server/server/modules/providers/tests/session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js +139 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js +90 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js +122 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +219 -1
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/skills.test.js +15 -3
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
- package/dist-server/server/modules/scheduled-messages/index.js +5 -0
- package/dist-server/server/modules/scheduled-messages/index.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js +47 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +175 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js +95 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +290 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -0
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js +23 -23
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +48 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -1
- package/dist-server/server/modules/user/tests/user.service.test.js +70 -0
- package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -1
- package/dist-server/server/modules/user/user.module.js +10 -1
- package/dist-server/server/modules/user/user.module.js.map +1 -1
- package/dist-server/server/modules/user/user.routes.js +44 -0
- package/dist-server/server/modules/user/user.routes.js.map +1 -1
- package/dist-server/server/modules/user/user.service.js +75 -0
- package/dist-server/server/modules/user/user.service.js.map +1 -1
- package/dist-server/server/modules/voice/tests/voice.service.test.js +3 -2
- package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -1
- package/dist-server/server/modules/websocket/index.js +7 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +37 -49
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +35 -5
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +204 -10
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js +94 -0
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +236 -20
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js +137 -0
- package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js +263 -0
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +29 -3
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js +96 -0
- package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js +106 -0
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +500 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
- package/dist-server/server/shared/claude-cli-path.js +22 -5
- package/dist-server/server/shared/claude-cli-path.js.map +1 -1
- package/dist-server/server/shared/index.js +3 -0
- package/dist-server/server/shared/index.js.map +1 -0
- package/dist-server/server/shared/message-unification.js +376 -0
- package/dist-server/server/shared/message-unification.js.map +1 -0
- package/dist-server/server/shared/tests/claude-cli-path.test.js +45 -1
- package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -1
- package/dist-server/server/shared/tests/message-unification.test.js +174 -0
- package/dist-server/server/shared/tests/message-unification.test.js.map +1 -0
- package/dist-server/server/shared/tests/read-only-roots.test.js +124 -0
- package/dist-server/server/shared/tests/read-only-roots.test.js.map +1 -0
- package/dist-server/server/shared/tests/strip-ansi-sequences.test.js +31 -0
- package/dist-server/server/shared/tests/strip-ansi-sequences.test.js.map +1 -0
- package/dist-server/server/shared/utils.js +160 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/electron/desktopWindow.js +6 -0
- package/electron/viewHost.js +25 -2
- package/package.json +20 -17
- package/server/index.ts +16 -5
- package/server/modules/agent/agent.module.ts +11 -1
- package/server/modules/agent/agent.routes.ts +200 -142
- package/server/modules/agent/tests/agent-run-registration.test.ts +348 -0
- package/server/modules/agent/tests/agent.routes.test.ts +51 -2
- package/server/modules/browser-use/browser-use.routes.ts +96 -33
- package/server/modules/database/connection.ts +2 -1
- package/server/modules/database/index.ts +10 -0
- package/server/modules/database/migrations.ts +51 -0
- package/server/modules/database/repositories/provider-models.ts +79 -6
- package/server/modules/database/repositories/scheduled-messages.db.ts +127 -0
- package/server/modules/database/repositories/session-drafts.db.ts +171 -0
- package/server/modules/database/repositories/sessions.db.ts +204 -3
- package/server/modules/database/repositories/user-preferences.db.ts +80 -0
- package/server/modules/database/schema.ts +103 -0
- package/server/modules/database/tests/provider-models.db.integration.test.ts +113 -0
- package/server/modules/database/tests/sessions.db.integration.test.ts +52 -0
- package/server/modules/database/tests/user-preferences.db.integration.test.ts +135 -0
- package/server/modules/file-tree/file-tree.module.ts +2 -1
- package/server/modules/file-tree/file-tree.service.ts +49 -7
- package/server/modules/file-tree/tests/file-tree.service.test.ts +163 -1
- package/server/modules/git/git-branch-diff.service.ts +204 -0
- package/server/modules/git/git-branch.service.ts +2 -12
- package/server/modules/git/git.routes.ts +100 -6
- package/server/modules/git/tests/git-branch-diff.service.test.ts +210 -0
- package/server/modules/projects/project-clone.routes.ts +142 -0
- package/server/modules/projects/projects.routes.ts +16 -81
- package/server/modules/projects/services/project-clone-request.service.ts +56 -0
- package/server/modules/projects/services/project-clone.service.ts +102 -34
- package/server/modules/projects/services/project-delete.service.ts +13 -1
- package/server/modules/projects/services/project-star.service.ts +2 -2
- package/server/modules/projects/tests/project-clone.routes.test.ts +246 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +376 -0
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +81 -11
- package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
- package/server/modules/providers/list/claude/claude-models.provider.ts +37 -17
- package/server/modules/providers/list/claude/claude-runtime.provider.js +510 -54
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +40 -14
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +1195 -63
- package/server/modules/providers/list/claude/claude-skills.provider.ts +4 -1
- package/server/modules/providers/list/claude/claude.provider.ts +3 -0
- package/server/modules/providers/list/codex/codex-app-server.client.ts +243 -0
- package/server/modules/providers/list/codex/codex-fork.provider.ts +40 -0
- package/server/modules/providers/list/codex/codex-models.provider.ts +47 -0
- package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +105 -70
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +1847 -455
- package/server/modules/providers/list/codex/codex.provider.ts +3 -0
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +10 -2
- package/server/modules/providers/list/cursor/cursor-runtime.provider.js +17 -8
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +297 -9
- package/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +30 -2
- package/server/modules/providers/provider.routes.ts +162 -2
- package/server/modules/providers/services/claude-cli-liveness.service.ts +121 -0
- package/server/modules/providers/services/claude-settings-permissions.service.ts +127 -0
- package/server/modules/providers/services/provider-capabilities.service.ts +40 -1
- package/server/modules/providers/services/provider-models.service.ts +71 -4
- package/server/modules/providers/services/provider-runtime.service.ts +11 -0
- package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
- package/server/modules/providers/services/session-conversations-search.service.ts +2 -5
- package/server/modules/providers/services/session-history-cache.service.ts +150 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +125 -38
- package/server/modules/providers/services/sessions-watcher.service.ts +3 -64
- package/server/modules/providers/services/sessions.service.ts +309 -17
- package/server/modules/providers/tests/claude-auth.test.ts +208 -0
- package/server/modules/providers/tests/claude-background-work.test.ts +213 -0
- package/server/modules/providers/tests/claude-compaction.test.ts +101 -0
- package/server/modules/providers/tests/claude-message-model.test.ts +160 -0
- package/server/modules/providers/tests/claude-models.test.ts +87 -0
- package/server/modules/providers/tests/claude-runtime-hold.test.ts +309 -0
- package/server/modules/providers/tests/claude-sessions.test.ts +2167 -37
- package/server/modules/providers/tests/claude-settings-permissions.test.ts +271 -0
- package/server/modules/providers/tests/claude-subagent-echo.test.ts +25 -0
- package/server/modules/providers/tests/claude-token-budget.test.ts +104 -0
- package/server/modules/providers/tests/codex-message-editing.test.ts +419 -0
- package/server/modules/providers/tests/codex-models.test.ts +37 -0
- package/server/modules/providers/tests/codex-runtime.test.ts +94 -0
- package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +396 -15
- package/server/modules/providers/tests/cursor-auth.test.ts +187 -0
- package/server/modules/providers/tests/opencode-models.test.ts +266 -8
- package/server/modules/providers/tests/opencode-sessions.test.ts +40 -1
- package/server/modules/providers/tests/provider-capabilities.service.test.ts +37 -0
- package/server/modules/providers/tests/provider-models.service.test.ts +99 -1
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
- package/server/modules/providers/tests/provider.routes.test.ts +230 -1
- package/server/modules/providers/tests/session-fork.test.ts +173 -0
- package/server/modules/providers/tests/session-history-cache.service.test.ts +165 -0
- package/server/modules/providers/tests/session-orphan-prune.test.ts +107 -0
- package/server/modules/providers/tests/session-synchronizer-coalescing.test.ts +136 -0
- package/server/modules/providers/tests/sessions.service.test.ts +303 -1
- package/server/modules/providers/tests/skills.test.ts +21 -5
- package/server/modules/scheduled-messages/index.ts +8 -0
- package/server/modules/scheduled-messages/scheduled-messages.routes.ts +68 -0
- package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +216 -0
- package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
- package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +354 -0
- package/server/modules/taskmaster/taskmaster.routes.ts +23 -33
- package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +57 -0
- package/server/modules/user/tests/user.service.test.ts +90 -0
- package/server/modules/user/user.module.ts +10 -1
- package/server/modules/user/user.routes.ts +44 -0
- package/server/modules/user/user.service.ts +105 -0
- package/server/modules/voice/tests/voice.service.test.ts +3 -2
- package/server/modules/websocket/README.md +4 -2
- package/server/modules/websocket/index.ts +8 -0
- package/server/modules/websocket/services/chat-run-registry.service.ts +45 -54
- package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
- package/server/modules/websocket/services/chat-websocket.service.ts +302 -16
- package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +276 -22
- package/server/modules/websocket/tests/chat-background-subscribe.test.ts +167 -0
- package/server/modules/websocket/tests/chat-edit-send.test.ts +309 -0
- package/server/modules/websocket/tests/chat-run-registry.test.ts +34 -3
- package/server/modules/websocket/tests/chat-stop-task.test.ts +127 -0
- package/server/modules/websocket/tests/session-upsert-broadcast.test.ts +138 -0
- package/server/modules/websocket/tests/shell-websocket.service.test.ts +629 -0
- package/server/shared/claude-cli-path.ts +24 -6
- package/server/shared/index.ts +3 -0
- package/server/shared/interfaces.ts +91 -0
- package/server/shared/message-unification.ts +449 -0
- package/server/shared/tests/claude-cli-path.test.ts +58 -1
- package/server/shared/tests/message-unification.test.ts +218 -0
- package/server/shared/tests/read-only-roots.test.ts +139 -0
- package/server/shared/tests/strip-ansi-sequences.test.ts +42 -0
- package/server/shared/types.ts +382 -7
- package/server/shared/utils.ts +176 -0
- package/dist/assets/channel-_falYbU1.js +0 -1
- package/dist/assets/classDiagram-JCYQIIEL-Dn-3CfpR.js +0 -1
- package/dist/assets/classDiagram-v2-OCEON4UE-Dn-3CfpR.js +0 -1
- package/dist/assets/index-CzPLBvXT.css +0 -32
- package/dist/assets/index-DFkBBuSA.js +0 -1549
- package/dist/assets/stateDiagram-v2-UEYNNEHI-BvBxUUrZ.js +0 -1
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-CsE-x4YK.js +0 -8
|
@@ -44,7 +44,11 @@ type CloneProjectDependencies = {
|
|
|
44
44
|
tokenId: number,
|
|
45
45
|
userId: number,
|
|
46
46
|
) => Promise<{ github_token: string } | null>;
|
|
47
|
-
spawnGitClone: (
|
|
47
|
+
spawnGitClone: (
|
|
48
|
+
cloneUrl: string,
|
|
49
|
+
clonePath: string,
|
|
50
|
+
environment: NodeJS.ProcessEnv,
|
|
51
|
+
) => GitCloneProcess;
|
|
48
52
|
registerProject: (projectPath: string, customName: string) => Promise<{ project: Record<string, unknown> }>;
|
|
49
53
|
logError: (message: string, error: unknown) => void;
|
|
50
54
|
};
|
|
@@ -67,16 +71,42 @@ async function defaultPathExists(targetPath: string): Promise<boolean> {
|
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
/**
|
|
75
|
+
* The only origin the credential helper answers for. The token is a GitHub
|
|
76
|
+
* token; a helper that answered every challenge would hand it to whatever
|
|
77
|
+
* host the clone URL names — and over plain http, in the clear.
|
|
78
|
+
*/
|
|
79
|
+
const GITHUB_TOKEN_CREDENTIAL_SCOPE = 'credential.https://github.com.helper';
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Builds the environment the clone runs in. The token never goes into the
|
|
83
|
+
* clone URL: git echoes that URL on stderr (which is the SSE progress stream),
|
|
84
|
+
* it sits in the process argv (readable through /proc) and it is written to the
|
|
85
|
+
* cloned repo's `.git/config` as the remote. Instead env-only config points git
|
|
86
|
+
* at a credential helper that reads the token from its own environment, so no
|
|
87
|
+
* channel git exposes carries it. The empty first helper entry clears any
|
|
88
|
+
* helper configured on the machine so a credential stored there cannot shadow
|
|
89
|
+
* the one the user selected; the helper itself is scoped to github.com over
|
|
90
|
+
* https, so a clone from any other host gets no credential at all.
|
|
91
|
+
*/
|
|
92
|
+
function buildGitCloneEnvironment(githubToken: string | null): NodeJS.ProcessEnv {
|
|
93
|
+
if (!githubToken) {
|
|
94
|
+
return { ...process.env, GIT_TERMINAL_PROMPT: '0' };
|
|
73
95
|
}
|
|
74
96
|
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
return {
|
|
98
|
+
...process.env,
|
|
99
|
+
GIT_CONFIG_COUNT: '2',
|
|
100
|
+
GIT_CONFIG_KEY_0: 'credential.helper',
|
|
101
|
+
GIT_CONFIG_VALUE_0: '',
|
|
102
|
+
GIT_CONFIG_KEY_1: GITHUB_TOKEN_CREDENTIAL_SCOPE,
|
|
103
|
+
GIT_CONFIG_VALUE_1: '!f() { echo username=x-access-token; echo "password=$CLOUDCLI_GITHUB_TOKEN"; }; f',
|
|
104
|
+
CLOUDCLI_GITHUB_TOKEN: githubToken,
|
|
105
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
106
|
+
};
|
|
77
107
|
}
|
|
78
108
|
|
|
79
|
-
function resolveCloneFailureMessage(lastError: string
|
|
109
|
+
function resolveCloneFailureMessage(lastError: string): string {
|
|
80
110
|
if (lastError.includes('Authentication failed') || lastError.includes('could not read Username')) {
|
|
81
111
|
return 'Authentication failed. Please check your credentials.';
|
|
82
112
|
}
|
|
@@ -89,11 +119,7 @@ function resolveCloneFailureMessage(lastError: string, sanitizedError: string):
|
|
|
89
119
|
return 'Directory already exists';
|
|
90
120
|
}
|
|
91
121
|
|
|
92
|
-
|
|
93
|
-
return sanitizedError;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return 'Git clone failed';
|
|
122
|
+
return lastError || 'Git clone failed';
|
|
97
123
|
}
|
|
98
124
|
|
|
99
125
|
function resolveErrorMessage(error: unknown): string {
|
|
@@ -126,13 +152,14 @@ const defaultDependencies: CloneProjectDependencies = {
|
|
|
126
152
|
| null;
|
|
127
153
|
return tokenRow;
|
|
128
154
|
},
|
|
129
|
-
spawnGitClone: (
|
|
155
|
+
spawnGitClone: (
|
|
156
|
+
cloneUrl: string,
|
|
157
|
+
clonePath: string,
|
|
158
|
+
environment: NodeJS.ProcessEnv,
|
|
159
|
+
): GitCloneProcess =>
|
|
130
160
|
spawn('git', ['clone', '--progress', '--', cloneUrl, clonePath], {
|
|
131
161
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
132
|
-
env:
|
|
133
|
-
...process.env,
|
|
134
|
-
GIT_TERMINAL_PROMPT: '0',
|
|
135
|
-
},
|
|
162
|
+
env: environment,
|
|
136
163
|
}) as unknown as GitCloneProcess,
|
|
137
164
|
registerProject: async (
|
|
138
165
|
projectPath: string,
|
|
@@ -147,11 +174,40 @@ const defaultDependencies: CloneProjectDependencies = {
|
|
|
147
174
|
},
|
|
148
175
|
};
|
|
149
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Whether a clone URL embeds a credential: any password, or a username on an
|
|
179
|
+
* http(s) URL. An SSH URL's `git@` is a login name, not a secret.
|
|
180
|
+
*/
|
|
181
|
+
function carriesCredentials(url: string): boolean {
|
|
182
|
+
let parsed: URL;
|
|
183
|
+
try {
|
|
184
|
+
parsed = new URL(url);
|
|
185
|
+
} catch {
|
|
186
|
+
// scp-like `git@github.com:org/repo.git` and bare paths are not URLs;
|
|
187
|
+
// an scp-like user is an SSH login, which is not a credential.
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (parsed.password) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
return Boolean(parsed.username) && (parsed.protocol === 'http:' || parsed.protocol === 'https:');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function isParsableUrl(url: string): boolean {
|
|
197
|
+
try {
|
|
198
|
+
new URL(url);
|
|
199
|
+
return true;
|
|
200
|
+
} catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
150
205
|
export async function startCloneProject(
|
|
151
206
|
input: CloneProjectInput,
|
|
152
207
|
handlers: CloneProjectEventHandlers,
|
|
153
|
-
|
|
208
|
+
dependencyOverrides: Partial<CloneProjectDependencies> = {},
|
|
154
209
|
): Promise<CloneProjectOperation> {
|
|
210
|
+
const dependencies = { ...defaultDependencies, ...dependencyOverrides };
|
|
155
211
|
const normalizedWorkspacePath = input.workspacePath.trim();
|
|
156
212
|
const normalizedGithubUrl = input.githubUrl.trim();
|
|
157
213
|
|
|
@@ -176,6 +232,25 @@ export async function startCloneProject(
|
|
|
176
232
|
});
|
|
177
233
|
}
|
|
178
234
|
|
|
235
|
+
// An http(s) string the URL parser rejects (a bad port, a space in the
|
|
236
|
+
// host) is one curl rejects too — but only after git has carried it, and
|
|
237
|
+
// any credential in it, on its command line.
|
|
238
|
+
if (/^https?:/i.test(normalizedGithubUrl) && !isParsableUrl(normalizedGithubUrl)) {
|
|
239
|
+
throw new AppError('Invalid githubUrl', {
|
|
240
|
+
code: 'INVALID_GITHUB_URL',
|
|
241
|
+
statusCode: 400,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (carriesCredentials(normalizedGithubUrl)) {
|
|
246
|
+
// The token field is the only way in: a credential in the URL would ride
|
|
247
|
+
// git's argv, its stderr and the clone's `.git/config`.
|
|
248
|
+
throw new AppError('Put the token in the token field, not in the URL', {
|
|
249
|
+
code: 'GITHUB_URL_CARRIES_CREDENTIALS',
|
|
250
|
+
statusCode: 400,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
179
254
|
const pathValidation = await dependencies.validatePath(normalizedWorkspacePath);
|
|
180
255
|
if (!pathValidation.valid || !pathValidation.resolvedPath) {
|
|
181
256
|
throw new AppError(pathValidation.error || 'Invalid workspace path', {
|
|
@@ -225,22 +300,16 @@ export async function startCloneProject(
|
|
|
225
300
|
);
|
|
226
301
|
}
|
|
227
302
|
|
|
228
|
-
let cloneUrl = normalizedGithubUrl;
|
|
229
|
-
if (githubToken) {
|
|
230
|
-
try {
|
|
231
|
-
const url = new URL(normalizedGithubUrl);
|
|
232
|
-
url.username = githubToken;
|
|
233
|
-
url.password = '';
|
|
234
|
-
cloneUrl = url.toString();
|
|
235
|
-
} catch {
|
|
236
|
-
// SSH URLs cannot be represented by URL constructor and are used as-is.
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
303
|
handlers.onProgress(`Cloning into '${repoName}'...`);
|
|
241
|
-
const gitProcess = dependencies.spawnGitClone(
|
|
304
|
+
const gitProcess = dependencies.spawnGitClone(
|
|
305
|
+
normalizedGithubUrl,
|
|
306
|
+
clonePath,
|
|
307
|
+
buildGitCloneEnvironment(githubToken),
|
|
308
|
+
);
|
|
242
309
|
let lastError = '';
|
|
243
310
|
|
|
311
|
+
// `git clone --progress` writes every byte of its progress to stderr. Git
|
|
312
|
+
// was handed a credential-free URL, so each chunk is forwarded as it arrives.
|
|
244
313
|
gitProcess.stdout?.on('data', (data: Buffer | string) => {
|
|
245
314
|
const message = data.toString().trim();
|
|
246
315
|
if (message) {
|
|
@@ -250,8 +319,8 @@ export async function startCloneProject(
|
|
|
250
319
|
|
|
251
320
|
gitProcess.stderr?.on('data', (data: Buffer | string) => {
|
|
252
321
|
const message = data.toString().trim();
|
|
253
|
-
lastError = message;
|
|
254
322
|
if (message) {
|
|
323
|
+
lastError = message;
|
|
255
324
|
handlers.onProgress(message);
|
|
256
325
|
}
|
|
257
326
|
});
|
|
@@ -277,8 +346,7 @@ export async function startCloneProject(
|
|
|
277
346
|
return;
|
|
278
347
|
}
|
|
279
348
|
|
|
280
|
-
const
|
|
281
|
-
const errorMessage = resolveCloneFailureMessage(lastError, sanitizedError);
|
|
349
|
+
const errorMessage = resolveCloneFailureMessage(lastError);
|
|
282
350
|
|
|
283
351
|
try {
|
|
284
352
|
await dependencies.removePath(clonePath);
|
|
@@ -43,11 +43,23 @@ async function unlinkJsonlIfExists(filePath: string): Promise<void> {
|
|
|
43
43
|
*/
|
|
44
44
|
export async function deleteSessionJsonlFilesForProjectPath(projectPath: string): Promise<void> {
|
|
45
45
|
const sessions = sessionsDb.getSessionsByProjectPathIncludingArchived(projectPath);
|
|
46
|
-
|
|
46
|
+
// Including the transcripts a session has left behind: editing a message on
|
|
47
|
+
// a provider that rewinds by branching moves the session onto a copy, and
|
|
48
|
+
// the row stops pointing at the file the replaced turns are still in.
|
|
49
|
+
const paths = uniqueJsonlPathsFromSessions([
|
|
50
|
+
...sessions,
|
|
51
|
+
...sessions.flatMap((session) => sessionsDb
|
|
52
|
+
.getSupersededTranscriptPaths(session.session_id)
|
|
53
|
+
.map((jsonl_path) => ({ jsonl_path }))),
|
|
54
|
+
]);
|
|
47
55
|
|
|
48
56
|
for (const filePath of paths) {
|
|
49
57
|
await unlinkJsonlIfExists(filePath);
|
|
50
58
|
}
|
|
59
|
+
|
|
60
|
+
for (const session of sessions) {
|
|
61
|
+
sessionsDb.clearSupersededProviderSessions(session.session_id);
|
|
62
|
+
}
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
/**
|
|
@@ -40,7 +40,7 @@ export function applyLegacyStarredProjectIds(projectIds: string[]): ApplyLegacyS
|
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
if (
|
|
43
|
+
if (project.isStarred) {
|
|
44
44
|
continue;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -71,7 +71,7 @@ export function toggleProjectStar(projectId: string): ToggleProjectStarResult {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const nextStarredState = !
|
|
74
|
+
const nextStarredState = !project.isStarred;
|
|
75
75
|
projectsDb.updateProjectIsStarredById(normalizedProjectId, nextStarredState);
|
|
76
76
|
|
|
77
77
|
return { isStarred: nextStarredState };
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { once } from 'node:events';
|
|
3
|
+
import type { AddressInfo } from 'node:net';
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
|
|
6
|
+
import express, {
|
|
7
|
+
type NextFunction,
|
|
8
|
+
type Request,
|
|
9
|
+
type Response,
|
|
10
|
+
} from 'express';
|
|
11
|
+
|
|
12
|
+
import { createProjectCloneRouter } from '@/modules/projects/project-clone.routes.js';
|
|
13
|
+
import { createPendingCloneRequests } from '@/modules/projects/services/project-clone-request.service.js';
|
|
14
|
+
import type { startCloneProject } from '@/modules/projects/services/project-clone.service.js';
|
|
15
|
+
import { AppError } from '@/shared/utils.js';
|
|
16
|
+
|
|
17
|
+
type CloneRunner = typeof startCloneProject;
|
|
18
|
+
type CloneInput = Parameters<CloneRunner>[0];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A clone runner that reports one progress line and completes at once. What
|
|
22
|
+
* it was asked to clone is recorded so a test can check the request survived
|
|
23
|
+
* the POST -> claim -> run hand-off intact.
|
|
24
|
+
*/
|
|
25
|
+
function createFakeCloneRunner(inputs: CloneInput[]): CloneRunner {
|
|
26
|
+
return async (input, handlers) => {
|
|
27
|
+
inputs.push(input);
|
|
28
|
+
handlers.onProgress('Receiving objects: 100% (20/20), done.');
|
|
29
|
+
handlers.onComplete({ project: { projectId: 'project-1' }, message: 'Repository cloned successfully' });
|
|
30
|
+
return { waitForCompletion: Promise.resolve(), cancel: () => undefined };
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const unexpectedCloneRunner: CloneRunner = async () => {
|
|
35
|
+
throw new Error('startCloneProject must not run before the progress stream is opened');
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type ServerOptions = {
|
|
39
|
+
startCloneProject: CloneRunner;
|
|
40
|
+
pendingCloneTtlMs?: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// The auth middleware is mounted ahead of the router in production; here the
|
|
44
|
+
// `x-user-id` header plays its part so a test can act as two different users.
|
|
45
|
+
async function withProjectsServer(
|
|
46
|
+
options: ServerOptions,
|
|
47
|
+
run: (baseUrl: string) => Promise<void>,
|
|
48
|
+
): Promise<void> {
|
|
49
|
+
const app = express();
|
|
50
|
+
app.use(express.json());
|
|
51
|
+
app.use((req: Request, _res: Response, next: NextFunction) => {
|
|
52
|
+
const userId = req.header('x-user-id');
|
|
53
|
+
if (userId) {
|
|
54
|
+
(req as Request & { user?: { id: number } }).user = { id: Number(userId) };
|
|
55
|
+
}
|
|
56
|
+
next();
|
|
57
|
+
});
|
|
58
|
+
app.use('/api/projects', createProjectCloneRouter({
|
|
59
|
+
startCloneProject: options.startCloneProject,
|
|
60
|
+
pendingCloneRequests: createPendingCloneRequests(options.pendingCloneTtlMs ?? 60_000),
|
|
61
|
+
}));
|
|
62
|
+
app.use((error: unknown, _req: Request, res: Response, _next: NextFunction) => {
|
|
63
|
+
if (error instanceof AppError) {
|
|
64
|
+
res.status(error.statusCode).json({ error: error.code });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
res.status(500).json({ error: 'INTERNAL_ERROR' });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const server = app.listen(0, '127.0.0.1');
|
|
72
|
+
await once(server, 'listening');
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const address = server.address() as AddressInfo;
|
|
76
|
+
await run(`http://127.0.0.1:${address.port}`);
|
|
77
|
+
} finally {
|
|
78
|
+
await new Promise<void>((resolve, reject) => {
|
|
79
|
+
server.close((error) => error ? reject(error) : resolve());
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const cloneBody = {
|
|
85
|
+
path: '/workspace/root',
|
|
86
|
+
githubUrl: 'https://github.com/example/repo.git',
|
|
87
|
+
githubTokenId: null,
|
|
88
|
+
newGithubToken: 'ghp_supersecrettoken1234567890abcd',
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
async function postClone(baseUrl: string, userId: number, body: Record<string, unknown> = cloneBody) {
|
|
92
|
+
const response = await fetch(`${baseUrl}/api/projects/clone`, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: { 'content-type': 'application/json', 'x-user-id': String(userId) },
|
|
95
|
+
body: JSON.stringify(body),
|
|
96
|
+
});
|
|
97
|
+
return { status: response.status, body: await response.json() as { cloneId?: string; error?: string } };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function openProgress(baseUrl: string, userId: number, cloneId: string) {
|
|
101
|
+
const response = await fetch(
|
|
102
|
+
`${baseUrl}/api/projects/clone-progress?cloneId=${encodeURIComponent(cloneId)}`,
|
|
103
|
+
{ headers: { 'x-user-id': String(userId) } },
|
|
104
|
+
);
|
|
105
|
+
const text = await response.text();
|
|
106
|
+
const events = text
|
|
107
|
+
.split('\n\n')
|
|
108
|
+
.filter((frame) => frame.startsWith('data: '))
|
|
109
|
+
.map((frame) => JSON.parse(frame.slice('data: '.length)) as { type: string; message?: string });
|
|
110
|
+
return { status: response.status, contentType: response.headers.get('content-type') ?? '', text, events };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
test('POST /clone parks the request under a fresh id without spawning anything', async () => {
|
|
114
|
+
await withProjectsServer({ startCloneProject: unexpectedCloneRunner }, async (baseUrl) => {
|
|
115
|
+
const first = await postClone(baseUrl, 1);
|
|
116
|
+
const second = await postClone(baseUrl, 1);
|
|
117
|
+
|
|
118
|
+
assert.equal(first.status, 200);
|
|
119
|
+
assert.match(first.body.cloneId ?? '', /^[0-9a-f-]{36}$/);
|
|
120
|
+
assert.notEqual(first.body.cloneId, second.body.cloneId);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('POST /clone requires an authenticated user', async () => {
|
|
125
|
+
await withProjectsServer({ startCloneProject: unexpectedCloneRunner }, async (baseUrl) => {
|
|
126
|
+
const response = await fetch(`${baseUrl}/api/projects/clone`, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: { 'content-type': 'application/json' },
|
|
129
|
+
body: JSON.stringify(cloneBody),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
assert.equal(response.status, 401);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('GET /clone-progress streams the clone the id names, then retires the id', async () => {
|
|
137
|
+
const inputs: CloneInput[] = [];
|
|
138
|
+
await withProjectsServer({ startCloneProject: createFakeCloneRunner(inputs) }, async (baseUrl) => {
|
|
139
|
+
const { body } = await postClone(baseUrl, 7, { ...cloneBody, githubTokenId: 12, newGithubToken: null });
|
|
140
|
+
assert.equal(inputs.length, 0, 'the POST alone must not start a clone');
|
|
141
|
+
|
|
142
|
+
const stream = await openProgress(baseUrl, 7, body.cloneId ?? '');
|
|
143
|
+
|
|
144
|
+
assert.equal(stream.status, 200);
|
|
145
|
+
assert.match(stream.contentType, /^text\/event-stream/);
|
|
146
|
+
assert.deepEqual(stream.events.map((event) => event.type), ['progress', 'complete']);
|
|
147
|
+
assert.equal(stream.events[0]?.message, 'Receiving objects: 100% (20/20), done.');
|
|
148
|
+
assert.deepEqual(inputs, [{
|
|
149
|
+
workspacePath: '/workspace/root',
|
|
150
|
+
githubUrl: 'https://github.com/example/repo.git',
|
|
151
|
+
githubTokenId: 12,
|
|
152
|
+
newGithubToken: null,
|
|
153
|
+
userId: 7,
|
|
154
|
+
}]);
|
|
155
|
+
|
|
156
|
+
const replay = await openProgress(baseUrl, 7, body.cloneId ?? '');
|
|
157
|
+
assert.equal(replay.status, 404, 'a claimed id must not run the clone twice');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('GET /clone-progress hands the posted token to the clone without it touching the URL', async () => {
|
|
162
|
+
const inputs: CloneInput[] = [];
|
|
163
|
+
await withProjectsServer({ startCloneProject: createFakeCloneRunner(inputs) }, async (baseUrl) => {
|
|
164
|
+
const { body } = await postClone(baseUrl, 1);
|
|
165
|
+
const stream = await openProgress(baseUrl, 1, body.cloneId ?? '');
|
|
166
|
+
|
|
167
|
+
assert.equal(stream.status, 200);
|
|
168
|
+
assert.equal(inputs[0]?.newGithubToken, cloneBody.newGithubToken);
|
|
169
|
+
assert.ok(!(body.cloneId ?? '').includes(cloneBody.newGithubToken));
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('GET /clone-progress rejects an unknown id with 404', async () => {
|
|
174
|
+
await withProjectsServer({ startCloneProject: unexpectedCloneRunner }, async (baseUrl) => {
|
|
175
|
+
const stream = await openProgress(baseUrl, 1, 'not-a-clone-id');
|
|
176
|
+
|
|
177
|
+
assert.equal(stream.status, 404);
|
|
178
|
+
assert.deepEqual(JSON.parse(stream.text), { error: 'CLONE_REQUEST_NOT_FOUND' });
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('GET /clone-progress rejects another user id with 404 and keeps it for its owner', async () => {
|
|
183
|
+
const inputs: CloneInput[] = [];
|
|
184
|
+
await withProjectsServer({ startCloneProject: createFakeCloneRunner(inputs) }, async (baseUrl) => {
|
|
185
|
+
const { body } = await postClone(baseUrl, 1);
|
|
186
|
+
|
|
187
|
+
const foreign = await openProgress(baseUrl, 2, body.cloneId ?? '');
|
|
188
|
+
assert.equal(foreign.status, 404);
|
|
189
|
+
assert.equal(inputs.length, 0, 'a foreign claim must not run the clone');
|
|
190
|
+
|
|
191
|
+
const owner = await openProgress(baseUrl, 1, body.cloneId ?? '');
|
|
192
|
+
assert.equal(owner.status, 200);
|
|
193
|
+
assert.equal(inputs[0]?.userId, 1);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('GET /clone-progress rejects an expired id with 404', async () => {
|
|
198
|
+
await withProjectsServer(
|
|
199
|
+
{ startCloneProject: unexpectedCloneRunner, pendingCloneTtlMs: 20 },
|
|
200
|
+
async (baseUrl) => {
|
|
201
|
+
const { body } = await postClone(baseUrl, 1);
|
|
202
|
+
await new Promise((resolve) => setTimeout(resolve, 60));
|
|
203
|
+
|
|
204
|
+
const stream = await openProgress(baseUrl, 1, body.cloneId ?? '');
|
|
205
|
+
assert.equal(stream.status, 404);
|
|
206
|
+
},
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('GET /clone-progress cancels a clone whose client left before the git process existed', async () => {
|
|
211
|
+
// The close listener could only cancel an operation it already had; a
|
|
212
|
+
// client gone during validation left git cloning for nobody, under an id
|
|
213
|
+
// no reconnect could reach.
|
|
214
|
+
let releaseStart: () => void = () => undefined;
|
|
215
|
+
const started = new Promise<void>((resolve) => { releaseStart = resolve; });
|
|
216
|
+
let cancelled = 0;
|
|
217
|
+
let resolveRun: () => void = () => undefined;
|
|
218
|
+
const runFinished = new Promise<void>((resolve) => { resolveRun = resolve; });
|
|
219
|
+
const slowRunner: CloneRunner = async () => {
|
|
220
|
+
await started;
|
|
221
|
+
return {
|
|
222
|
+
waitForCompletion: new Promise<void>((resolve) => { setTimeout(resolve, 20); }).then(() => resolveRun()),
|
|
223
|
+
cancel: () => { cancelled += 1; },
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
await withProjectsServer({ startCloneProject: slowRunner }, async (baseUrl) => {
|
|
228
|
+
const { body } = await postClone(baseUrl, 1);
|
|
229
|
+
const controller = new AbortController();
|
|
230
|
+
const request = fetch(
|
|
231
|
+
`${baseUrl}/api/projects/clone-progress?cloneId=${encodeURIComponent(body.cloneId ?? '')}`,
|
|
232
|
+
{ headers: { 'x-user-id': '1' }, signal: controller.signal },
|
|
233
|
+
);
|
|
234
|
+
// Headers are flushed before the runner is awaited; drop the client
|
|
235
|
+
// while the runner is still "validating".
|
|
236
|
+
await new Promise((resolve) => { setTimeout(resolve, 50); });
|
|
237
|
+
controller.abort();
|
|
238
|
+
await request.catch(() => undefined);
|
|
239
|
+
await new Promise((resolve) => { setTimeout(resolve, 50); });
|
|
240
|
+
assert.equal(cancelled, 0, 'nothing to cancel yet');
|
|
241
|
+
|
|
242
|
+
releaseStart();
|
|
243
|
+
await runFinished;
|
|
244
|
+
assert.equal(cancelled, 1, 'the operation is cancelled as soon as it exists');
|
|
245
|
+
});
|
|
246
|
+
});
|