@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
|
@@ -6,7 +6,7 @@ import test from 'node:test';
|
|
|
6
6
|
|
|
7
7
|
import { closeConnection, initializeDatabase, sessionsDb } from '@/modules/database/index.js';
|
|
8
8
|
import { CodexSessionSynchronizer } from '@/modules/providers/list/codex/codex-session-synchronizer.provider.js';
|
|
9
|
-
import { CodexSessionsProvider } from '@/modules/providers/list/codex/codex-sessions.provider.js';
|
|
9
|
+
import { CodexSessionsProvider, parseCodexExecScript, readCodexMemoryCitations } from '@/modules/providers/list/codex/codex-sessions.provider.js';
|
|
10
10
|
|
|
11
11
|
const patchHomeDir = (nextHomeDir: string) => {
|
|
12
12
|
const original = os.homedir;
|
|
@@ -152,7 +152,7 @@ test('Codex synchronizer leaves indexed sessions untitled when no name is availa
|
|
|
152
152
|
}
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
-
test('Codex history
|
|
155
|
+
test('Codex history translates wrapped exec scripts into the tools they ran', { concurrency: false }, async () => {
|
|
156
156
|
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-exec-history-'));
|
|
157
157
|
const workspacePath = path.join(tempRoot, 'workspace');
|
|
158
158
|
await mkdir(workspacePath, { recursive: true });
|
|
@@ -161,6 +161,8 @@ test('Codex history preserves wrapped exec tool calls and results', { concurrenc
|
|
|
161
161
|
try {
|
|
162
162
|
const providerSessionId = 'codex-exec-1';
|
|
163
163
|
const transcriptPath = await writeCodexTranscript(tempRoot, providerSessionId, workspacePath);
|
|
164
|
+
// Every shape Codex's "code mode" emits. Only a wrapper nothing recognizes
|
|
165
|
+
// may stay labelled `exec`; the rest must name the work they performed.
|
|
164
166
|
const wrappedCalls = [
|
|
165
167
|
{
|
|
166
168
|
callId: 'shell-command-1',
|
|
@@ -176,14 +178,22 @@ test('Codex history preserves wrapped exec tool calls and results', { concurrenc
|
|
|
176
178
|
},
|
|
177
179
|
{
|
|
178
180
|
callId: 'exec-command-1',
|
|
179
|
-
input: 'await tools.exec_command({"
|
|
181
|
+
input: 'await tools.exec_command({"cmd":"npm test","workdir":"/repo","yield_time_ms":10000});',
|
|
180
182
|
expectedToolName: 'Bash',
|
|
181
|
-
expectedToolInput: JSON.stringify({ command: '
|
|
183
|
+
expectedToolInput: JSON.stringify({ command: 'npm test' }),
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
callId: 'web-run-1',
|
|
187
|
+
input: 'await tools.web__run({ search_query: [{ q: "Codex" }, { q: "rollout format" }] });',
|
|
188
|
+
expectedToolName: 'WebSearch',
|
|
189
|
+
expectedToolInput: JSON.stringify({ query: 'Codex | rollout format' }),
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
callId: 'unknown-1',
|
|
193
|
+
input: 'await tools.unknown_wrapper({ value: true });',
|
|
194
|
+
expectedToolName: 'exec',
|
|
195
|
+
expectedToolInput: 'await tools.unknown_wrapper({ value: true });',
|
|
182
196
|
},
|
|
183
|
-
{ callId: 'apply-patch-1', input: 'await tools.apply_patch("*** Begin Patch\\n*** End Patch");' },
|
|
184
|
-
{ callId: 'web-run-1', input: 'await tools.web__run({ search_query: [{ q: "Codex" }] });' },
|
|
185
|
-
{ callId: 'update-plan-1', input: 'await tools.update_plan({ plan: [] });' },
|
|
186
|
-
{ callId: 'unknown-1', input: 'await tools.unknown_wrapper({ value: true });' },
|
|
187
197
|
];
|
|
188
198
|
const transcriptLines = [
|
|
189
199
|
JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
|
|
@@ -209,19 +219,15 @@ test('Codex history preserves wrapped exec tool calls and results', { concurrenc
|
|
|
209
219
|
|
|
210
220
|
const history = await new CodexSessionsProvider().fetchHistory('app-exec-1');
|
|
211
221
|
const toolUses = history.messages.filter((message) => message.kind === 'tool_use');
|
|
212
|
-
const toolResults = history.messages.filter((message) => message.kind === 'tool_result');
|
|
213
222
|
const toolUsesById = new Map(toolUses.map((message) => [message.toolId, message]));
|
|
214
|
-
const toolResultsById = new Map(toolResults.map((message) => [message.toolId, message]));
|
|
215
223
|
|
|
216
224
|
assert.equal(toolUses.length, wrappedCalls.length);
|
|
217
|
-
assert.equal(toolResults.length, wrappedCalls.length);
|
|
218
225
|
for (const call of wrappedCalls) {
|
|
219
226
|
const toolUse = toolUsesById.get(call.callId);
|
|
220
|
-
assert.ok(toolUse);
|
|
221
|
-
assert.equal(toolUse.toolName, call.expectedToolName
|
|
222
|
-
assert.equal(toolUse.toolInput, call.expectedToolInput
|
|
227
|
+
assert.ok(toolUse, `missing row for ${call.callId}`);
|
|
228
|
+
assert.equal(toolUse.toolName, call.expectedToolName);
|
|
229
|
+
assert.equal(toolUse.toolInput, call.expectedToolInput);
|
|
223
230
|
assert.equal(toolUse.toolResult?.content, `result:${call.callId}`);
|
|
224
|
-
assert.equal(toolResultsById.get(call.callId)?.content, `result:${call.callId}`);
|
|
225
231
|
}
|
|
226
232
|
});
|
|
227
233
|
} finally {
|
|
@@ -229,3 +235,378 @@ test('Codex history preserves wrapped exec tool calls and results', { concurrenc
|
|
|
229
235
|
await rm(tempRoot, { recursive: true, force: true });
|
|
230
236
|
}
|
|
231
237
|
});
|
|
238
|
+
|
|
239
|
+
test('Codex history strips the sandbox envelopes from shell output', { concurrency: false }, async () => {
|
|
240
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-exec-output-'));
|
|
241
|
+
const workspacePath = path.join(tempRoot, 'workspace');
|
|
242
|
+
await mkdir(workspacePath, { recursive: true });
|
|
243
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const providerSessionId = 'codex-output-1';
|
|
247
|
+
const transcriptPath = await writeCodexTranscript(tempRoot, providerSessionId, workspacePath);
|
|
248
|
+
await writeFile(transcriptPath, `${[
|
|
249
|
+
JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
|
|
250
|
+
JSON.stringify({
|
|
251
|
+
type: 'response_item',
|
|
252
|
+
payload: {
|
|
253
|
+
type: 'custom_tool_call',
|
|
254
|
+
name: 'exec',
|
|
255
|
+
call_id: 'failing-1',
|
|
256
|
+
input: 'await tools.shell_command({ command: "npm test" });',
|
|
257
|
+
},
|
|
258
|
+
}),
|
|
259
|
+
// Codex nests two report headers: one for the sandbox script and one for
|
|
260
|
+
// the command it ran. Neither belongs in a chat transcript.
|
|
261
|
+
JSON.stringify({
|
|
262
|
+
type: 'response_item',
|
|
263
|
+
payload: {
|
|
264
|
+
type: 'custom_tool_call_output',
|
|
265
|
+
call_id: 'failing-1',
|
|
266
|
+
output: [
|
|
267
|
+
{ type: 'input_text', text: 'Script failed\nWall time 1.2 seconds\nOutput:\n' },
|
|
268
|
+
{ type: 'input_text', text: 'Script error:\nExit code: 1\nWall time: 1.1 seconds\nOutput:\n1 test failed\n' },
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
}),
|
|
272
|
+
].join('\n')}\n`, 'utf8');
|
|
273
|
+
|
|
274
|
+
await withIsolatedDatabase(async () => {
|
|
275
|
+
sessionsDb.createAppSession('app-output-1', 'codex', workspacePath);
|
|
276
|
+
sessionsDb.assignProviderSessionId('app-output-1', providerSessionId);
|
|
277
|
+
await new CodexSessionSynchronizer().synchronize();
|
|
278
|
+
|
|
279
|
+
const history = await new CodexSessionsProvider().fetchHistory('app-output-1');
|
|
280
|
+
const bash = history.messages.find((message) => message.kind === 'tool_use');
|
|
281
|
+
|
|
282
|
+
assert.equal(bash?.toolName, 'Bash');
|
|
283
|
+
assert.equal(bash?.toolResult?.content, '1 test failed\n');
|
|
284
|
+
assert.equal(bash?.toolResult?.isError, true, 'a non-zero exit code must mark the row failed');
|
|
285
|
+
});
|
|
286
|
+
} finally {
|
|
287
|
+
restoreHomeDir();
|
|
288
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test('Codex history renders one file row per patched file, from the applied diff', { concurrency: false }, async () => {
|
|
293
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-patch-history-'));
|
|
294
|
+
const workspacePath = path.join(tempRoot, 'workspace');
|
|
295
|
+
await mkdir(workspacePath, { recursive: true });
|
|
296
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
const providerSessionId = 'codex-patch-1';
|
|
300
|
+
const transcriptPath = await writeCodexTranscript(tempRoot, providerSessionId, workspacePath);
|
|
301
|
+
const patch = [
|
|
302
|
+
'*** Begin Patch',
|
|
303
|
+
'*** Update File: /repo/a.ts',
|
|
304
|
+
'@@',
|
|
305
|
+
'-const a = 1;',
|
|
306
|
+
'+const a = 2;',
|
|
307
|
+
'*** Update File: /repo/b.ts',
|
|
308
|
+
'@@',
|
|
309
|
+
'-const b = 1;',
|
|
310
|
+
'+const b = 2;',
|
|
311
|
+
'*** End Patch',
|
|
312
|
+
].join('\n');
|
|
313
|
+
|
|
314
|
+
await writeFile(transcriptPath, `${[
|
|
315
|
+
JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
|
|
316
|
+
JSON.stringify({
|
|
317
|
+
type: 'response_item',
|
|
318
|
+
payload: { type: 'custom_tool_call', name: 'apply_patch', call_id: 'patch-1', input: patch },
|
|
319
|
+
}),
|
|
320
|
+
// The out-of-band report carries the real diffs, keyed by path in an
|
|
321
|
+
// order that does not follow the patch body.
|
|
322
|
+
JSON.stringify({
|
|
323
|
+
type: 'event_msg',
|
|
324
|
+
payload: {
|
|
325
|
+
type: 'patch_apply_end',
|
|
326
|
+
call_id: 'patch-1',
|
|
327
|
+
success: true,
|
|
328
|
+
changes: {
|
|
329
|
+
'/repo/b.ts': { type: 'update', unified_diff: '@@\n-const b = 1;\n+const b = 22;\n' },
|
|
330
|
+
'/repo/a.ts': { type: 'update', unified_diff: '@@\n-const a = 1;\n+const a = 22;\n' },
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
}),
|
|
334
|
+
JSON.stringify({
|
|
335
|
+
type: 'response_item',
|
|
336
|
+
payload: { type: 'custom_tool_call_output', call_id: 'patch-1', output: '{}' },
|
|
337
|
+
}),
|
|
338
|
+
].join('\n')}\n`, 'utf8');
|
|
339
|
+
|
|
340
|
+
await withIsolatedDatabase(async () => {
|
|
341
|
+
sessionsDb.createAppSession('app-patch-1', 'codex', workspacePath);
|
|
342
|
+
sessionsDb.assignProviderSessionId('app-patch-1', providerSessionId);
|
|
343
|
+
await new CodexSessionSynchronizer().synchronize();
|
|
344
|
+
|
|
345
|
+
const history = await new CodexSessionsProvider().fetchHistory('app-patch-1');
|
|
346
|
+
const edits = history.messages.filter((message) => message.kind === 'tool_use');
|
|
347
|
+
|
|
348
|
+
assert.equal(edits.length, 2, 'the patch must not be rendered twice');
|
|
349
|
+
const byPath = new Map(edits.map((edit) => {
|
|
350
|
+
const input = JSON.parse(String(edit.toolInput)) as { file_path: string; new_string: string };
|
|
351
|
+
return [input.file_path, { edit, input }];
|
|
352
|
+
}));
|
|
353
|
+
|
|
354
|
+
assert.equal(byPath.get('/repo/a.ts')?.edit.toolName, 'Edit');
|
|
355
|
+
// The applied diff wins over the one reconstructed from the call input,
|
|
356
|
+
// and it must land on the row for its own file.
|
|
357
|
+
assert.equal(byPath.get('/repo/a.ts')?.input.new_string, 'const a = 22;');
|
|
358
|
+
assert.equal(byPath.get('/repo/b.ts')?.input.new_string, 'const b = 22;');
|
|
359
|
+
for (const edit of edits) {
|
|
360
|
+
assert.equal(edit.toolResult?.isError, false, 'a successful patch must resolve its row');
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
} finally {
|
|
364
|
+
restoreHomeDir();
|
|
365
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test('Codex history attaches a spawned agent\'s own transcript to the Task row', { concurrency: false }, async () => {
|
|
370
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-subagent-history-'));
|
|
371
|
+
const workspacePath = path.join(tempRoot, 'workspace');
|
|
372
|
+
await mkdir(workspacePath, { recursive: true });
|
|
373
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
const providerSessionId = 'codex-parent-2';
|
|
377
|
+
const agentThreadId = 'codex-agent-thread-2';
|
|
378
|
+
const transcriptPath = await writeCodexTranscript(tempRoot, providerSessionId, workspacePath);
|
|
379
|
+
|
|
380
|
+
await writeFile(transcriptPath, `${[
|
|
381
|
+
JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
|
|
382
|
+
JSON.stringify({
|
|
383
|
+
type: 'response_item',
|
|
384
|
+
payload: {
|
|
385
|
+
type: 'function_call',
|
|
386
|
+
name: 'spawn_agent',
|
|
387
|
+
call_id: 'spawn-1',
|
|
388
|
+
arguments: JSON.stringify({ task_name: 'test_agent_1' }),
|
|
389
|
+
},
|
|
390
|
+
}),
|
|
391
|
+
JSON.stringify({
|
|
392
|
+
type: 'event_msg',
|
|
393
|
+
payload: {
|
|
394
|
+
type: 'sub_agent_activity',
|
|
395
|
+
kind: 'started',
|
|
396
|
+
event_id: 'spawn-1',
|
|
397
|
+
agent_thread_id: agentThreadId,
|
|
398
|
+
agent_path: '/root/test_agent_1',
|
|
399
|
+
},
|
|
400
|
+
}),
|
|
401
|
+
JSON.stringify({
|
|
402
|
+
type: 'response_item',
|
|
403
|
+
payload: {
|
|
404
|
+
type: 'agent_message',
|
|
405
|
+
author: '/root/test_agent_1',
|
|
406
|
+
content: [{ type: 'input_text', text: 'Message Type: FINAL_ANSWER\nSender: /root/test_agent_1\nPayload:\nAll done.' }],
|
|
407
|
+
},
|
|
408
|
+
}),
|
|
409
|
+
].join('\n')}\n`, 'utf8');
|
|
410
|
+
|
|
411
|
+
// Codex writes a spawned agent's rollout next to its parent's, named after
|
|
412
|
+
// the agent thread id.
|
|
413
|
+
await writeFile(
|
|
414
|
+
path.join(path.dirname(transcriptPath), `rollout-2026-07-07T00-00-00-${agentThreadId}.jsonl`),
|
|
415
|
+
`${[
|
|
416
|
+
JSON.stringify({
|
|
417
|
+
type: 'session_meta',
|
|
418
|
+
payload: { id: agentThreadId, cwd: workspacePath, thread_source: 'subagent', agent_nickname: 'Hegel' },
|
|
419
|
+
}),
|
|
420
|
+
JSON.stringify({
|
|
421
|
+
type: 'response_item',
|
|
422
|
+
payload: {
|
|
423
|
+
type: 'custom_tool_call',
|
|
424
|
+
name: 'exec',
|
|
425
|
+
call_id: 'agent-call-1',
|
|
426
|
+
input: 'await tools.shell_command({ command: "ls" });',
|
|
427
|
+
},
|
|
428
|
+
}),
|
|
429
|
+
JSON.stringify({
|
|
430
|
+
type: 'response_item',
|
|
431
|
+
payload: { type: 'custom_tool_call_output', call_id: 'agent-call-1', output: 'Exit code: 0\nOutput:\nREADME.md\n' },
|
|
432
|
+
}),
|
|
433
|
+
].join('\n')}\n`,
|
|
434
|
+
'utf8',
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
await withIsolatedDatabase(async () => {
|
|
438
|
+
sessionsDb.createAppSession('app-parent-2', 'codex', workspacePath);
|
|
439
|
+
sessionsDb.assignProviderSessionId('app-parent-2', providerSessionId);
|
|
440
|
+
await new CodexSessionSynchronizer().synchronize();
|
|
441
|
+
|
|
442
|
+
const history = await new CodexSessionsProvider().fetchHistory('app-parent-2');
|
|
443
|
+
const task = history.messages.find((message) => message.kind === 'tool_use' && message.toolName === 'Task');
|
|
444
|
+
|
|
445
|
+
assert.ok(task, 'a spawned agent must produce a Task row');
|
|
446
|
+
assert.equal(task.subagent?.id, agentThreadId);
|
|
447
|
+
assert.equal(task.subagent?.name, 'Hegel');
|
|
448
|
+
assert.equal(task.subagent?.status, 'completed');
|
|
449
|
+
assert.equal(task.toolResult?.content, 'All done.');
|
|
450
|
+
|
|
451
|
+
// The agent's own shell call is translated exactly like the parent's.
|
|
452
|
+
assert.equal(task.subagentTools?.length, 1);
|
|
453
|
+
assert.equal(task.subagentTools?.[0].kind, 'tool');
|
|
454
|
+
assert.equal(task.subagentTools?.[0].toolName, 'Bash');
|
|
455
|
+
assert.equal(task.subagentTools?.[0].toolResult?.content, 'README.md\n');
|
|
456
|
+
});
|
|
457
|
+
} finally {
|
|
458
|
+
restoreHomeDir();
|
|
459
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
test('Codex memory citations are lifted out of the reply they trail', () => {
|
|
464
|
+
const reply = [
|
|
465
|
+
'Here is the answer.',
|
|
466
|
+
'',
|
|
467
|
+
'<oai-mem-citation>',
|
|
468
|
+
'<citation_entries>',
|
|
469
|
+
'MEMORY.md:137-142|note=[used verified container provisioning details]',
|
|
470
|
+
'notes/deploy.md:4-9',
|
|
471
|
+
'</citation_entries>',
|
|
472
|
+
'<rollout_ids>',
|
|
473
|
+
'019eda6d-c2f7-70c1-8b42-bf03c44a1f35',
|
|
474
|
+
'</rollout_ids>',
|
|
475
|
+
'</oai-mem-citation>',
|
|
476
|
+
].join('\n');
|
|
477
|
+
|
|
478
|
+
const { text, memoryCitations } = readCodexMemoryCitations(reply);
|
|
479
|
+
|
|
480
|
+
assert.equal(text, 'Here is the answer.');
|
|
481
|
+
assert.deepEqual(memoryCitations, [
|
|
482
|
+
{ source: 'MEMORY.md:137-142', note: 'used verified container provisioning details' },
|
|
483
|
+
{ source: 'notes/deploy.md:4-9' },
|
|
484
|
+
]);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test('a plan followed by a memory citation is still recognized as a plan', async () => {
|
|
488
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-plan-citation-'));
|
|
489
|
+
const workspacePath = path.join(tempRoot, 'workspace');
|
|
490
|
+
await mkdir(workspacePath, { recursive: true });
|
|
491
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
const providerSessionId = 'codex-plan-citation-1';
|
|
495
|
+
const sessionsDir = path.join(tempRoot, '.codex', 'sessions', '2026', '07', '07');
|
|
496
|
+
await mkdir(sessionsDir, { recursive: true });
|
|
497
|
+
await writeFile(path.join(sessionsDir, `rollout-${providerSessionId}.jsonl`), `${[
|
|
498
|
+
JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
|
|
499
|
+
JSON.stringify({
|
|
500
|
+
type: 'response_item',
|
|
501
|
+
payload: {
|
|
502
|
+
type: 'message',
|
|
503
|
+
role: 'assistant',
|
|
504
|
+
id: 'plan-1',
|
|
505
|
+
content: [{
|
|
506
|
+
type: 'output_text',
|
|
507
|
+
text: '<proposed_plan>\n# Ship it\n</proposed_plan>\n\n<oai-mem-citation>\n<citation_entries>\nMEMORY.md:1-2|note=[prior deploy steps]\n</citation_entries>\n</oai-mem-citation>',
|
|
508
|
+
}],
|
|
509
|
+
},
|
|
510
|
+
}),
|
|
511
|
+
].join('\n')}\n`, 'utf8');
|
|
512
|
+
|
|
513
|
+
await withIsolatedDatabase(async () => {
|
|
514
|
+
sessionsDb.createAppSession('app-plan-citation-1', 'codex', workspacePath);
|
|
515
|
+
sessionsDb.assignProviderSessionId('app-plan-citation-1', providerSessionId);
|
|
516
|
+
await new CodexSessionSynchronizer().synchronize();
|
|
517
|
+
|
|
518
|
+
const history = await new CodexSessionsProvider().fetchHistory('app-plan-citation-1');
|
|
519
|
+
const plan = history.messages.find((message) => message.toolName === 'ExitPlanMode');
|
|
520
|
+
|
|
521
|
+
assert.ok(plan, 'the trailing citation block must not hide the plan envelope');
|
|
522
|
+
assert.equal(JSON.parse(String(plan.toolInput)).plan, '# Ship it');
|
|
523
|
+
assert.deepEqual(plan.memoryCitations, [{ source: 'MEMORY.md:1-2', note: 'prior deploy steps' }]);
|
|
524
|
+
assert.ok(
|
|
525
|
+
!history.messages.some((message) => JSON.stringify(message).includes('oai-mem-citation')),
|
|
526
|
+
'no transcript row may still carry the raw citation markup',
|
|
527
|
+
);
|
|
528
|
+
});
|
|
529
|
+
} finally {
|
|
530
|
+
restoreHomeDir();
|
|
531
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test('an exec script that updates the plan yields the steps it set', () => {
|
|
536
|
+
const operations = parseCodexExecScript([
|
|
537
|
+
'const p = await tools.update_plan({plan:[',
|
|
538
|
+
' {step:"Confirm the current workspace",status:"completed"},',
|
|
539
|
+
' {step:"Check the Git working-tree status",status:"in_progress"},',
|
|
540
|
+
' {step:"Identify the project",status:"pending"}',
|
|
541
|
+
']});',
|
|
542
|
+
'text(p);',
|
|
543
|
+
].join('\n'));
|
|
544
|
+
|
|
545
|
+
assert.deepEqual(operations, [{
|
|
546
|
+
kind: 'plan',
|
|
547
|
+
todos: [
|
|
548
|
+
{ content: 'Confirm the current workspace', status: 'completed' },
|
|
549
|
+
{ content: 'Check the Git working-tree status', status: 'in_progress' },
|
|
550
|
+
{ content: 'Identify the project', status: 'pending' },
|
|
551
|
+
],
|
|
552
|
+
}]);
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
test('Codex history restores user prompts from typed item_completed rows', { concurrency: false }, async () => {
|
|
557
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-typed-user-history-'));
|
|
558
|
+
const workspacePath = path.join(tempRoot, 'workspace');
|
|
559
|
+
await mkdir(workspacePath, { recursive: true });
|
|
560
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
561
|
+
|
|
562
|
+
try {
|
|
563
|
+
const providerSessionId = 'codex-typed-1';
|
|
564
|
+
const prompt = 'Typed prompt that must survive a refresh';
|
|
565
|
+
const transcriptPath = path.join(tempRoot, '.codex', 'sessions', '2026', '07', '07', 'rollout-' + providerSessionId + '.jsonl');
|
|
566
|
+
await mkdir(path.dirname(transcriptPath), { recursive: true });
|
|
567
|
+
const lineEnd = String.fromCharCode(10);
|
|
568
|
+
const typedLine = JSON.stringify({
|
|
569
|
+
timestamp: '2026-09-07T17:59:17.000Z',
|
|
570
|
+
type: 'event_msg',
|
|
571
|
+
payload: {
|
|
572
|
+
type: 'item_completed',
|
|
573
|
+
turn_id: 'typed-turn-1',
|
|
574
|
+
item: {
|
|
575
|
+
type: 'UserMessage',
|
|
576
|
+
id: 'item-user-1',
|
|
577
|
+
content: [
|
|
578
|
+
{ type: 'text', text: prompt, text_elements: [] },
|
|
579
|
+
{ type: 'local_image', path: '/tmp/prompt-picture.png', detail: null },
|
|
580
|
+
],
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
});
|
|
584
|
+
const wireUserEcho = JSON.stringify({
|
|
585
|
+
timestamp: '2026-09-07T17:59:19.000Z',
|
|
586
|
+
type: 'response_item',
|
|
587
|
+
payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: prompt }] },
|
|
588
|
+
});
|
|
589
|
+
await writeFile(
|
|
590
|
+
transcriptPath,
|
|
591
|
+
[JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }), typedLine, wireUserEcho].join(lineEnd) + lineEnd,
|
|
592
|
+
'utf8',
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
await withIsolatedDatabase(async () => {
|
|
596
|
+
sessionsDb.createAppSession('app-typed-1', 'codex', workspacePath);
|
|
597
|
+
sessionsDb.assignProviderSessionId('app-typed-1', providerSessionId);
|
|
598
|
+
await new CodexSessionSynchronizer().synchronize();
|
|
599
|
+
|
|
600
|
+
const history = await new CodexSessionsProvider().fetchHistory('app-typed-1');
|
|
601
|
+
const users = history.messages.filter((message) => message.role === 'user');
|
|
602
|
+
const images = users[0]?.images as Array<{ path?: string; data?: string }> | undefined;
|
|
603
|
+
|
|
604
|
+
assert.equal(users.length, 1, 'the wire user echo must not duplicate the typed prompt');
|
|
605
|
+
assert.equal(users[0]?.content, prompt);
|
|
606
|
+
assert.equal(images?.[0]?.path, '/tmp/prompt-picture.png');
|
|
607
|
+
});
|
|
608
|
+
} finally {
|
|
609
|
+
restoreHomeDir();
|
|
610
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
611
|
+
}
|
|
612
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import test, { mock } from 'node:test';
|
|
7
|
+
|
|
8
|
+
import { CursorProviderAuth } from '@/modules/providers/list/cursor/cursor-auth.provider.js';
|
|
9
|
+
|
|
10
|
+
const findEnvKey = (name: string) =>
|
|
11
|
+
Object.keys(process.env).find((key) => key.toLowerCase() === name.toLowerCase()) || name;
|
|
12
|
+
|
|
13
|
+
// What the real CLI prints when it is logged in and the account request succeeded.
|
|
14
|
+
const LOGGED_IN_OUTPUT = '✓ Logged in as cursor-user@example.com';
|
|
15
|
+
|
|
16
|
+
// A stand-in for the real CLI: `--version` answers at once, while `status` drops a started
|
|
17
|
+
// marker and keeps running (like the real one does while it fetches the account details from
|
|
18
|
+
// Cursor's API) until the test creates the release file, then prints the configured output.
|
|
19
|
+
// It records a SIGTERM so the tests can check that a timed-out check kills it.
|
|
20
|
+
const FAKE_CURSOR_AGENT_SCRIPT = `
|
|
21
|
+
const fs = require('node:fs');
|
|
22
|
+
const path = require('node:path');
|
|
23
|
+
const command = process.argv[2];
|
|
24
|
+
const markerDir = process.env.CURSOR_FAKE_MARKER_DIR;
|
|
25
|
+
if (command === '--version') {
|
|
26
|
+
console.log('2026.07.20-test');
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
if (command === 'status') {
|
|
30
|
+
process.on('SIGTERM', () => {
|
|
31
|
+
fs.writeFileSync(path.join(markerDir, 'status-killed'), '');
|
|
32
|
+
process.exit(143);
|
|
33
|
+
});
|
|
34
|
+
fs.writeFileSync(path.join(markerDir, 'status-started'), '');
|
|
35
|
+
// Never outlive a broken test run (its cleanup deletes the marker dir).
|
|
36
|
+
const giveUpAt = Date.now() + 30000;
|
|
37
|
+
const poll = () => {
|
|
38
|
+
if (!fs.existsSync(markerDir) || Date.now() > giveUpAt) {
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
if (fs.existsSync(path.join(markerDir, 'release-status'))) {
|
|
42
|
+
process.stdout.write(fs.readFileSync(path.join(markerDir, 'status-output'), 'utf8') + '\\n');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
setTimeout(poll, 20);
|
|
46
|
+
};
|
|
47
|
+
poll();
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
async function createFakeCursorAgent(binDir: string) {
|
|
52
|
+
await writeFile(path.join(binDir, 'cursor-agent.cjs'), FAKE_CURSOR_AGENT_SCRIPT, 'utf8');
|
|
53
|
+
|
|
54
|
+
if (process.platform === 'win32') {
|
|
55
|
+
const commandPath = path.join(binDir, 'cursor-agent.cmd');
|
|
56
|
+
await writeFile(commandPath, '@echo off\r\nnode "%~dp0cursor-agent.cjs" %*\r\n', 'utf8');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// exec so the provider's kill() on timeout reaches the node process itself.
|
|
61
|
+
const commandPath = path.join(binDir, 'cursor-agent');
|
|
62
|
+
await writeFile(commandPath, '#!/bin/sh\nexec node "$(dirname "$0")/cursor-agent.cjs" "$@"\n', 'utf8');
|
|
63
|
+
await chmod(commandPath, 0o755);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Waits in real time (the tests mock only setTimeout, not setImmediate or Date) and fails
|
|
67
|
+
// instead of hanging when the fake never gets there.
|
|
68
|
+
async function waitForFile(filePath: string, description: string) {
|
|
69
|
+
const giveUpAt = Date.now() + 15_000;
|
|
70
|
+
while (!existsSync(filePath)) {
|
|
71
|
+
if (Date.now() > giveUpAt) {
|
|
72
|
+
assert.fail(`timed out waiting for ${description}`);
|
|
73
|
+
}
|
|
74
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type FakeCursorAgent = {
|
|
79
|
+
releaseStatus: () => Promise<void>;
|
|
80
|
+
waitForStatusStart: () => Promise<void>;
|
|
81
|
+
waitForStatusKill: () => Promise<void>;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const withFakeCursorAgent = async (statusOutput: string, fn: (fake: FakeCursorAgent) => Promise<void>) => {
|
|
85
|
+
const binDir = await mkdtemp(path.join(os.tmpdir(), 'cursor-auth-test-'));
|
|
86
|
+
const fake: FakeCursorAgent = {
|
|
87
|
+
releaseStatus: () => writeFile(path.join(binDir, 'release-status'), '', 'utf8'),
|
|
88
|
+
waitForStatusStart: () => waitForFile(path.join(binDir, 'status-started'), 'the fake `status` to start'),
|
|
89
|
+
waitForStatusKill: () => waitForFile(path.join(binDir, 'status-killed'), 'the fake `status` to get SIGTERM'),
|
|
90
|
+
};
|
|
91
|
+
const pathKey = findEnvKey('PATH');
|
|
92
|
+
const pathExtKey = findEnvKey('PATHEXT');
|
|
93
|
+
const previousPath = process.env[pathKey];
|
|
94
|
+
const previousPathExt = process.env[pathExtKey];
|
|
95
|
+
const previousMarkerDir = process.env.CURSOR_FAKE_MARKER_DIR;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await createFakeCursorAgent(binDir);
|
|
99
|
+
await writeFile(path.join(binDir, 'status-output'), statusOutput, 'utf8');
|
|
100
|
+
process.env[pathKey] = `${binDir}${path.delimiter}${previousPath || ''}`;
|
|
101
|
+
process.env.CURSOR_FAKE_MARKER_DIR = binDir;
|
|
102
|
+
if (process.platform === 'win32') {
|
|
103
|
+
process.env[pathExtKey] = previousPathExt?.toUpperCase().includes('.CMD')
|
|
104
|
+
? previousPathExt
|
|
105
|
+
: `.COM;.EXE;.BAT;.CMD${previousPathExt ? `;${previousPathExt}` : ''}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await fn(fake);
|
|
109
|
+
} finally {
|
|
110
|
+
// Lets a fake `status` that is still polling exit right away.
|
|
111
|
+
await fake.releaseStatus().catch(() => {});
|
|
112
|
+
process.env[pathKey] = previousPath;
|
|
113
|
+
if (previousPathExt === undefined) {
|
|
114
|
+
delete process.env[pathExtKey];
|
|
115
|
+
} else {
|
|
116
|
+
process.env[pathExtKey] = previousPathExt;
|
|
117
|
+
}
|
|
118
|
+
if (previousMarkerDir === undefined) {
|
|
119
|
+
delete process.env.CURSOR_FAKE_MARKER_DIR;
|
|
120
|
+
} else {
|
|
121
|
+
process.env.CURSOR_FAKE_MARKER_DIR = previousMarkerDir;
|
|
122
|
+
}
|
|
123
|
+
await rm(binDir, { recursive: true, force: true });
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
test('getStatus waits for a logged-in `cursor-agent status` that takes longer than 5 s (#551)', async () => {
|
|
128
|
+
await withFakeCursorAgent(LOGGED_IN_OUTPUT, async (fake) => {
|
|
129
|
+
// getStatus() arms its timeout before it spawns `status`, so it must see the mocked setTimeout.
|
|
130
|
+
mock.timers.enable({ apis: ['setTimeout'] });
|
|
131
|
+
try {
|
|
132
|
+
const pending = new CursorProviderAuth().getStatus();
|
|
133
|
+
// Move the clock only once `status` runs, i.e. once the timeout is armed.
|
|
134
|
+
await fake.waitForStatusStart();
|
|
135
|
+
|
|
136
|
+
// CLI startup alone took 3-6 s on a busy host; the account request adds a network round trip.
|
|
137
|
+
mock.timers.tick(10_000);
|
|
138
|
+
await fake.releaseStatus();
|
|
139
|
+
|
|
140
|
+
const status = await pending;
|
|
141
|
+
assert.equal(status.error, undefined);
|
|
142
|
+
assert.equal(status.authenticated, true);
|
|
143
|
+
assert.equal(status.email, 'cursor-user@example.com');
|
|
144
|
+
assert.equal(status.method, 'cli');
|
|
145
|
+
} finally {
|
|
146
|
+
mock.timers.reset();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('getStatus still gives up on, and kills, a `cursor-agent status` that never answers', async () => {
|
|
152
|
+
await withFakeCursorAgent(LOGGED_IN_OUTPUT, async (fake) => {
|
|
153
|
+
mock.timers.enable({ apis: ['setTimeout'] });
|
|
154
|
+
try {
|
|
155
|
+
const pending = new CursorProviderAuth().getStatus();
|
|
156
|
+
await fake.waitForStatusStart();
|
|
157
|
+
|
|
158
|
+
mock.timers.tick(60_000);
|
|
159
|
+
|
|
160
|
+
const status = await pending;
|
|
161
|
+
assert.equal(status.installed, true);
|
|
162
|
+
assert.equal(status.authenticated, false);
|
|
163
|
+
assert.equal(status.error, 'Command timeout');
|
|
164
|
+
// On Windows kill() ends the .cmd shim's cmd.exe, which does not signal node behind it.
|
|
165
|
+
if (process.platform !== 'win32') {
|
|
166
|
+
await fake.waitForStatusKill();
|
|
167
|
+
}
|
|
168
|
+
} finally {
|
|
169
|
+
mock.timers.reset();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('getStatus reports no email when `cursor-agent status` could not fetch the account', async () => {
|
|
175
|
+
// What the real CLI prints when its account request fails: the stored login is still valid.
|
|
176
|
+
const output = '✓ Login successful!\nLogged in (unable to fetch user details)';
|
|
177
|
+
await withFakeCursorAgent(output, async (fake) => {
|
|
178
|
+
await fake.releaseStatus();
|
|
179
|
+
|
|
180
|
+
const status = await new CursorProviderAuth().getStatus();
|
|
181
|
+
assert.equal(status.error, undefined);
|
|
182
|
+
assert.equal(status.authenticated, true);
|
|
183
|
+
// The old 'Logged in' placeholder made the Settings card read "Logged in as Logged in".
|
|
184
|
+
assert.equal(status.email, null);
|
|
185
|
+
assert.equal(status.method, 'cli');
|
|
186
|
+
});
|
|
187
|
+
});
|