oh-my-agent-web 0.7.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +394 -248
- package/README.md +387 -247
- package/dist/server/approval/rules.js +4 -16
- package/dist/server/approval/scope.js +3 -3
- package/dist/server/cli-config.js +3 -3
- package/dist/server/cli-info.js +9 -9
- package/dist/server/cli.js +108 -23
- package/dist/server/engine/builtin.js +64 -1
- package/dist/server/engine/catalog-probe.js +34 -0
- package/dist/server/engine/claude-map.js +296 -0
- package/dist/server/engine/claude-sessions.js +581 -0
- package/dist/server/engine/claude-transport.js +75 -0
- package/dist/server/engine/claude.js +414 -0
- package/dist/server/engine/cli-version.js +119 -0
- package/dist/server/engine/codex-map.js +19 -23
- package/dist/server/engine/codex-sessions.js +5 -5
- package/dist/server/engine/codex-subagents.js +349 -0
- package/dist/server/engine/codex.js +215 -2
- package/dist/server/engine/dsh-history.js +1 -1
- package/dist/server/engine/dsh-log.js +47 -36
- package/dist/server/engine/dsh-map.js +3 -5
- package/dist/server/engine/dsh-prompt-patch.js +48 -0
- package/dist/server/engine/dsh-sessions.js +10 -11
- package/dist/server/engine/dsh-transport.js +2 -2
- package/dist/server/engine/dsh.js +45 -2
- package/dist/server/engine/engine-versions.js +41 -0
- package/dist/server/engine/fake.js +38 -7
- package/dist/server/engine/model-registry-reload.js +1 -1
- package/dist/server/engine/opencode-map.js +2 -2
- package/dist/server/engine/opencode-projects.js +1 -1
- package/dist/server/engine/opencode-sessions.js +2 -2
- package/dist/server/engine/opencode.js +39 -1
- package/dist/server/engine/pi-events.js +4 -4
- package/dist/server/engine/pi-plugins.js +8 -6
- package/dist/server/engine/pi-sdk.js +179 -5
- package/dist/server/engine/pi-subagent-tool.js +2 -2
- package/dist/server/engine/pi-subagents.js +2 -2
- package/dist/server/engine/pi-transcript.js +1 -1
- package/dist/server/engine/registry.js +12 -2
- package/dist/server/engine/suggestion-guidance.js +22 -9
- package/dist/server/engine/wiring.js +57 -3
- package/dist/server/files/content-search.js +187 -0
- package/dist/server/files/file-search.js +3 -3
- package/dist/server/files/file-service.js +33 -15
- package/dist/server/files/files-routes.js +68 -7
- package/dist/server/files/git-info.js +51 -11
- package/dist/server/files/path-resolve.js +176 -0
- package/dist/server/files/pick-dir-routes.js +93 -0
- package/dist/server/files/pick-path-routes.js +126 -0
- package/dist/server/http/auth-routes.js +71 -4
- package/dist/server/http/auth.js +48 -30
- package/dist/server/http/aux-routes.js +20 -4
- package/dist/server/http/file-headers.js +3 -3
- package/dist/server/http/health-and-static.js +1 -1
- package/dist/server/http/live-routes.js +4 -4
- package/dist/server/http/login-page.js +128 -1
- package/dist/server/http/password-store.js +31 -0
- package/dist/server/index.js +93 -11
- package/dist/server/markers/actions.js +8 -8
- package/dist/server/mcp/codex-mcp-store.js +190 -0
- package/dist/server/mcp/json-mcp-store.js +276 -0
- package/dist/server/mcp/mcp-auth.js +1 -1
- package/dist/server/mcp/mcp-command.js +0 -2
- package/dist/server/mcp/mcp-engine-shared.js +112 -0
- package/dist/server/mcp/mcp-import.js +4 -5
- package/dist/server/mcp/mcp-market.js +2 -3
- package/dist/server/mcp/mcp-routes.js +4 -3
- package/dist/server/mcp/none-mcp-store.js +27 -0
- package/dist/server/mcp/pi-mcp-config.js +1 -1
- package/dist/server/plugins/engine-plugin-stores.js +265 -0
- package/dist/server/plugins/plugin-routes.js +4 -3
- package/dist/server/protocol-parse.js +52 -4
- package/dist/server/protocol.js +0 -1
- package/dist/server/providers/catalog-writes.js +1 -1
- package/dist/server/providers/catalog.js +2 -2
- package/dist/server/providers/codex-models.js +1 -1
- package/dist/server/providers/codex-test.js +1 -1
- package/dist/server/providers/enabled-models.js +1 -11
- package/dist/server/providers/import-routes.js +2 -2
- package/dist/server/providers/probe-routes.js +26 -0
- package/dist/server/providers/provider-handlers.js +3 -3
- package/dist/server/providers/provider-pool.js +2 -2
- package/dist/server/providers/provider-store.js +5 -2
- package/dist/server/providers/subscription-routes.js +4 -2
- package/dist/server/providers/validate.js +13 -14
- package/dist/server/push/push-routes.js +32 -3
- package/dist/server/push/push.js +43 -9
- package/dist/server/session/client-view.js +2 -2
- package/dist/server/session/close-ephemeral.js +23 -0
- package/dist/server/session/conversation-create.js +31 -9
- package/dist/server/session/delete-session.js +25 -14
- package/dist/server/session/engine-switch.js +12 -5
- package/dist/server/session/fork-session.js +38 -0
- package/dist/server/session/lock.js +4 -4
- package/dist/server/session/model-controller.js +14 -2
- package/dist/server/session/move-session.js +40 -0
- package/dist/server/session/persist-ephemeral.js +48 -0
- package/dist/server/session/pi-opener.js +2 -2
- package/dist/server/session/pi-refresh.js +1 -1
- package/dist/server/session/pi-sessions-provider.js +2 -2
- package/dist/server/session/pi-sessions.js +107 -18
- package/dist/server/session/runtime.js +144 -9
- package/dist/server/session/session-stats.js +2 -2
- package/dist/server/session/settings.js +45 -49
- package/dist/server/session/subagents.js +4 -1
- package/dist/server/session/suggest-title.js +33 -0
- package/dist/server/session/title-prompt.js +126 -0
- package/dist/server/session/turn-finish.js +2 -2
- package/dist/server/session/turn-queue.js +5 -5
- package/dist/server/session/turn-watchdog.js +2 -2
- package/dist/server/skills/skill-install.js +3 -5
- package/dist/server/skills/skill-store.js +3 -3
- package/dist/server/skills/skillhub.js +3 -4
- package/dist/server/snapshot/messages.js +2 -8
- package/dist/server/snapshot/sender.js +1 -1
- package/dist/server/snapshot/state.js +3 -5
- package/dist/server/subagents/profile-store.js +11 -9
- package/dist/server/terminal/terminal-session.js +9 -9
- package/dist/server/uploads/upload-routes.js +1 -1
- package/dist/server/uploads/upload-store.js +4 -6
- package/dist/server/util/env.js +27 -21
- package/dist/server/util/ids.js +1 -1
- package/dist/server/util/json-file.js +38 -0
- package/dist/server/util/log-file.js +3 -3
- package/dist/server/util/log.js +4 -4
- package/dist/server/util/origin.js +5 -24
- package/dist/server/util/paths.js +25 -3
- package/dist/server/util/pricing-index.js +2 -6
- package/dist/server/util/pricing.js +10 -10
- package/dist/server/util/spawn-cli.js +2 -2
- package/dist/server/util/upstream-version.js +10 -12
- package/dist/server/util/version.js +2 -2
- package/dist/server/web-proxy/proxy-routes.js +167 -0
- package/dist/server/web-proxy/proxy-url.js +60 -0
- package/dist/server/web-proxy/rewrite.js +84 -0
- package/dist/server/ws/handle-message.js +21 -2
- package/package.json +13 -14
- package/scripts/prune-prebuilds.mjs +1 -1
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_abnfDiagram-WEYOIA3C-Dq-2fRb4.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_architectureDiagram-ZHVYKT53-Dh5CEs4k.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_c4Diagram-XDPPPE54-Cb19QmmE.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_cynefinDiagram-WF24UDRP-COk-IL9O.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_diagram-CBK3MWTC-Gp_0nfEJ.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_diagram-ODIL2UCW-rzPf_9IN.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_diagram-PFBEJUXE-B5EnLWwl.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_diagram-TYUNFSNR-DVpFKFwC.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_ebnfDiagram-V4P3TTPT-CDL1qiPY.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_ishikawaDiagram-HPEHV2GE-CSkoONzp.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_mindmap-definition-6OY57UG3-B9sdVB6_.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_pegDiagram-ECIUJNBX-B6P7baCF.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_railroadDiagram-Y72NRBL7-W_a8Vy2l.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_swimlanes-B3ENRKFQ-DvGif-RR.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_swimlanesDiagram-AYQRV4AK-D0gFG_EI.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_usecaseDiagram-WPOLBNNX-BjgUBJiR.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_vennDiagram-Q3Y3IDCW-DnVuWTpF.js +1 -0
- package/web/dist/assets/_oh-my-agent-web-mermaid-pruned_chunk_wardleyDiagram-IVAIDKWA-B2vDAqpl.js +1 -0
- package/web/dist/assets/{arc-BTAvbR16.js → arc-Df9SI9yh.js} +1 -1
- package/web/dist/assets/{architecture-WOLXFQ4H-fewLD5_7.js → architecture-WOLXFQ4H-iO49E8Y1.js} +1 -1
- package/web/dist/assets/{blockDiagram-RJAE7QA3-ClmgyxZl.js → blockDiagram-RJAE7QA3-D8d1ukIs.js} +1 -1
- package/web/dist/assets/channel-C2-OT56h.js +1 -0
- package/web/dist/assets/{chunk-55XCU76K-DdC5EhZG.js → chunk-55XCU76K-C5wOvqDb.js} +1 -1
- package/web/dist/assets/{chunk-7S6XSTFO-CgToWHK6.js → chunk-7S6XSTFO-WIrWcDbG.js} +1 -1
- package/web/dist/assets/{chunk-AQDTSKZB-D-rQMnp7.js → chunk-AQDTSKZB-OkzYqFsw.js} +1 -1
- package/web/dist/assets/{chunk-DSNLSHJZ-cuZoTGMH.js → chunk-DSNLSHJZ-CHEA9kYd.js} +2 -2
- package/web/dist/assets/{chunk-HNE4Q36P-CiKr9iSN.js → chunk-HNE4Q36P-CPsq34CI.js} +1 -1
- package/web/dist/assets/{chunk-VQ5W62QI-DbTfXAAT.js → chunk-VQ5W62QI-CamPnEuF.js} +1 -1
- package/web/dist/assets/{chunk-WQQL4CDK-DHanHfB5.js → chunk-WQQL4CDK-gNypBVAM.js} +1 -1
- package/web/dist/assets/{chunk-ZAUX2LTX-SrjFXpy2.js → chunk-ZAUX2LTX--4WyRBRV.js} +1 -1
- package/web/dist/assets/{chunk-ZCHMQSOR-7Wy8xXQ5.js → chunk-ZCHMQSOR-umv3Lkpn.js} +1 -1
- package/web/dist/assets/{classDiagram-v2-4MYWR3UA-CBe5ZDvZ.js → classDiagram-v2-4MYWR3UA-C1XldpO1.js} +1 -1
- package/web/dist/assets/{cose-bilkent-JH36ORCC-Dljdyvhw.js → cose-bilkent-JH36ORCC-BvugZzFU.js} +1 -1
- package/web/dist/assets/{cynefin-EF2NZ3EQ-CcZU7GFP.js → cynefin-EF2NZ3EQ-6qXB70s4.js} +1 -1
- package/web/dist/assets/{dagre-NK4GIZVX-Bq_6y8qp.js → dagre-NK4GIZVX-DDdlABYw.js} +1 -1
- package/web/dist/assets/{diagram-I5XIIV2D-C2lnnYVG.js → diagram-I5XIIV2D-T8oWy5CY.js} +1 -1
- package/web/dist/assets/{diagram-RJA3I4VV-Bqf8Z6wJ.js → diagram-RJA3I4VV-CkdHTtfV.js} +1 -1
- package/web/dist/assets/{elk-QONTBCTQ-Cdzq_cJ5.js → elk-QONTBCTQ-BiEUbJJX.js} +1 -1
- package/web/dist/assets/{erDiagram-KYQIGQAA-ZJLCjPFC.js → erDiagram-KYQIGQAA-q3WwhyzH.js} +1 -1
- package/web/dist/assets/{eventmodeling-K75KTNOO-DHqeIpes.js → eventmodeling-K75KTNOO-BO5gW_NC.js} +1 -1
- package/web/dist/assets/{flowDiagram-IUGLLWBU-AVgYvXKN.js → flowDiagram-IUGLLWBU-DubjP6UK.js} +1 -1
- package/web/dist/assets/{ganttDiagram-R7GPNTJY-CR1SG1M7.js → ganttDiagram-R7GPNTJY-lKNRH8CI.js} +1 -1
- package/web/dist/assets/{gitGraph-VSP46ZUC-CIZQPK0R.js → gitGraph-VSP46ZUC-Cwlv3omT.js} +1 -1
- package/web/dist/assets/{gitGraphDiagram-PDYL7XHY-BhOCWEh6.js → gitGraphDiagram-PDYL7XHY-Bo9GpFvW.js} +1 -1
- package/web/dist/assets/index-CeYhxqkk.js +113 -0
- package/web/dist/assets/index-GJgJwZ23.css +1 -0
- package/web/dist/assets/{info-OHQRW6UA-C2wuRxeQ.js → info-OHQRW6UA-g3MbTRmN.js} +1 -1
- package/web/dist/assets/{infoDiagram-MVDRKAX5-BP5doAB5.js → infoDiagram-MVDRKAX5-CmILYLoB.js} +1 -1
- package/web/dist/assets/{journeyDiagram-BOSHW7O7-BcIDRTPS.js → journeyDiagram-BOSHW7O7-Av8T0U1C.js} +1 -1
- package/web/dist/assets/{kanban-definition-FNSA4HC5-CRH_bcoT.js → kanban-definition-FNSA4HC5-B9NyWQYN.js} +1 -1
- package/web/dist/assets/{line-iA4GPA_S.js → line-BUrll1My.js} +1 -1
- package/web/dist/assets/{linear-BGUKwkTH.js → linear-DjET0phX.js} +1 -1
- package/web/dist/assets/{math-DgNW_bVP.js → math-BJAjkhlu.js} +1 -1
- package/web/dist/assets/{mermaid-parser.core-CsqmgpEv.js → mermaid-parser.core-CaTdkhkS.js} +3 -3
- package/web/dist/assets/{mermaid.core-C1yCGQK6.js → mermaid.core-Cktx937U.js} +4 -4
- package/web/dist/assets/{packet-JDAUHWVQ-2ggaoz2g.js → packet-JDAUHWVQ-DhG6l3PG.js} +1 -1
- package/web/dist/assets/{pie-XZMESJXO-h_yslQQs.js → pie-XZMESJXO-YPcOx7s0.js} +1 -1
- package/web/dist/assets/{pieDiagram-GNMPY74T-6-JZMFyk.js → pieDiagram-GNMPY74T-DesiZKUl.js} +1 -1
- package/web/dist/assets/{quadrantDiagram-D57K4MC3-K5r1HOgL.js → quadrantDiagram-D57K4MC3-Cn1kBVgu.js} +1 -1
- package/web/dist/assets/{radar-ABXABTNO-6fOJGGgq.js → radar-ABXABTNO-CtuRfYSz.js} +1 -1
- package/web/dist/assets/{railroad-I3PHUGI6-B_gUgQ3d.js → railroad-I3PHUGI6-D5Gnyy0R.js} +1 -1
- package/web/dist/assets/{railroad-abnf-LNEFI6M7-CJ_eqiXZ.js → railroad-abnf-LNEFI6M7-Kw4pmJEm.js} +1 -1
- package/web/dist/assets/{railroad-ebnf-DWYD2UWJ-BTsgjLTU.js → railroad-ebnf-DWYD2UWJ-D8Mk5dIe.js} +1 -1
- package/web/dist/assets/{railroad-peg-JR7BVNR7-D91uiLln.js → railroad-peg-JR7BVNR7-R5Jwzs8F.js} +1 -1
- package/web/dist/assets/{requirementDiagram-EQYSW5RA-uapnV_OO.js → requirementDiagram-EQYSW5RA-B9RDq-cL.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-SY24JHO5-BQsbFgUb.js → sankeyDiagram-SY24JHO5-DoEatOm6.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-BLEHNHQU-iRoSYX_e.js → sequenceDiagram-BLEHNHQU-DGE5Z9lT.js} +1 -1
- package/web/dist/assets/{src-BBrKcs9r.js → src-BnEL7pZw.js} +1 -1
- package/web/dist/assets/{stateDiagram-v2-A6IESKWZ-Bzj9z5KH.js → stateDiagram-v2-A6IESKWZ-_vn8W-k_.js} +1 -1
- package/web/dist/assets/{timeline-definition-JXYKGLGB-DLUUuJX0.js → timeline-definition-JXYKGLGB-BaSLTtgR.js} +1 -1
- package/web/dist/assets/{treeView-D4JQ5SDB-CphIQ3n5.js → treeView-D4JQ5SDB-DZElyFgL.js} +1 -1
- package/web/dist/assets/{treemap-SAJKECNS-DqU3insw.js → treemap-SAJKECNS-DWDO4K4G.js} +1 -1
- package/web/dist/assets/{wardley-7MLQ67FV-Ch7d8cmC.js → wardley-7MLQ67FV-DTOUsk6Q.js} +1 -1
- package/web/dist/assets/{xychartDiagram-4URNHDU4-DdxpWBeM.js → xychartDiagram-4URNHDU4-DiaCfgAB.js} +1 -1
- package/web/dist/index.html +2 -2
- package/docs/screenshots/01-chat.png +0 -0
- package/docs/screenshots/02-answer.png +0 -0
- package/docs/screenshots/03-files-diff.png +0 -0
- package/docs/screenshots/04-terminal.png +0 -0
- package/docs/screenshots/05-models.png +0 -0
- package/docs/screenshots/06-skills.png +0 -0
- package/docs/screenshots/07-skills-hub.png +0 -0
- package/docs/screenshots/08-mcp.png +0 -0
- package/docs/screenshots/09-subagents.png +0 -0
- package/docs/screenshots/10-settings.png +0 -0
- package/docs/screenshots/11-plugins.png +0 -0
- package/docs/screenshots/12-voice.png +0 -0
- package/docs/screenshots/13-push.png +0 -0
- package/docs/screenshots/README.md +0 -45
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_abnfDiagram-WEYOIA3C-CvNbnJ75.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_architectureDiagram-ZHVYKT53-T7NZZ4ao.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_c4Diagram-XDPPPE54-ByuAPg39.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_cynefinDiagram-WF24UDRP-BY5DHttT.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_diagram-CBK3MWTC-D7ZYyrF7.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_diagram-ODIL2UCW-C6kcOZFc.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_diagram-PFBEJUXE-CzFPFq0D.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_diagram-TYUNFSNR-B4G4T8SL.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_ebnfDiagram-V4P3TTPT-BkQ4xwD0.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_ishikawaDiagram-HPEHV2GE-52xqckJv.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_mindmap-definition-6OY57UG3-7cQksbFn.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_pegDiagram-ECIUJNBX-udgtBpwv.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_railroadDiagram-Y72NRBL7-Cd2dcQWz.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_swimlanes-B3ENRKFQ-p34pL1Yk.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_swimlanesDiagram-AYQRV4AK-BslfPs4r.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_usecaseDiagram-WPOLBNNX-DJZoN9Xk.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_vennDiagram-Q3Y3IDCW-DPXo67cZ.js +0 -1
- package/web/dist/assets/_omaw-mermaid-pruned_chunk_wardleyDiagram-IVAIDKWA-BE6ukwkg.js +0 -1
- package/web/dist/assets/channel-DfaYIFFE.js +0 -1
- package/web/dist/assets/index-CZQjJbRg.js +0 -104
- package/web/dist/assets/index-ChDUWKZX.css +0 -1
- /package/web/dist/assets/{_omaw-mermaid-pruned_katex_katex-Zojedrce.js → _oh-my-agent-web-mermaid-pruned_katex_katex-Zojedrce.js} +0 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/** 模型 id 的前缀:界面按 `provider/model` 显示,Claude 只有一家 provider */
|
|
2
|
+
export const CLAUDE_PROVIDER = "anthropic";
|
|
3
|
+
/** ACP 的模型表 → 我们的模型下拉(id 统一成 `anthropic/<modelId>`,label 用适配器给的名字) */
|
|
4
|
+
export function modelOptions(models) {
|
|
5
|
+
const current = models?.currentModelId;
|
|
6
|
+
const out = [];
|
|
7
|
+
for (const model of models?.availableModels ?? []) {
|
|
8
|
+
const modelId = typeof model.modelId === "string" ? model.modelId.trim() : "";
|
|
9
|
+
if (modelId === "")
|
|
10
|
+
continue;
|
|
11
|
+
out.push({
|
|
12
|
+
id: `${CLAUDE_PROVIDER}/${modelId}`,
|
|
13
|
+
label: (model.name ?? modelId).trim(),
|
|
14
|
+
...(modelId === current ? { current: true } : {}),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
export function currentModelId(models) {
|
|
20
|
+
const id = typeof models?.currentModelId === "string" ? models.currentModelId.trim() : "";
|
|
21
|
+
return id === "" ? null : id;
|
|
22
|
+
}
|
|
23
|
+
/** `anthropic/opus` → `opus`;`opus` 也接受(AGENT_WEB_CLAUDE_MODEL 允许写裸别名) */
|
|
24
|
+
function parseClaudeModelId(value) {
|
|
25
|
+
const raw = (value ?? "").trim();
|
|
26
|
+
if (raw === "")
|
|
27
|
+
return null;
|
|
28
|
+
if (!raw.startsWith(`${CLAUDE_PROVIDER}/`))
|
|
29
|
+
return raw;
|
|
30
|
+
const rest = raw.slice(CLAUDE_PROVIDER.length + 1).trim();
|
|
31
|
+
return rest === "" ? null : rest;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 一个模型标识 → 发给 `session/set_model` 的 modelId。
|
|
35
|
+
*
|
|
36
|
+
* 只认**列表里有的**(给 null = 这个模型不存在):和 opencode 一样,「点了就要么成功要么
|
|
37
|
+
* 给出可读原因」,而不是把随便一个字符串塞给引擎再收一个 500。
|
|
38
|
+
* 列表为空(还没建会话)时也不放行 —— 调用方会先 resume 拿列表。
|
|
39
|
+
*/
|
|
40
|
+
export function modelIdFor(models, wanted) {
|
|
41
|
+
const target = parseClaudeModelId(wanted);
|
|
42
|
+
if (target === null)
|
|
43
|
+
return null;
|
|
44
|
+
for (const model of models?.availableModels ?? []) {
|
|
45
|
+
if (model.modelId === target)
|
|
46
|
+
return target;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
/** 当前模型 → port 的 `{provider, model}`(取不到给 null) */
|
|
51
|
+
export function currentModelOf(models) {
|
|
52
|
+
const id = currentModelId(models);
|
|
53
|
+
return id === null ? null : { provider: CLAUDE_PROVIDER, model: id };
|
|
54
|
+
}
|
|
55
|
+
/** ACP 的 content 块 → 纯文本(图片块不参与正文) */
|
|
56
|
+
export function textOfContent(content) {
|
|
57
|
+
if (typeof content === "string")
|
|
58
|
+
return content;
|
|
59
|
+
if (Array.isArray(content)) {
|
|
60
|
+
return content
|
|
61
|
+
.map((item) => textOfContent(item))
|
|
62
|
+
.filter((text) => text !== "")
|
|
63
|
+
.join("");
|
|
64
|
+
}
|
|
65
|
+
if (content !== null && typeof content === "object") {
|
|
66
|
+
const block = content;
|
|
67
|
+
if (typeof block.text === "string" && (block.type === undefined || block.type === "text"))
|
|
68
|
+
return block.text;
|
|
69
|
+
}
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
/** 正文 / 思考增量;其余 update(plan / available_commands / current_mode…)不产生事件 */
|
|
73
|
+
export function textEventOf(update) {
|
|
74
|
+
if (update.sessionUpdate === "agent_message_chunk") {
|
|
75
|
+
const text = textOfContent(update.content);
|
|
76
|
+
return text === "" ? null : { type: "text_delta", text };
|
|
77
|
+
}
|
|
78
|
+
if (update.sessionUpdate === "agent_thought_chunk") {
|
|
79
|
+
const text = textOfContent(update.content);
|
|
80
|
+
return text === "" ? null : { type: "thinking_delta", text };
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/** 从 tool_call / tool_call_update 里抽出工具事实(后到的 update 更详细,合并而不是覆盖) */
|
|
85
|
+
export function toolFactsOf(update) {
|
|
86
|
+
const toolCallId = typeof update.toolCallId === "string" ? update.toolCallId : "";
|
|
87
|
+
if (toolCallId === "")
|
|
88
|
+
return null;
|
|
89
|
+
const facts = { toolCallId };
|
|
90
|
+
const toolName = update._meta?.claudeCode?.toolName;
|
|
91
|
+
if (typeof toolName === "string" && toolName !== "")
|
|
92
|
+
facts.toolName = toolName;
|
|
93
|
+
if (typeof update.title === "string" && update.title !== "")
|
|
94
|
+
facts.title = update.title;
|
|
95
|
+
if (typeof update.kind === "string" && update.kind !== "")
|
|
96
|
+
facts.kind = update.kind;
|
|
97
|
+
if (update.rawInput !== undefined)
|
|
98
|
+
facts.rawInput = update.rawInput;
|
|
99
|
+
return facts;
|
|
100
|
+
}
|
|
101
|
+
/** 参数里挑一句最有用的话当摘要(Bash 的命令 / Write 的路径),拿不到才退回整段 JSON */
|
|
102
|
+
export function summarizeToolInput(toolName, rawInput) {
|
|
103
|
+
const input = rawInput !== null && typeof rawInput === "object" ? rawInput : null;
|
|
104
|
+
const pick = (...keys) => {
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
const value = input?.[key];
|
|
107
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
};
|
|
112
|
+
const name = (toolName ?? "").toLowerCase();
|
|
113
|
+
if (name === "bash" || name === "shell") {
|
|
114
|
+
const command = pick("command", "cmd");
|
|
115
|
+
if (command)
|
|
116
|
+
return command;
|
|
117
|
+
}
|
|
118
|
+
if (["read", "write", "edit", "multiedit", "notebookread", "notebookedit"].includes(name)) {
|
|
119
|
+
const path = pick("file_path", "notebook_path", "path");
|
|
120
|
+
if (path)
|
|
121
|
+
return path;
|
|
122
|
+
}
|
|
123
|
+
if (name === "grep" || name === "glob") {
|
|
124
|
+
const pattern = pick("pattern");
|
|
125
|
+
if (pattern)
|
|
126
|
+
return pattern;
|
|
127
|
+
}
|
|
128
|
+
if (name === "webfetch" || name === "websearch") {
|
|
129
|
+
const target = pick("url", "query");
|
|
130
|
+
if (target)
|
|
131
|
+
return target;
|
|
132
|
+
}
|
|
133
|
+
if (name === "task") {
|
|
134
|
+
const description = pick("description");
|
|
135
|
+
if (description)
|
|
136
|
+
return description;
|
|
137
|
+
}
|
|
138
|
+
// 认不出工具(自定义 / MCP):把常见字段都试一遍,最后退回整段 JSON
|
|
139
|
+
const generic = pick("command", "file_path", "path", "pattern", "url", "description", "prompt");
|
|
140
|
+
return generic ?? safeJson(rawInput);
|
|
141
|
+
}
|
|
142
|
+
export function toolStartEvent(facts) {
|
|
143
|
+
const name = (facts.toolName ?? "").trim() || (facts.title ?? "").trim() || facts.kind || "tool";
|
|
144
|
+
const summary = summarizeToolInput(facts.toolName, facts.rawInput);
|
|
145
|
+
const full = facts.rawInput === undefined ? "" : safeJson(facts.rawInput);
|
|
146
|
+
return {
|
|
147
|
+
type: "tool_start",
|
|
148
|
+
toolCallId: facts.toolCallId,
|
|
149
|
+
name,
|
|
150
|
+
// 摘要就是全部参数时(bash 就一句命令)不再给 argsFull,别让界面出现两份一样的
|
|
151
|
+
argsSummary: summary.length > 300 ? `${summary.slice(0, 300)}…` : summary,
|
|
152
|
+
...(hasSpareArgs(facts.rawInput, summary) ? { argsFull: full } : {}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 完整参数里除了「已经当摘要的那一句」还有别的东西吗?
|
|
157
|
+
*
|
|
158
|
+
* 判据:超过一个键,或唯一的那个键不是字符串(嵌套对象/数组,摘要表达不了)。
|
|
159
|
+
* 为什么不用「JSON 字符串 ≠ 摘要」:`{"command":"npm test"}` 永远不等于 `npm test`,
|
|
160
|
+
* 那个判据会把每次 bash 都变成「有两份参数」。
|
|
161
|
+
*/
|
|
162
|
+
function hasSpareArgs(rawInput, summary) {
|
|
163
|
+
if (rawInput === null || typeof rawInput !== "object")
|
|
164
|
+
return false;
|
|
165
|
+
const keys = Object.keys(rawInput);
|
|
166
|
+
if (keys.length === 0)
|
|
167
|
+
return false;
|
|
168
|
+
if (keys.length > 1)
|
|
169
|
+
return true;
|
|
170
|
+
const only = rawInput[keys[0]];
|
|
171
|
+
return typeof only === "string" ? only !== summary : true;
|
|
172
|
+
}
|
|
173
|
+
/** 工具收口:只有 completed / failed 才发(pending 是过程态) */
|
|
174
|
+
export function isFinalToolStatus(status) {
|
|
175
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
176
|
+
}
|
|
177
|
+
export function toolEndEvent(update) {
|
|
178
|
+
const toolCallId = typeof update.toolCallId === "string" ? update.toolCallId : "claude-tool";
|
|
179
|
+
// rawOutput 是 SDK 的 tool_result.content:字符串、文本块数组、或 {stdout,stderr} 之类的对象
|
|
180
|
+
const output = outputTextOf(update.rawOutput);
|
|
181
|
+
return {
|
|
182
|
+
type: "tool_end",
|
|
183
|
+
toolCallId,
|
|
184
|
+
status: update.status === "completed" ? "ok" : "failed",
|
|
185
|
+
output,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function outputTextOf(value) {
|
|
189
|
+
if (typeof value === "string")
|
|
190
|
+
return value;
|
|
191
|
+
if (Array.isArray(value)) {
|
|
192
|
+
return value
|
|
193
|
+
.map((item) => outputTextOf(item))
|
|
194
|
+
.filter((text) => text !== "")
|
|
195
|
+
.join("\n");
|
|
196
|
+
}
|
|
197
|
+
if (value !== null && typeof value === "object") {
|
|
198
|
+
const record = value;
|
|
199
|
+
if (typeof record.text === "string")
|
|
200
|
+
return record.text;
|
|
201
|
+
if (typeof record.content !== "undefined")
|
|
202
|
+
return outputTextOf(record.content);
|
|
203
|
+
const stdout = typeof record.stdout === "string" ? record.stdout : "";
|
|
204
|
+
const stderr = typeof record.stderr === "string" ? record.stderr : "";
|
|
205
|
+
if (stdout !== "" || stderr !== "")
|
|
206
|
+
return [stdout, stderr].filter((text) => text !== "").join("\n");
|
|
207
|
+
return safeJson(value);
|
|
208
|
+
}
|
|
209
|
+
return "";
|
|
210
|
+
}
|
|
211
|
+
function safeJson(value) {
|
|
212
|
+
try {
|
|
213
|
+
return JSON.stringify(value) ?? "";
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return String(value);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** ACP stopReason → 我们的回合结束原因(与 dsh / opencode 同口径) */
|
|
220
|
+
export function stopReasonToTurnEnd(reason) {
|
|
221
|
+
if (reason === "cancelled")
|
|
222
|
+
return "interrupted";
|
|
223
|
+
if (reason === "refusal")
|
|
224
|
+
return "error";
|
|
225
|
+
return "done"; // end_turn / max_tokens / max_turn_requests:这轮确实结束了
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 权限请求 → 我们的审批请求。
|
|
229
|
+
*
|
|
230
|
+
* 请求体只有 `toolCall.title`(人话标题)与 `rawInput`,真工具名要靠 `tool_call` 那次通知
|
|
231
|
+
* 缓存的 `_meta.claudeCode.toolName`(`toolFacts`)。拿不到就用标题当名字 ——
|
|
232
|
+
* 审批规则按 `scopeKey` 匹配,宁可粗一点,也不要空着。
|
|
233
|
+
*/
|
|
234
|
+
export function permissionApprovalRequest(request, tool) {
|
|
235
|
+
const toolName = (tool?.toolName ?? "").trim() || (tool?.title ?? request.toolCall?.title ?? "").trim() || "Claude 工具调用";
|
|
236
|
+
const args = request.toolCall?.rawInput ?? tool?.rawInput;
|
|
237
|
+
const summary = args === undefined ? "" : summarizeToolInput(toolName, args);
|
|
238
|
+
const kind = tool?.kind ?? "";
|
|
239
|
+
const riskNote = kind === "execute" || /bash|shell/i.test(toolName)
|
|
240
|
+
? "Claude Code 请求执行这条命令;是否放行由你决定"
|
|
241
|
+
: kind === "edit"
|
|
242
|
+
? "Claude Code 请求修改这个文件;是否放行由你决定"
|
|
243
|
+
: kind === "fetch"
|
|
244
|
+
? "Claude Code 请求访问这个网络地址;是否放行由你决定"
|
|
245
|
+
: "Claude Code 请求执行这个操作;是否放行由你决定";
|
|
246
|
+
return {
|
|
247
|
+
toolName,
|
|
248
|
+
argsSummary: summary.length > 300 ? `${summary.slice(0, 300)}…` : summary,
|
|
249
|
+
scopeKey: `claude|${toolName}`,
|
|
250
|
+
scopeHint: toolName,
|
|
251
|
+
riskNote,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* 我们的三态 → 权限选项 id。
|
|
256
|
+
*
|
|
257
|
+
* 一律**按 kind 匹配**:Claude 有两组选项(普通工具 `allow_always/allow/reject`、
|
|
258
|
+
* `ExitPlanMode` 的 `acceptEdits/default/plan`),写死 id 会在第二组上给出错的答案。
|
|
259
|
+
*/
|
|
260
|
+
export function permissionOptionId(options, decision) {
|
|
261
|
+
const list = (options ?? []).filter((option) => typeof option.optionId === "string");
|
|
262
|
+
const byKind = (kinds) => {
|
|
263
|
+
for (const kind of kinds) {
|
|
264
|
+
const hit = list.find((option) => option.kind === kind);
|
|
265
|
+
if (hit)
|
|
266
|
+
return hit.optionId;
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
};
|
|
270
|
+
const picked = decision === "deny"
|
|
271
|
+
? (byKind(["reject_once", "reject_always"]) ??
|
|
272
|
+
list.find((option) => /reject|deny|cancel|plan/i.test(option.optionId))?.optionId)
|
|
273
|
+
: (byKind(decision === "always" ? ["allow_always", "allow_once"] : ["allow_once", "allow_always"]) ??
|
|
274
|
+
list.find((option) => /allow|accept|approve|default/i.test(option.optionId))?.optionId);
|
|
275
|
+
// 兜底:宁可给第一个(Claude 的两组里第一个都是「允许」),也别让对端一直等回包
|
|
276
|
+
return picked ?? list[0]?.optionId ?? (decision === "deny" ? "reject" : "allow");
|
|
277
|
+
}
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
// 错误 → 人话
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
export function friendlyClaudeError(error) {
|
|
282
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
283
|
+
if (/ENOENT|spawn .* ENOENT|command not found/i.test(message)) {
|
|
284
|
+
return "没有找到 claude-code-acp / claude-agent-acp:先 `npm i -g @zed-industries/claude-code-acp`(或改名后的 `npm i -g @agentclientprotocol/claude-agent-acp`;Claude Code 本体也要装好并登录),或设 AGENT_WEB_CLAUDE_BIN 指向它";
|
|
285
|
+
}
|
|
286
|
+
if (/authRequired|authentication required|Please run \/login|Invalid API key|401|403|unauthor/i.test(message)) {
|
|
287
|
+
return "Claude Code 没登录 / 凭据失效:先在终端里跑一次 `claude` 完成登录(本机凭据要单独登录),再切回来";
|
|
288
|
+
}
|
|
289
|
+
if (/model.*(not found|invalid|unknown)|unknown model/i.test(message)) {
|
|
290
|
+
return "Claude Code 里没有这个模型:换一个(列表来自 claude 自己的可用模型)";
|
|
291
|
+
}
|
|
292
|
+
if (/Session not found|session.*not found/i.test(message)) {
|
|
293
|
+
return "Claude Code 里找不到这条会话(可能已被清理):新建一条即可";
|
|
294
|
+
}
|
|
295
|
+
return message;
|
|
296
|
+
}
|