coding-tool-x 3.8.4 → 3.8.5
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/CHANGELOG.md +15 -0
- package/README.md +86 -15
- package/dist/web/assets/AgentsDrawer-BG6P3DX-.js +1 -0
- package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
- package/dist/web/assets/Analytics--z_FmL5P.js +1 -0
- package/dist/web/assets/Analytics-CyHhR126.css +1 -0
- package/dist/web/assets/AssetPathField-CrJtRKHy.js +1 -0
- package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
- package/dist/web/assets/ChatHistoryDrawer-BiuEryJj.js +1 -0
- package/dist/web/assets/CollapseItem-BAYEsL_Q.js +1 -0
- package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
- package/dist/web/assets/CommandsDrawer-YJ-U7BXN.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-Ce_hAknW.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
- package/dist/web/assets/ConfigTemplates-Bvr1NN6K.js +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-BjIeHfF-.js +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-AV00tm_D.js +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
- package/dist/web/assets/Divider-D28g1gwj.js +1 -0
- package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
- package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
- package/dist/web/assets/FavoritesDrawer-Cz95o4CD.js +1 -0
- package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
- package/dist/web/assets/GatewayConvertDrawer-DLlbMz27.js +1 -0
- package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
- package/dist/web/assets/Home-D7CCOFQC.js +1 -0
- package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
- package/dist/web/assets/MarkdownEditor-rEZ-c72L.js +1 -0
- package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
- package/dist/web/assets/McpDrawer-DfoCyxU2.js +1 -0
- package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
- package/dist/web/assets/McpFormDrawer-SKu8HNUR.js +1 -0
- package/dist/web/assets/PluginManager-BbeK-3rV.js +1 -0
- package/dist/web/assets/PluginsDrawer-CWVJ7yjS.js +1 -0
- package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
- package/dist/web/assets/PluginsPanel-Cm2N3StG.js +1 -0
- package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
- package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
- package/dist/web/assets/ProjectList-CsjnsNPv.js +1 -0
- package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
- package/dist/web/assets/PromptsDrawer-EGd3djFa.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
- package/dist/web/assets/RecentSessionsDrawer-D1vtoLAs.js +1 -0
- package/dist/web/assets/SessionCard-BgLg49U3.js +1 -0
- package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
- package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
- package/dist/web/assets/SessionList-TRiTklWk.js +1 -0
- package/dist/web/assets/SettingsDrawer-d5liXjHq.css +1 -0
- package/dist/web/assets/SettingsDrawer-rz2WejGY.js +1 -0
- package/dist/web/assets/SkillManager-BTjTUbHq.js +1 -0
- package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
- package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
- package/dist/web/assets/SkillsDrawer-pN0hbkK3.js +1 -0
- package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
- package/dist/web/assets/SkillsPanel-gmBWEoiF.js +1 -0
- package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
- package/dist/web/assets/SpeedTestDrawer-DcNzuVJk.js +1 -0
- package/dist/web/assets/Upload-v5rV0qTT.js +1 -0
- package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
- package/dist/web/assets/WorkspaceDrawer-DxmicM4U.js +1 -0
- package/dist/web/assets/WorkspaceManager-CJHCjARN.js +1 -0
- package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
- package/dist/web/assets/WorkspacePanel-D8_Zq-du.js +1 -0
- package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
- package/dist/web/assets/client-UDwXBP9i.js +1 -0
- package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
- package/dist/web/assets/headers-cf-QapUq.js +1 -0
- package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
- package/dist/web/assets/index-C0EHRym0.css +1 -0
- package/dist/web/assets/index-DnEocV0M.js +2 -0
- package/dist/web/assets/light-Cp0t7v32.js +1 -0
- package/dist/web/assets/markdown-u082bbtj.js +1 -0
- package/dist/web/assets/platforms-CBkB45JP.js +1 -0
- package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
- package/dist/web/assets/project-config-B2baPzYL.js +1 -0
- package/dist/web/assets/sessions-Cmy31slD.js +1 -0
- package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
- package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
- package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
- package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
- package/dist/web/assets/useResponsiveDrawer-B3uojyvV.js +1 -0
- package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
- package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
- package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
- package/dist/web/assets/xml-CgY3RMlX.js +1 -0
- package/dist/web/index.html +12 -8
- package/docs/native-log-memory-2026-09-16.md +43 -0
- package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
- package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
- package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
- package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
- package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
- package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
- package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
- package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
- package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
- package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
- package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
- package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
- package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
- package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
- package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
- package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
- package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
- package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
- package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
- package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
- package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
- package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
- package/package.json +8 -4
- package/src/commands/channels.js +88 -191
- package/src/commands/cli-type.js +19 -11
- package/src/commands/daemon.js +94 -32
- package/src/commands/doctor.js +16 -9
- package/src/commands/export-config.js +23 -5
- package/src/commands/list.js +130 -52
- package/src/commands/logs.js +44 -27
- package/src/commands/platform-command-registry.js +78 -0
- package/src/commands/plugin.js +11 -5
- package/src/commands/port-config.js +34 -52
- package/src/commands/proxy-control.js +207 -145
- package/src/commands/proxy.js +2 -2
- package/src/commands/resume.js +87 -91
- package/src/commands/search.js +137 -67
- package/src/commands/stats.js +30 -22
- package/src/commands/switch.js +1 -1
- package/src/commands/toggle-proxy.js +94 -111
- package/src/commands/ui.js +67 -6
- package/src/commands/workspace.js +90 -8
- package/src/config/default.js +6 -53
- package/src/config/loader.js +61 -24
- package/src/config/model-metadata.js +49 -59
- package/src/config/model-metadata.json +4095 -99
- package/src/config/paths.js +181 -8
- package/src/index.js +134 -71
- package/src/platforms/access.js +196 -0
- package/src/{server/services → platforms}/agents-service.js +354 -197
- package/src/platforms/channel-auth-registry.js +162 -0
- package/src/platforms/channel-auth-service.js +82 -0
- package/src/{server/services → platforms}/commands-service.js +195 -115
- package/src/platforms/driver-registry.js +59 -0
- package/src/platforms/drivers/claude/api-operations.js +62 -0
- package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
- package/src/platforms/drivers/claude/channels.js +39 -0
- package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
- package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
- package/src/platforms/drivers/claude/health.js +22 -0
- package/src/platforms/drivers/claude/mcp.js +9 -0
- package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
- package/src/platforms/drivers/claude/native-config.js +46 -0
- package/src/platforms/drivers/claude/native-logs.js +72 -0
- package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
- package/src/platforms/drivers/claude/project-config.js +21 -0
- package/src/platforms/drivers/claude/projects.js +62 -0
- package/src/platforms/drivers/claude/prompts.js +9 -0
- package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
- package/src/platforms/drivers/claude/proxy.js +16 -0
- package/src/platforms/drivers/claude/resource-sync.js +9 -0
- package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
- package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
- package/src/platforms/drivers/claude/sessions.js +76 -0
- package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/claude/statistics.js +16 -0
- package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
- package/src/platforms/drivers/codex/api-operations.js +9 -0
- package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
- package/src/platforms/drivers/codex/channels.js +45 -0
- package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
- package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
- package/src/platforms/drivers/codex/mcp.js +9 -0
- package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
- package/src/platforms/drivers/codex/native-config.js +47 -0
- package/src/platforms/drivers/codex/native-logs.js +166 -0
- package/src/platforms/drivers/codex/project-config.js +21 -0
- package/src/platforms/drivers/codex/projects.js +26 -0
- package/src/platforms/drivers/codex/prompts.js +9 -0
- package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
- package/src/platforms/drivers/codex/proxy.js +16 -0
- package/src/platforms/drivers/codex/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
- package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
- package/src/platforms/drivers/codex/sessions.js +36 -0
- package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/codex/statistics.js +16 -0
- package/src/platforms/drivers/gemini/api-operations.js +9 -0
- package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
- package/src/platforms/drivers/gemini/channels.js +39 -0
- package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
- package/src/platforms/drivers/gemini/mcp.js +9 -0
- package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
- package/src/platforms/drivers/gemini/native-config.js +39 -0
- package/src/platforms/drivers/gemini/native-logs.js +180 -0
- package/src/platforms/drivers/gemini/project-config.js +21 -0
- package/src/platforms/drivers/gemini/projects.js +26 -0
- package/src/platforms/drivers/gemini/prompts.js +9 -0
- package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
- package/src/platforms/drivers/gemini/proxy.js +16 -0
- package/src/platforms/drivers/gemini/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
- package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
- package/src/platforms/drivers/gemini/sessions.js +47 -0
- package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/gemini/statistics.js +16 -0
- package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
- package/src/platforms/drivers/generic-filesystem.js +166 -0
- package/src/platforms/drivers/generic-jsonl.js +141 -0
- package/src/platforms/drivers/generic-mcp.js +44 -0
- package/src/platforms/drivers/generic-openai-compatible.js +45 -0
- package/src/platforms/drivers/generic-prompt.js +41 -0
- package/src/platforms/drivers/legacy.js +677 -0
- package/src/platforms/drivers/native-config-snapshot.js +125 -0
- package/src/platforms/drivers/native-log-utils.js +1292 -0
- package/src/platforms/drivers/notification-hooks.js +578 -0
- package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
- package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
- package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
- package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
- package/src/platforms/drivers/omp/channels.js +58 -0
- package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
- package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
- package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
- package/src/platforms/drivers/omp/mcp.js +9 -0
- package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
- package/src/platforms/drivers/omp/native-config.js +46 -0
- package/src/platforms/drivers/omp/native-logs.js +52 -0
- package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
- package/src/platforms/drivers/omp/project-config.js +29 -0
- package/src/platforms/drivers/omp/projects.js +20 -0
- package/src/platforms/drivers/omp/prompts.js +9 -0
- package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
- package/src/platforms/drivers/omp/proxy.js +16 -0
- package/src/platforms/drivers/omp/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
- package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
- package/src/platforms/drivers/omp/sessions.js +36 -0
- package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
- package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
- package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
- package/src/platforms/drivers/omp/statistics.js +16 -0
- package/src/platforms/drivers/opencode/api-operations.js +9 -0
- package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
- package/src/platforms/drivers/opencode/channels.js +40 -0
- package/src/platforms/drivers/opencode/conversion.js +41 -0
- package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
- package/src/platforms/drivers/opencode/mcp.js +9 -0
- package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
- package/src/platforms/drivers/opencode/native-config.js +26 -0
- package/src/platforms/drivers/opencode/native-logs.js +133 -0
- package/src/platforms/drivers/opencode/project-config.js +98 -0
- package/src/platforms/drivers/opencode/projects.js +20 -0
- package/src/platforms/drivers/opencode/prompts.js +9 -0
- package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
- package/src/platforms/drivers/opencode/proxy.js +16 -0
- package/src/platforms/drivers/opencode/resource-sync.js +9 -0
- package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
- package/src/platforms/drivers/opencode/sessions.js +48 -0
- package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/opencode/statistics.js +16 -0
- package/src/platforms/drivers/secure-file-driver.js +272 -0
- package/src/platforms/drivers/unsupported.js +7 -0
- package/src/platforms/manifest-schema.js +398 -0
- package/src/platforms/manifests/claude.json +489 -0
- package/src/platforms/manifests/codex.json +448 -0
- package/src/platforms/manifests/gemini.json +447 -0
- package/src/platforms/manifests/omp.json +496 -0
- package/src/platforms/manifests/opencode.json +422 -0
- package/src/{server/services → platforms}/native-oauth-adapters.js +87 -417
- package/src/{server/services → platforms}/notification-hooks.js +140 -1023
- package/src/platforms/oauth-credentials-service.js +506 -0
- package/src/platforms/path-resolver.js +179 -0
- package/src/platforms/platform-path-context.js +305 -0
- package/src/platforms/registry.js +248 -0
- package/src/platforms/remote-notification-providers.js +678 -0
- package/src/platforms/runtime.js +259 -0
- package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
- package/src/plugins/plugin-loader.js +2 -2
- package/src/plugins/plugin-manager.js +2 -2
- package/src/plugins/registry.js +27 -10
- package/src/reset-config.js +21 -5
- package/src/server/api/agents.js +46 -72
- package/src/server/api/commands.js +16 -6
- package/src/server/api/config-registry.js +206 -59
- package/src/server/api/config-sync.js +3 -2
- package/src/server/api/config-templates.js +20 -4
- package/src/server/api/config.js +111 -102
- package/src/server/api/convert.js +54 -178
- package/src/server/api/dashboard.js +168 -102
- package/src/server/api/hooks.js +1 -1
- package/src/server/api/mcp.js +224 -84
- package/src/server/api/platform-api-config.js +102 -0
- package/src/server/api/platform-route-factory.js +215 -0
- package/src/server/api/platform-routes.js +26 -0
- package/src/server/api/platforms.js +110 -0
- package/src/server/api/plugins.js +57 -57
- package/src/server/api/project-config.js +168 -0
- package/src/server/api/prompts.js +58 -45
- package/src/server/api/settings.js +55 -8
- package/src/server/api/skills.js +262 -133
- package/src/server/api/ui-config.js +2 -2
- package/src/server/api/validation-errors.js +18 -4
- package/src/server/api/workspaces.js +47 -12
- package/src/server/index.js +164 -213
- package/src/server/platform-context.js +81 -0
- package/src/server/services/channel-balance.js +11 -20
- package/src/server/services/channel-health.js +21 -6
- package/src/server/services/channel-scheduler.js +29 -39
- package/src/server/services/channel-sync-utils.js +23 -13
- package/src/server/services/config-export-service.js +350 -319
- package/src/server/services/config-registry-service.js +280 -54
- package/src/server/services/config-sync-manager.js +172 -944
- package/src/server/services/config-templates-service.js +419 -353
- package/src/server/services/control-manifest-store.js +206 -0
- package/src/server/services/dashboard-snapshot-worker.js +656 -100
- package/src/server/services/effective-control-service.js +557 -0
- package/src/server/services/local-resource-index.js +314 -0
- package/src/server/services/mcp-service.js +607 -977
- package/src/server/services/model-detector.js +15 -10
- package/src/server/services/native-log-channel-resolver.js +214 -0
- package/src/server/services/native-log-observer.js +291 -0
- package/src/server/services/network-access.js +1 -1
- package/src/server/services/platform-catalog.js +82 -0
- package/src/server/services/platform-resolution.js +32 -29
- package/src/server/services/plugins-service.js +376 -122
- package/src/server/services/project-config-service.js +550 -0
- package/src/server/services/project-path-validation.js +31 -0
- package/src/server/services/prompts-service.js +267 -233
- package/src/server/services/proxy-runtime.js +7 -4
- package/src/server/services/remote-credential-cache.js +43 -0
- package/src/server/services/repo-scanner-base.js +182 -75
- package/src/server/services/request-logger.js +7 -4
- package/src/server/services/session-history-index.js +780 -213
- package/src/server/services/session-history-worker.js +228 -33
- package/src/server/services/session-snapshot-worker.js +124 -34
- package/src/server/services/session-snapshots.js +4 -2
- package/src/server/services/skill-artifact-store.js +355 -0
- package/src/server/services/skill-format-adapters.js +65 -0
- package/src/server/services/skill-projection-service.js +323 -0
- package/src/server/services/skill-refresh-task-service.js +275 -0
- package/src/server/services/skill-service.js +2020 -677
- package/src/server/services/snapshot-cache.js +30 -8
- package/src/server/services/speed-test.js +8 -8
- package/src/server/services/sqlite-connection.js +19 -3
- package/src/server/services/statistics-service.js +240 -57
- package/src/server/services/ui-config.js +107 -76
- package/src/server/services/usage-log-utils.js +98 -0
- package/src/server/services/web-build.js +1 -1
- package/src/server/services/workspace-service.js +59 -29
- package/src/server/utils/pricing.js +10 -19
- package/src/server/websocket-server.js +153 -28
- package/src/{server/services/base → shared}/base-channel-service.js +156 -28
- package/src/shared/capability-driver.js +104 -0
- package/src/shared/channel-auth-policy.js +27 -0
- package/src/shared/channel-driver.js +161 -0
- package/src/shared/driver-factories/api.js +180 -0
- package/src/shared/driver-factories/mcp.js +244 -0
- package/src/shared/driver-factories/projects.js +121 -0
- package/src/shared/driver-factories/prompts.js +58 -0
- package/src/shared/driver-factories/resource-sync.js +375 -0
- package/src/shared/driver-result.js +78 -0
- package/src/shared/mcp-format.js +189 -0
- package/src/shared/platforms.js +104 -167
- package/src/shared/project-config.js +512 -0
- package/src/shared/proxy-driver.js +57 -0
- package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
- package/src/ui/menu.js +91 -67
- package/src/utils/port-helper.js +35 -19
- package/src/utils/session.js +1 -1
- package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
- package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
- package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
- package/dist/web/assets/Home-DCEyLzwX.js +0 -1
- package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
- package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
- package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
- package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
- package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
- package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
- package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
- package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
- package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
- package/dist/web/assets/icons-nGdSKQn2.js +0 -1
- package/dist/web/assets/index-D5hyHPj7.js +0 -2
- package/dist/web/assets/index-bJRG3RXH.css +0 -1
- package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
- package/dist/web/assets/markdown-BfC0goYb.css +0 -10
- package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
- package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
- package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
- package/src/config/model-pricing.js +0 -35
- package/src/server/api/channels.js +0 -439
- package/src/server/api/claude-hooks.js +0 -56
- package/src/server/api/claude-statistics.js +0 -57
- package/src/server/api/codex-channels.js +0 -491
- package/src/server/api/codex-projects.js +0 -117
- package/src/server/api/codex-proxy.js +0 -293
- package/src/server/api/codex-sessions.js +0 -652
- package/src/server/api/codex-statistics.js +0 -57
- package/src/server/api/gemini-channels.js +0 -392
- package/src/server/api/gemini-projects.js +0 -103
- package/src/server/api/gemini-proxy.js +0 -231
- package/src/server/api/gemini-sessions.js +0 -550
- package/src/server/api/gemini-statistics.js +0 -57
- package/src/server/api/health-check.js +0 -31
- package/src/server/api/oauth-credentials.js +0 -198
- package/src/server/api/omp-channels.js +0 -575
- package/src/server/api/omp-projects.js +0 -88
- package/src/server/api/omp-proxy.js +0 -125
- package/src/server/api/omp-sessions.js +0 -386
- package/src/server/api/omp-statistics.js +0 -40
- package/src/server/api/opencode-channels.js +0 -471
- package/src/server/api/opencode-projects.js +0 -110
- package/src/server/api/opencode-proxy.js +0 -354
- package/src/server/api/opencode-sessions.js +0 -466
- package/src/server/api/opencode-statistics.js +0 -57
- package/src/server/api/projects.js +0 -120
- package/src/server/api/proxy.js +0 -330
- package/src/server/api/sessions.js +0 -810
- package/src/server/services/oauth-credentials-service.js +0 -945
- package/src/server/services/omp-session-log-observer.js +0 -138
- package/src/server/services/opencode-gateway-adapters.js +0 -43
- package/src/server/services/project-snapshots.js +0 -59
- package/src/server/services/proxy-log-helper.js +0 -298
- package/src/server/services/session-cache.js +0 -127
- package/src/server/services/session-converter.js +0 -582
- package/src/server/services/session-history-adapters/claude.js +0 -235
- /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
- /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
- /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
- /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
- /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
- /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
- /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
- /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
|
@@ -1,47 +1,59 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
|
-
const http = require('http');
|
|
5
|
-
const https = require('https');
|
|
6
|
-
const crypto = require('crypto');
|
|
7
|
-
const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
8
4
|
const { execSync, execFileSync } = require('child_process');
|
|
9
|
-
const { PATHS, NATIVE_PATHS } = require('
|
|
10
|
-
const { loadUIConfig, saveUIConfig } = require('
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
5
|
+
const { PATHS, NATIVE_PATHS } = require('../config/paths');
|
|
6
|
+
const { loadUIConfig, saveUIConfig } = require('../server/services/ui-config');
|
|
7
|
+
const remoteProviderRegistry = require('./remote-notification-providers');
|
|
8
|
+
const {
|
|
9
|
+
getRemoteProviderTypes,
|
|
10
|
+
normalizeRemoteProvider,
|
|
11
|
+
normalizeRemoteNotificationsConfig,
|
|
12
|
+
validateRemoteProviderConfig,
|
|
13
|
+
validateRemoteNotifications,
|
|
14
|
+
sendRemoteProviderTest,
|
|
15
|
+
validateFeishuWebhookUrl
|
|
16
|
+
} = remoteProviderRegistry;
|
|
17
|
+
const { getPlatformCatalog } = require('../server/services/platform-catalog');
|
|
18
|
+
const notificationHooksDriver = require('./drivers/notification-hooks');
|
|
19
|
+
const {
|
|
20
|
+
buildClaudeCommand,
|
|
21
|
+
buildCodexNotifyCommand,
|
|
22
|
+
buildGeminiCommand,
|
|
23
|
+
buildOpenCodePluginContent,
|
|
24
|
+
buildOmpExtensionContent,
|
|
25
|
+
getOpenCodeManagedPluginPath,
|
|
26
|
+
getOmpManagedExtensionPath,
|
|
27
|
+
getClaudeHookStatus,
|
|
28
|
+
getCodexHookStatus,
|
|
29
|
+
getGeminiHookStatus,
|
|
30
|
+
getOpenCodeHookStatus,
|
|
31
|
+
getOmpHookStatus,
|
|
32
|
+
parseCodexNotificationStatus,
|
|
33
|
+
parseGeminiNotificationStatus,
|
|
34
|
+
parseOpenCodeNotificationStatus,
|
|
35
|
+
parseOmpNotificationStatus
|
|
36
|
+
} = notificationHooksDriver;
|
|
37
|
+
|
|
38
|
+
const MANAGED_HOOK_NAME = notificationHooksDriver.MANAGED_HOOK_NAME;
|
|
39
|
+
|
|
40
|
+
function getConfiguredNativePath(platform, name, fallback) {
|
|
41
|
+
try {
|
|
42
|
+
const { getPlatformContext } = require('../server/platform-context');
|
|
43
|
+
const pathContext = getPlatformContext().registry.resolvePathContext(platform);
|
|
44
|
+
if (pathContext?.customized && pathContext.native?.[name]) {
|
|
45
|
+
return pathContext.native[name];
|
|
46
|
+
}
|
|
47
|
+
} catch (_) {
|
|
48
|
+
// Keep legacy paths when notification helpers are used in isolation.
|
|
49
|
+
}
|
|
50
|
+
return fallback;
|
|
51
|
+
}
|
|
34
52
|
|
|
35
53
|
function normalizeType(type) {
|
|
36
54
|
return type === 'dialog' || type === 'browser' ? type : 'notification';
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
function createClientId(prefix = 'coding-tool') {
|
|
40
|
-
const randomPart = typeof crypto.randomUUID === 'function'
|
|
41
|
-
? crypto.randomUUID().replace(/-/g, '').slice(0, 16)
|
|
42
|
-
: crypto.randomBytes(8).toString('hex');
|
|
43
|
-
return `${prefix}-${randomPart}`;
|
|
44
|
-
}
|
|
45
57
|
|
|
46
58
|
function ensureParentDir(filePath) {
|
|
47
59
|
const dir = path.dirname(filePath);
|
|
@@ -69,96 +81,13 @@ function writeJsonFile(filePath, value) {
|
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
function readClaudeSettings() {
|
|
72
|
-
return readJsonFile(NATIVE_PATHS.claude.settings);
|
|
84
|
+
return readJsonFile(getConfiguredNativePath('claude', 'settings', NATIVE_PATHS.claude.settings));
|
|
73
85
|
}
|
|
74
86
|
|
|
75
87
|
function writeClaudeSettings(settings) {
|
|
76
|
-
writeJsonFile(NATIVE_PATHS.claude.settings, settings);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function trimString(value) {
|
|
80
|
-
return String(value || '').trim();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function normalizeRemoteProviderType(type) {
|
|
84
|
-
const value = trimString(type);
|
|
85
|
-
return REMOTE_PROVIDER_TYPES.includes(value) ? value : '';
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function createRemoteProviderId(type, index = 0) {
|
|
89
|
-
return `${type || 'provider'}-${Date.now().toString(36)}-${index}`;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function normalizeProviderConfig(type, config = {}) {
|
|
93
|
-
const source = config && typeof config === 'object' ? config : {};
|
|
94
|
-
switch (type) {
|
|
95
|
-
case 'wechatBot':
|
|
96
|
-
return {
|
|
97
|
-
tokenFile: trimString(source.tokenFile),
|
|
98
|
-
botToken: trimString(source.botToken),
|
|
99
|
-
targetUserId: trimString(source.targetUserId),
|
|
100
|
-
contextToken: trimString(source.contextToken)
|
|
101
|
-
};
|
|
102
|
-
case 'qqBot':
|
|
103
|
-
return {
|
|
104
|
-
endpoint: trimString(source.endpoint),
|
|
105
|
-
accessToken: trimString(source.accessToken),
|
|
106
|
-
targetType: source.targetType === 'group' ? 'group' : 'private',
|
|
107
|
-
targetId: trimString(source.targetId)
|
|
108
|
-
};
|
|
109
|
-
case 'feishuBot':
|
|
110
|
-
return {
|
|
111
|
-
webhookUrl: trimString(source.webhookUrl)
|
|
112
|
-
};
|
|
113
|
-
case 'wecomBot':
|
|
114
|
-
return {
|
|
115
|
-
webhookUrl: trimString(source.webhookUrl)
|
|
116
|
-
};
|
|
117
|
-
case 'dingtalkBot':
|
|
118
|
-
return {
|
|
119
|
-
mode: source.mode === 'app' ? 'app' : 'webhook',
|
|
120
|
-
webhookUrl: trimString(source.webhookUrl),
|
|
121
|
-
clientId: trimString(source.clientId),
|
|
122
|
-
clientSecret: trimString(source.clientSecret),
|
|
123
|
-
targetType: source.targetType === 'user' ? 'user' : 'group',
|
|
124
|
-
targetId: trimString(source.targetId)
|
|
125
|
-
};
|
|
126
|
-
case 'telegramBot':
|
|
127
|
-
return {
|
|
128
|
-
botToken: trimString(source.botToken),
|
|
129
|
-
chatId: trimString(source.chatId),
|
|
130
|
-
proxy: trimString(source.proxy)
|
|
131
|
-
};
|
|
132
|
-
default:
|
|
133
|
-
return {};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function normalizeRemoteProvider(provider = {}, index = 0) {
|
|
138
|
-
const type = normalizeRemoteProviderType(provider.type);
|
|
139
|
-
if (!type) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
id: trimString(provider.id) || createRemoteProviderId(type, index),
|
|
145
|
-
type,
|
|
146
|
-
name: trimString(provider.name) || REMOTE_PROVIDER_LABELS[type],
|
|
147
|
-
enabled: provider.enabled === true,
|
|
148
|
-
config: normalizeProviderConfig(type, provider.config)
|
|
149
|
-
};
|
|
88
|
+
writeJsonFile(getConfiguredNativePath('claude', 'settings', NATIVE_PATHS.claude.settings), settings);
|
|
150
89
|
}
|
|
151
90
|
|
|
152
|
-
function normalizeRemoteNotificationsConfig(remoteNotifications = {}) {
|
|
153
|
-
const source = remoteNotifications && typeof remoteNotifications === 'object' ? remoteNotifications : {};
|
|
154
|
-
const providers = Array.isArray(source.providers)
|
|
155
|
-
? source.providers.map((provider, index) => normalizeRemoteProvider(provider, index)).filter(Boolean)
|
|
156
|
-
: [];
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
providers
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
91
|
|
|
163
92
|
function getRemoteNotificationsConfig(uiConfig = loadUIConfig()) {
|
|
164
93
|
return normalizeRemoteNotificationsConfig(uiConfig.remoteNotifications);
|
|
@@ -190,115 +119,7 @@ function createValidationError(message) {
|
|
|
190
119
|
return error;
|
|
191
120
|
}
|
|
192
121
|
|
|
193
|
-
function validateFeishuWebhookUrl(webhookUrl) {
|
|
194
|
-
const value = String(webhookUrl || '').trim();
|
|
195
|
-
if (!value) {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
let urlObj;
|
|
200
|
-
try {
|
|
201
|
-
urlObj = new URL(value);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
throw createValidationError('飞书 Webhook URL 格式不正确');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (urlObj.protocol !== 'https:') {
|
|
207
|
-
throw createValidationError('飞书 Webhook 必须使用 HTTPS');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (urlObj.hostname !== 'open.feishu.cn') {
|
|
211
|
-
throw createValidationError('仅支持 open.feishu.cn 的飞书 Webhook');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return urlObj;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function validateHttpsUrl(value, label, options = {}) {
|
|
218
|
-
const raw = trimString(value);
|
|
219
|
-
if (!raw) {
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
let urlObj;
|
|
224
|
-
try {
|
|
225
|
-
urlObj = new URL(raw);
|
|
226
|
-
} catch (error) {
|
|
227
|
-
throw createValidationError(`${label} 格式不正确`);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (options.requireHttps !== false && urlObj.protocol !== 'https:') {
|
|
231
|
-
throw createValidationError(`${label} 必须使用 HTTPS`);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (options.hostname && urlObj.hostname !== options.hostname) {
|
|
235
|
-
throw createValidationError(`${label} 仅支持 ${options.hostname}`);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return urlObj;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function requireConfigValue(config, key, label) {
|
|
242
|
-
if (!trimString(config?.[key])) {
|
|
243
|
-
throw createValidationError(`请填写${label}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
122
|
|
|
247
|
-
function validateRemoteProviderConfig(provider = {}) {
|
|
248
|
-
const type = normalizeRemoteProviderType(provider.type);
|
|
249
|
-
const config = provider.config || {};
|
|
250
|
-
if (!type) {
|
|
251
|
-
throw createValidationError('远程通知渠道类型不正确');
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
switch (type) {
|
|
255
|
-
case 'wechatBot':
|
|
256
|
-
if (!trimString(config.botToken) && !trimString(config.tokenFile)) {
|
|
257
|
-
throw createValidationError('请填写微信 Bot Token 或 token.json 路径');
|
|
258
|
-
}
|
|
259
|
-
requireConfigValue(config, 'targetUserId', '微信接收用户 ID');
|
|
260
|
-
break;
|
|
261
|
-
case 'qqBot':
|
|
262
|
-
requireConfigValue(config, 'endpoint', 'QQ Bot HTTP 地址');
|
|
263
|
-
validateHttpsUrl(config.endpoint, 'QQ Bot HTTP 地址', { requireHttps: false });
|
|
264
|
-
requireConfigValue(config, 'targetId', 'QQ 接收对象 ID');
|
|
265
|
-
break;
|
|
266
|
-
case 'feishuBot':
|
|
267
|
-
validateFeishuWebhookUrl(config.webhookUrl);
|
|
268
|
-
requireConfigValue(config, 'webhookUrl', '飞书 Webhook URL');
|
|
269
|
-
break;
|
|
270
|
-
case 'wecomBot':
|
|
271
|
-
validateHttpsUrl(config.webhookUrl, '企业微信 Webhook URL');
|
|
272
|
-
requireConfigValue(config, 'webhookUrl', '企业微信 Webhook URL');
|
|
273
|
-
break;
|
|
274
|
-
case 'dingtalkBot':
|
|
275
|
-
if (config.mode === 'app') {
|
|
276
|
-
requireConfigValue(config, 'clientId', '钉钉 App Key');
|
|
277
|
-
requireConfigValue(config, 'clientSecret', '钉钉 App Secret');
|
|
278
|
-
requireConfigValue(config, 'targetId', '钉钉接收对象 ID');
|
|
279
|
-
} else {
|
|
280
|
-
validateHttpsUrl(config.webhookUrl, '钉钉 Webhook URL');
|
|
281
|
-
requireConfigValue(config, 'webhookUrl', '钉钉 Webhook URL');
|
|
282
|
-
}
|
|
283
|
-
break;
|
|
284
|
-
case 'telegramBot':
|
|
285
|
-
requireConfigValue(config, 'botToken', 'Telegram Bot Token');
|
|
286
|
-
requireConfigValue(config, 'chatId', 'Telegram Chat ID');
|
|
287
|
-
break;
|
|
288
|
-
default:
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function validateRemoteNotifications(remoteNotifications = {}) {
|
|
294
|
-
const remote = normalizeRemoteNotificationsConfig(remoteNotifications);
|
|
295
|
-
remote.providers.forEach((provider) => {
|
|
296
|
-
if (provider.enabled === true) {
|
|
297
|
-
validateRemoteProviderConfig(provider);
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
return remote;
|
|
301
|
-
}
|
|
302
123
|
|
|
303
124
|
function removeNotifyScript() {
|
|
304
125
|
if (fs.existsSync(PATHS.notifyHook)) {
|
|
@@ -333,148 +154,6 @@ function isManagedNotifyPath(input) {
|
|
|
333
154
|
return normalizedInput.includes('notify-hook.js') || (normalizedPath && normalizedInput.includes(normalizedPath));
|
|
334
155
|
}
|
|
335
156
|
|
|
336
|
-
function buildManagedArgs(source, type, extra = []) {
|
|
337
|
-
const mode = normalizeType(type);
|
|
338
|
-
return [
|
|
339
|
-
`--source=${source}`,
|
|
340
|
-
`--mode=${mode}`,
|
|
341
|
-
`--cc-notify-type=${mode}`,
|
|
342
|
-
...extra
|
|
343
|
-
];
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function quoteShellArg(value) {
|
|
347
|
-
const stringValue = String(value || '');
|
|
348
|
-
if (/^[A-Za-z0-9_./:=+-]+$/.test(stringValue)) {
|
|
349
|
-
return stringValue;
|
|
350
|
-
}
|
|
351
|
-
return `"${stringValue.replace(/"/g, '\\"')}"`;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function buildClaudeCommand(type) {
|
|
355
|
-
const mode = normalizeType(type);
|
|
356
|
-
const args = ['node', PATHS.notifyHook, `--source=claude`, `--mode=${mode}`, `--cc-notify-type=${mode}`];
|
|
357
|
-
return `${quoteShellArg(args[0])} ${quoteShellArg(args[1])} ${args.slice(2).map(quoteShellArg).join(' ')}`;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function buildCodexNotifyCommand(type) {
|
|
361
|
-
const mode = normalizeType(type);
|
|
362
|
-
return ['node', PATHS.notifyHook, `--source=codex`, `--mode=${mode}`, `--cc-notify-type=${mode}`];
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function buildGeminiCommand(type) {
|
|
366
|
-
const mode = normalizeType(type);
|
|
367
|
-
const args = ['node', PATHS.notifyHook, `--source=gemini`, `--mode=${mode}`, `--cc-notify-type=${mode}`];
|
|
368
|
-
return `${quoteShellArg(args[0])} ${quoteShellArg(args[1])} ${args.slice(2).map(quoteShellArg).join(' ')}`;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function getOpenCodeManagedPluginPath() {
|
|
372
|
-
return path.join(NATIVE_PATHS.opencode.config, 'plugins', MANAGED_OPENCODE_PLUGIN_FILE);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function getOmpManagedExtensionPath() {
|
|
376
|
-
const ompPaths = NATIVE_PATHS.omp || {};
|
|
377
|
-
const extensionsDir = ompPaths.extensions ||
|
|
378
|
-
path.join(ompPaths.dir || path.dirname(ompPaths.settings || PATHS.notifyHook), 'extensions');
|
|
379
|
-
return path.join(extensionsDir, MANAGED_OMP_EXTENSION_FILE);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function buildOpenCodePluginContent(type) {
|
|
383
|
-
const mode = normalizeType(type);
|
|
384
|
-
return `// Managed by Coding Tool. Do not edit manually.
|
|
385
|
-
// mode:${mode}
|
|
386
|
-
import { spawn } from 'node:child_process'
|
|
387
|
-
|
|
388
|
-
const SCRIPT_PATH = ${JSON.stringify(PATHS.notifyHook)}
|
|
389
|
-
const MODE = ${JSON.stringify(mode)}
|
|
390
|
-
|
|
391
|
-
function fire(eventType) {
|
|
392
|
-
try {
|
|
393
|
-
const child = spawn('node', [
|
|
394
|
-
SCRIPT_PATH,
|
|
395
|
-
'--source=opencode',
|
|
396
|
-
\`--mode=\${MODE}\`,
|
|
397
|
-
\`--cc-notify-type=\${MODE}\`,
|
|
398
|
-
\`--event-type=\${eventType}\`
|
|
399
|
-
], {
|
|
400
|
-
detached: true,
|
|
401
|
-
stdio: 'ignore',
|
|
402
|
-
windowsHide: true
|
|
403
|
-
})
|
|
404
|
-
child.unref()
|
|
405
|
-
} catch (error) {
|
|
406
|
-
// Ignore notification failures.
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
export const CodingToolNotifyPlugin = async () => ({
|
|
411
|
-
event: async ({ event }) => {
|
|
412
|
-
const eventType = event?.type
|
|
413
|
-
if (eventType === 'session.idle' || eventType === 'session.error') {
|
|
414
|
-
fire(eventType)
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
})
|
|
418
|
-
`;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
function buildOmpExtensionContent(type) {
|
|
422
|
-
const mode = normalizeType(type);
|
|
423
|
-
return `// Managed by Coding Tool. Do not edit manually.
|
|
424
|
-
// mode:${mode}
|
|
425
|
-
import { spawn } from 'node:child_process'
|
|
426
|
-
|
|
427
|
-
const SCRIPT_PATH = ${JSON.stringify(PATHS.notifyHook)}
|
|
428
|
-
const MODE = ${JSON.stringify(mode)}
|
|
429
|
-
let lastFireAt = 0
|
|
430
|
-
|
|
431
|
-
function shouldFire() {
|
|
432
|
-
const now = Date.now()
|
|
433
|
-
if (now - lastFireAt < 1000) {
|
|
434
|
-
return false
|
|
435
|
-
}
|
|
436
|
-
lastFireAt = now
|
|
437
|
-
return true
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function fire(eventType) {
|
|
441
|
-
if (!shouldFire()) {
|
|
442
|
-
return
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
try {
|
|
446
|
-
const child = spawn('node', [
|
|
447
|
-
SCRIPT_PATH,
|
|
448
|
-
'--source=omp',
|
|
449
|
-
\`--mode=\${MODE}\`,
|
|
450
|
-
\`--cc-notify-type=\${MODE}\`,
|
|
451
|
-
\`--event-type=\${eventType}\`
|
|
452
|
-
], {
|
|
453
|
-
detached: true,
|
|
454
|
-
stdio: 'ignore',
|
|
455
|
-
windowsHide: true
|
|
456
|
-
})
|
|
457
|
-
child.unref()
|
|
458
|
-
} catch (error) {
|
|
459
|
-
// Ignore notification failures.
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
export default async function CodingToolNotifyExtension(omp) {
|
|
464
|
-
const register = (eventName) => {
|
|
465
|
-
if (typeof omp?.on !== 'function') {
|
|
466
|
-
return
|
|
467
|
-
}
|
|
468
|
-
omp.on(eventName, async () => {
|
|
469
|
-
fire(eventName)
|
|
470
|
-
})
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
register('agent_settled')
|
|
474
|
-
register('turn_end')
|
|
475
|
-
}
|
|
476
|
-
`;
|
|
477
|
-
}
|
|
478
157
|
|
|
479
158
|
function escapeForAppleScript(value) {
|
|
480
159
|
return String(value || '').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
@@ -1382,365 +1061,62 @@ function writeNotifyScript(remoteNotifications = {}) {
|
|
|
1382
1061
|
fs.writeFileSync(PATHS.notifyHook, generateNotifyScript(remoteNotifications), { mode: 0o755 });
|
|
1383
1062
|
}
|
|
1384
1063
|
|
|
1385
|
-
function getClaudeHookStatus() {
|
|
1386
|
-
const settings = readClaudeSettings();
|
|
1387
|
-
const stopHooks = Array.isArray(settings?.hooks?.Stop) ? settings.hooks.Stop : [];
|
|
1388
|
-
let enabled = false;
|
|
1389
|
-
let external = false;
|
|
1390
|
-
let type = 'notification';
|
|
1391
|
-
|
|
1392
|
-
stopHooks.forEach((group) => {
|
|
1393
|
-
const hooks = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
1394
|
-
hooks.forEach((hook) => {
|
|
1395
|
-
const command = String(hook?.command || '');
|
|
1396
|
-
if (!command) return;
|
|
1397
|
-
|
|
1398
|
-
const isManaged = isManagedNotifyPath(command);
|
|
1399
|
-
if (isManaged) {
|
|
1400
|
-
enabled = true;
|
|
1401
|
-
type = parseManagedType(command) || type;
|
|
1402
|
-
} else {
|
|
1403
|
-
external = true;
|
|
1404
|
-
}
|
|
1405
|
-
});
|
|
1406
|
-
});
|
|
1407
|
-
|
|
1408
|
-
return {
|
|
1409
|
-
enabled,
|
|
1410
|
-
external,
|
|
1411
|
-
type,
|
|
1412
|
-
method: 'Stop Hook'
|
|
1413
|
-
};
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
function saveClaudeHook(enabled, type) {
|
|
1417
|
-
const settings = readClaudeSettings();
|
|
1418
|
-
const hooks = settings.hooks && typeof settings.hooks === 'object' ? { ...settings.hooks } : {};
|
|
1419
|
-
const currentGroups = Array.isArray(hooks.Stop) ? hooks.Stop : [];
|
|
1420
|
-
|
|
1421
|
-
const filteredGroups = currentGroups.map((group) => {
|
|
1422
|
-
const groupHooks = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
1423
|
-
const nextHooks = groupHooks.filter((hook) => !isManagedNotifyPath(hook?.command));
|
|
1424
|
-
if (nextHooks.length === 0) {
|
|
1425
|
-
return null;
|
|
1426
|
-
}
|
|
1427
|
-
return { ...group, hooks: nextHooks };
|
|
1428
|
-
}).filter(Boolean);
|
|
1429
|
-
|
|
1430
|
-
if (enabled) {
|
|
1431
|
-
filteredGroups.push({
|
|
1432
|
-
hooks: [
|
|
1433
|
-
{
|
|
1434
|
-
name: MANAGED_HOOK_NAME,
|
|
1435
|
-
type: 'command',
|
|
1436
|
-
command: buildClaudeCommand(type)
|
|
1437
|
-
}
|
|
1438
|
-
]
|
|
1439
|
-
});
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
if (filteredGroups.length > 0) {
|
|
1443
|
-
hooks.Stop = filteredGroups;
|
|
1444
|
-
} else {
|
|
1445
|
-
delete hooks.Stop;
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
if (Object.keys(hooks).length > 0) {
|
|
1449
|
-
settings.hooks = hooks;
|
|
1450
|
-
} else {
|
|
1451
|
-
delete settings.hooks;
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
writeClaudeSettings(settings);
|
|
1455
|
-
}
|
|
1456
1064
|
|
|
1457
|
-
function
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
function parseCodexNotificationStatus(config = {}) {
|
|
1473
|
-
const notify = Array.isArray(config?.notify) ? config.notify : [];
|
|
1474
|
-
|
|
1475
|
-
if (notify.length === 0) {
|
|
1476
|
-
return {
|
|
1477
|
-
enabled: false,
|
|
1478
|
-
external: false,
|
|
1479
|
-
type: 'notification',
|
|
1480
|
-
method: 'notify'
|
|
1481
|
-
};
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
const joined = notify.join(' ');
|
|
1485
|
-
const managed = isManagedCodexNotify(notify);
|
|
1486
|
-
|
|
1487
|
-
return {
|
|
1488
|
-
enabled: managed,
|
|
1489
|
-
external: !managed,
|
|
1490
|
-
type: parseManagedType(joined) || 'notification',
|
|
1491
|
-
method: 'notify'
|
|
1492
|
-
};
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
function getCodexHookStatus() {
|
|
1496
|
-
return parseCodexNotificationStatus(safeReadCodexConfig());
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
function saveCodexHook(enabled, type) {
|
|
1500
|
-
const config = safeReadCodexConfig();
|
|
1501
|
-
const nextConfig = (config && typeof config === 'object') ? { ...config } : {};
|
|
1502
|
-
const managed = isManagedCodexNotify(nextConfig.notify);
|
|
1503
|
-
|
|
1504
|
-
if (enabled) {
|
|
1505
|
-
nextConfig.notify = buildCodexNotifyCommand(type);
|
|
1506
|
-
} else if (managed) {
|
|
1507
|
-
delete nextConfig.notify;
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
ensureParentDir(NATIVE_PATHS.codex.config);
|
|
1511
|
-
codexSettingsManager.writeConfig(nextConfig);
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
function safeReadGeminiSettings() {
|
|
1515
|
-
try {
|
|
1516
|
-
if (typeof geminiSettingsManager.settingsExists === 'function' && !geminiSettingsManager.settingsExists()) {
|
|
1517
|
-
return {};
|
|
1065
|
+
function getHookPlatformEntries(catalog = getPlatformCatalog()) {
|
|
1066
|
+
const manifests = typeof catalog?.list === 'function'
|
|
1067
|
+
? catalog.list({ capability: 'hooks' })
|
|
1068
|
+
: [];
|
|
1069
|
+
const entries = [];
|
|
1070
|
+
for (const manifest of manifests) {
|
|
1071
|
+
const driver = typeof catalog.driver === 'function'
|
|
1072
|
+
? catalog.driver(manifest.key, 'hooks')
|
|
1073
|
+
: null;
|
|
1074
|
+
if (!driver
|
|
1075
|
+
|| typeof driver.getHooks !== 'function'
|
|
1076
|
+
|| typeof driver.saveHooks !== 'function'
|
|
1077
|
+
|| typeof driver.testHooks !== 'function'
|
|
1078
|
+
|| typeof driver.getDefinition !== 'function') {
|
|
1079
|
+
continue;
|
|
1518
1080
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1081
|
+
const definition = driver.getDefinition();
|
|
1082
|
+
if (!definition || typeof definition !== 'object') continue;
|
|
1083
|
+
entries.push({ manifest, driver, definition });
|
|
1522
1084
|
}
|
|
1085
|
+
return entries;
|
|
1523
1086
|
}
|
|
1524
1087
|
|
|
1525
|
-
function
|
|
1526
|
-
const command = String(hook?.command || '');
|
|
1527
|
-
const name = String(hook?.name || '');
|
|
1528
|
-
return name === MANAGED_HOOK_NAME || isManagedNotifyPath(command);
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
function parseGeminiNotificationStatus(settings = {}) {
|
|
1532
|
-
const afterAgentGroups = Array.isArray(settings?.hooks?.AfterAgent) ? settings.hooks.AfterAgent : [];
|
|
1533
|
-
let enabled = false;
|
|
1534
|
-
let external = false;
|
|
1535
|
-
let type = 'notification';
|
|
1536
|
-
|
|
1537
|
-
afterAgentGroups.forEach((group) => {
|
|
1538
|
-
const hooks = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
1539
|
-
hooks.forEach((hook) => {
|
|
1540
|
-
if (isManagedGeminiHook(hook)) {
|
|
1541
|
-
enabled = true;
|
|
1542
|
-
type = parseManagedType(hook.command) || type;
|
|
1543
|
-
} else {
|
|
1544
|
-
external = true;
|
|
1545
|
-
}
|
|
1546
|
-
});
|
|
1547
|
-
});
|
|
1548
|
-
|
|
1088
|
+
function toPublicHookDefinition({ manifest, definition }) {
|
|
1549
1089
|
return {
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1090
|
+
key: manifest.key,
|
|
1091
|
+
label: manifest.label || definition.label || manifest.key,
|
|
1092
|
+
description: typeof definition.description === 'string' ? definition.description : '',
|
|
1093
|
+
implementation: typeof definition.implementation === 'string' ? definition.implementation : '',
|
|
1094
|
+
externalMessage: typeof definition.externalMessage === 'string' ? definition.externalMessage : '',
|
|
1095
|
+
hints: Array.isArray(definition.hints) ? [...definition.hints] : []
|
|
1554
1096
|
};
|
|
1555
1097
|
}
|
|
1556
1098
|
|
|
1557
|
-
function
|
|
1558
|
-
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
function saveGeminiHook(enabled, type) {
|
|
1562
|
-
const settings = safeReadGeminiSettings();
|
|
1563
|
-
const nextSettings = (settings && typeof settings === 'object') ? { ...settings } : {};
|
|
1564
|
-
const hooks = nextSettings.hooks && typeof nextSettings.hooks === 'object' ? { ...nextSettings.hooks } : {};
|
|
1565
|
-
const currentGroups = Array.isArray(hooks.AfterAgent) ? hooks.AfterAgent : [];
|
|
1566
|
-
|
|
1567
|
-
const filteredGroups = currentGroups.map((group) => {
|
|
1568
|
-
const groupHooks = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
1569
|
-
const nextHooks = groupHooks.filter((hook) => !isManagedGeminiHook(hook));
|
|
1570
|
-
if (nextHooks.length === 0) {
|
|
1571
|
-
return null;
|
|
1572
|
-
}
|
|
1573
|
-
return { ...group, hooks: nextHooks };
|
|
1574
|
-
}).filter(Boolean);
|
|
1575
|
-
|
|
1576
|
-
if (enabled) {
|
|
1577
|
-
filteredGroups.push({
|
|
1578
|
-
matcher: '*',
|
|
1579
|
-
hooks: [
|
|
1580
|
-
{
|
|
1581
|
-
name: MANAGED_HOOK_NAME,
|
|
1582
|
-
type: 'command',
|
|
1583
|
-
command: buildGeminiCommand(type)
|
|
1584
|
-
}
|
|
1585
|
-
]
|
|
1586
|
-
});
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
if (filteredGroups.length > 0) {
|
|
1590
|
-
hooks.AfterAgent = filteredGroups;
|
|
1591
|
-
} else {
|
|
1592
|
-
delete hooks.AfterAgent;
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
if (Object.keys(hooks).length > 0) {
|
|
1596
|
-
nextSettings.hooks = hooks;
|
|
1597
|
-
} else {
|
|
1598
|
-
delete nextSettings.hooks;
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
ensureParentDir(geminiSettingsManager.getSettingsPath());
|
|
1602
|
-
geminiSettingsManager.writeSettings(nextSettings);
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
function parseOpenCodeNotificationStatus(content = '') {
|
|
1606
|
-
if (!content) {
|
|
1607
|
-
return {
|
|
1608
|
-
enabled: false,
|
|
1609
|
-
external: false,
|
|
1610
|
-
type: 'notification',
|
|
1611
|
-
method: 'Plugin Events'
|
|
1612
|
-
};
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
return {
|
|
1616
|
-
enabled: true,
|
|
1617
|
-
external: false,
|
|
1618
|
-
type: parseManagedType(content) || 'notification',
|
|
1619
|
-
method: 'Plugin Events'
|
|
1620
|
-
};
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
function parseOmpNotificationStatus(content = '') {
|
|
1624
|
-
if (!content) {
|
|
1625
|
-
return {
|
|
1626
|
-
enabled: false,
|
|
1627
|
-
external: false,
|
|
1628
|
-
type: 'notification',
|
|
1629
|
-
method: 'Extension Events'
|
|
1630
|
-
};
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
return {
|
|
1634
|
-
enabled: true,
|
|
1635
|
-
external: false,
|
|
1636
|
-
type: parseManagedType(content) || 'notification',
|
|
1637
|
-
method: 'Extension Events'
|
|
1638
|
-
};
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
function getOpenCodeHookStatus() {
|
|
1642
|
-
const pluginPath = getOpenCodeManagedPluginPath();
|
|
1643
|
-
if (!fs.existsSync(pluginPath)) {
|
|
1644
|
-
return parseOpenCodeNotificationStatus('');
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
return parseOpenCodeNotificationStatus(fs.readFileSync(pluginPath, 'utf8'));
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
function getOmpHookStatus() {
|
|
1651
|
-
const extensionPath = getOmpManagedExtensionPath();
|
|
1652
|
-
if (!fs.existsSync(extensionPath)) {
|
|
1653
|
-
return parseOmpNotificationStatus('');
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
return parseOmpNotificationStatus(fs.readFileSync(extensionPath, 'utf8'));
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
function saveOpenCodeHook(enabled, type) {
|
|
1660
|
-
const pluginPath = getOpenCodeManagedPluginPath();
|
|
1661
|
-
const opencodeSettingsManager = require('./opencode-settings-manager');
|
|
1662
|
-
const configPath = opencodeSettingsManager.selectConfigPath();
|
|
1663
|
-
|
|
1664
|
-
if (!enabled) {
|
|
1665
|
-
// Remove plugin file
|
|
1666
|
-
if (fs.existsSync(pluginPath)) {
|
|
1667
|
-
fs.unlinkSync(pluginPath);
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
// Remove from opencode.json plugins array
|
|
1671
|
-
if (fs.existsSync(configPath)) {
|
|
1672
|
-
try {
|
|
1673
|
-
const config = opencodeSettingsManager.readConfig(configPath);
|
|
1674
|
-
if (Array.isArray(config.plugins)) {
|
|
1675
|
-
config.plugins = config.plugins.filter(p => p !== './plugins/coding-tool-notify.js');
|
|
1676
|
-
if (config.plugins.length === 0) {
|
|
1677
|
-
delete config.plugins;
|
|
1678
|
-
}
|
|
1679
|
-
opencodeSettingsManager.writeConfig(configPath, config);
|
|
1680
|
-
}
|
|
1681
|
-
} catch (error) {
|
|
1682
|
-
console.error('Failed to update opencode.json:', error);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
return;
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
// Create plugin file
|
|
1689
|
-
ensureParentDir(pluginPath);
|
|
1690
|
-
fs.writeFileSync(pluginPath, buildOpenCodePluginContent(type), 'utf8');
|
|
1691
|
-
|
|
1692
|
-
// Add to opencode.json plugins array
|
|
1693
|
-
try {
|
|
1694
|
-
const config = fs.existsSync(configPath)
|
|
1695
|
-
? opencodeSettingsManager.readConfig(configPath)
|
|
1696
|
-
: {};
|
|
1697
|
-
|
|
1698
|
-
if (!Array.isArray(config.plugins)) {
|
|
1699
|
-
config.plugins = [];
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
|
-
const pluginRef = './plugins/coding-tool-notify.js';
|
|
1703
|
-
if (!config.plugins.includes(pluginRef)) {
|
|
1704
|
-
config.plugins.push(pluginRef);
|
|
1705
|
-
}
|
|
1706
|
-
|
|
1707
|
-
opencodeSettingsManager.writeConfig(configPath, config);
|
|
1708
|
-
} catch (error) {
|
|
1709
|
-
console.error('Failed to update opencode.json:', error);
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
function saveOmpHook(enabled, type) {
|
|
1714
|
-
const extensionPath = getOmpManagedExtensionPath();
|
|
1715
|
-
|
|
1716
|
-
if (!enabled) {
|
|
1717
|
-
if (fs.existsSync(extensionPath)) {
|
|
1718
|
-
fs.unlinkSync(extensionPath);
|
|
1719
|
-
}
|
|
1720
|
-
return;
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
ensureParentDir(extensionPath);
|
|
1724
|
-
fs.writeFileSync(extensionPath, buildOmpExtensionContent(type), 'utf8');
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
function getNotificationSettings() {
|
|
1099
|
+
function getNotificationSettings({ catalog } = {}) {
|
|
1100
|
+
const entries = getHookPlatformEntries(catalog);
|
|
1728
1101
|
const uiConfig = loadUIConfig();
|
|
1729
1102
|
const remoteNotifications = getRemoteNotificationsConfig(uiConfig);
|
|
1103
|
+
const platforms = {};
|
|
1104
|
+
const platformDefinitions = [];
|
|
1105
|
+
for (const entry of entries) {
|
|
1106
|
+
platforms[entry.manifest.key] = entry.driver.getHooks();
|
|
1107
|
+
platformDefinitions.push(toPublicHookDefinition(entry));
|
|
1108
|
+
}
|
|
1109
|
+
const claudeStatus = platforms.claude || { enabled: false, type: 'notification' };
|
|
1730
1110
|
return {
|
|
1731
1111
|
success: true,
|
|
1732
1112
|
platform: os.platform(),
|
|
1733
1113
|
remoteNotifications,
|
|
1734
|
-
remoteProviderTypes:
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
codex: getCodexHookStatus(),
|
|
1741
|
-
gemini: getGeminiHookStatus(),
|
|
1742
|
-
opencode: getOpenCodeHookStatus(),
|
|
1743
|
-
omp: getOmpHookStatus()
|
|
1114
|
+
remoteProviderTypes: getRemoteProviderTypes(),
|
|
1115
|
+
platformDefinitions,
|
|
1116
|
+
platforms,
|
|
1117
|
+
stopHook: {
|
|
1118
|
+
enabled: claudeStatus.enabled === true,
|
|
1119
|
+
type: normalizeType(claudeStatus.type)
|
|
1744
1120
|
}
|
|
1745
1121
|
};
|
|
1746
1122
|
}
|
|
@@ -1800,43 +1176,44 @@ function emitBrowserNotification(input = {}) {
|
|
|
1800
1176
|
timestamp: Date.now()
|
|
1801
1177
|
};
|
|
1802
1178
|
|
|
1803
|
-
const { broadcastBrowserNotification } = require('../websocket-server');
|
|
1179
|
+
const { broadcastBrowserNotification } = require('../server/websocket-server');
|
|
1804
1180
|
broadcastBrowserNotification(payload);
|
|
1805
1181
|
return payload;
|
|
1806
1182
|
}
|
|
1807
|
-
|
|
1808
|
-
function saveNotificationSettings(input = {}) {
|
|
1183
|
+
function saveNotificationSettings(input = {}, { catalog } = {}) {
|
|
1809
1184
|
const remoteNotifications = validateRemoteNotifications(
|
|
1810
1185
|
input.remoteNotifications !== undefined
|
|
1811
1186
|
? input.remoteNotifications
|
|
1812
1187
|
: {}
|
|
1813
1188
|
);
|
|
1814
|
-
const
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1189
|
+
const entries = getHookPlatformEntries(catalog);
|
|
1190
|
+
const inputPlatforms = input.platforms && typeof input.platforms === 'object' && !Array.isArray(input.platforms)
|
|
1191
|
+
? input.platforms
|
|
1192
|
+
: {};
|
|
1193
|
+
const platforms = Object.fromEntries(entries.map(entry => {
|
|
1194
|
+
const key = entry.manifest.key;
|
|
1195
|
+
const platformInput = Object.prototype.hasOwnProperty.call(inputPlatforms, key)
|
|
1196
|
+
? inputPlatforms[key]
|
|
1197
|
+
: Object.prototype.hasOwnProperty.call(input, key)
|
|
1198
|
+
? input[key]
|
|
1199
|
+
: key === 'claude'
|
|
1200
|
+
? input.stopHook
|
|
1201
|
+
: undefined;
|
|
1202
|
+
return [key, normalizePlatformInput(platformInput)];
|
|
1203
|
+
}));
|
|
1821
1204
|
|
|
1822
|
-
|
|
1205
|
+
const claudeEntry = entries.find(entry => entry.manifest.key === 'claude');
|
|
1206
|
+
saveNotificationUiConfig(remoteNotifications, claudeEntry ? platforms.claude.enabled : undefined);
|
|
1823
1207
|
|
|
1824
|
-
const hasManagedPlatform = Object.values(platforms).some(
|
|
1825
|
-
|
|
1826
|
-
|
|
1208
|
+
const hasManagedPlatform = Object.values(platforms).some(platform => platform.enabled);
|
|
1209
|
+
for (const entry of entries) {
|
|
1210
|
+
entry.driver.saveHooks(platforms[entry.manifest.key]);
|
|
1827
1211
|
}
|
|
1828
1212
|
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
saveGeminiHook(platforms.gemini.enabled, platforms.gemini.type);
|
|
1832
|
-
saveOpenCodeHook(platforms.opencode.enabled, platforms.opencode.type);
|
|
1833
|
-
saveOmpHook(platforms.omp.enabled, platforms.omp.type);
|
|
1834
|
-
|
|
1835
|
-
if (!hasManagedPlatform) {
|
|
1836
|
-
removeNotifyScript();
|
|
1837
|
-
}
|
|
1213
|
+
if (hasManagedPlatform) writeNotifyScript(remoteNotifications);
|
|
1214
|
+
else removeNotifyScript();
|
|
1838
1215
|
|
|
1839
|
-
return getNotificationSettings();
|
|
1216
|
+
return getNotificationSettings({ catalog });
|
|
1840
1217
|
}
|
|
1841
1218
|
|
|
1842
1219
|
function parseNotifyTypeMarker(command) {
|
|
@@ -1950,20 +1327,19 @@ function normalizeSavedPlatformStatus(platform = {}) {
|
|
|
1950
1327
|
}
|
|
1951
1328
|
|
|
1952
1329
|
function buildLegacyClaudeSaveInput(input = {}, currentSettings = getNotificationSettings()) {
|
|
1330
|
+
const platforms = {
|
|
1331
|
+
...(currentSettings?.platforms && typeof currentSettings.platforms === 'object'
|
|
1332
|
+
? currentSettings.platforms
|
|
1333
|
+
: {})
|
|
1334
|
+
};
|
|
1335
|
+
platforms.claude = input.stopHook !== undefined
|
|
1336
|
+
? normalizePlatformInput(input.stopHook)
|
|
1337
|
+
: { enabled: false, type: 'notification' };
|
|
1953
1338
|
return {
|
|
1954
|
-
platforms
|
|
1955
|
-
claude: input.stopHook !== undefined
|
|
1956
|
-
? normalizePlatformInput(input.stopHook)
|
|
1957
|
-
: { enabled: false, type: 'notification' },
|
|
1958
|
-
codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
|
|
1959
|
-
gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
|
|
1960
|
-
opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode),
|
|
1961
|
-
omp: normalizeSavedPlatformStatus(currentSettings?.platforms?.omp)
|
|
1962
|
-
},
|
|
1339
|
+
platforms,
|
|
1963
1340
|
remoteNotifications: currentSettings?.remoteNotifications || { providers: [] }
|
|
1964
1341
|
};
|
|
1965
1342
|
}
|
|
1966
|
-
|
|
1967
1343
|
function getLegacyClaudeHookSettings() {
|
|
1968
1344
|
return {
|
|
1969
1345
|
success: true,
|
|
@@ -1986,21 +1362,21 @@ function initDefaultHooks() {
|
|
|
1986
1362
|
return;
|
|
1987
1363
|
}
|
|
1988
1364
|
|
|
1365
|
+
const entries = getHookPlatformEntries();
|
|
1366
|
+
const claudeEntry = entries.find(entry => entry.manifest.key === 'claude');
|
|
1367
|
+
if (!claudeEntry) return;
|
|
1368
|
+
|
|
1989
1369
|
const currentClaudeSettings = readClaudeSettings();
|
|
1990
1370
|
const currentStatus = parseStopHookStatus(currentClaudeSettings);
|
|
1371
|
+
const currentSettings = getNotificationSettings();
|
|
1372
|
+
const platforms = { ...currentSettings.platforms };
|
|
1991
1373
|
|
|
1992
1374
|
if (currentStatus.enabled) {
|
|
1993
1375
|
if (shouldRepairStopHook(currentClaudeSettings)) {
|
|
1994
|
-
|
|
1376
|
+
platforms.claude = { enabled: true, type: currentStatus.type || 'notification' };
|
|
1995
1377
|
saveNotificationSettings({
|
|
1996
|
-
platforms
|
|
1997
|
-
|
|
1998
|
-
codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
|
|
1999
|
-
gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
|
|
2000
|
-
opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode),
|
|
2001
|
-
omp: normalizeSavedPlatformStatus(currentSettings?.platforms?.omp)
|
|
2002
|
-
},
|
|
2003
|
-
remoteNotifications: currentSettings?.remoteNotifications || { providers: [] }
|
|
1378
|
+
platforms,
|
|
1379
|
+
remoteNotifications: currentSettings.remoteNotifications || { providers: [] }
|
|
2004
1380
|
});
|
|
2005
1381
|
console.log('[Claude Hooks] 检测到旧版 Stop hook 路径,已自动修复');
|
|
2006
1382
|
} else {
|
|
@@ -2009,16 +1385,10 @@ function initDefaultHooks() {
|
|
|
2009
1385
|
return;
|
|
2010
1386
|
}
|
|
2011
1387
|
|
|
2012
|
-
|
|
1388
|
+
platforms.claude = { enabled: true, type: 'notification' };
|
|
2013
1389
|
saveNotificationSettings({
|
|
2014
|
-
platforms
|
|
2015
|
-
|
|
2016
|
-
codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
|
|
2017
|
-
gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
|
|
2018
|
-
opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode),
|
|
2019
|
-
omp: normalizeSavedPlatformStatus(currentSettings?.platforms?.omp)
|
|
2020
|
-
},
|
|
2021
|
-
remoteNotifications: currentSettings?.remoteNotifications || { providers: [] }
|
|
1390
|
+
platforms,
|
|
1391
|
+
remoteNotifications: currentSettings.remoteNotifications || { providers: [] }
|
|
2022
1392
|
});
|
|
2023
1393
|
console.log('[Claude Hooks] 已自动开启任务完成通知(右上角卡片)');
|
|
2024
1394
|
} catch (error) {
|
|
@@ -2026,261 +1396,6 @@ function initDefaultHooks() {
|
|
|
2026
1396
|
}
|
|
2027
1397
|
}
|
|
2028
1398
|
|
|
2029
|
-
function sendFeishuTest(webhookUrl) {
|
|
2030
|
-
return new Promise((resolve, reject) => {
|
|
2031
|
-
try {
|
|
2032
|
-
const urlObj = validateFeishuWebhookUrl(webhookUrl);
|
|
2033
|
-
const data = JSON.stringify({
|
|
2034
|
-
msg_type: 'interactive',
|
|
2035
|
-
card: {
|
|
2036
|
-
header: {
|
|
2037
|
-
title: { tag: 'plain_text', content: 'Coding Tool - 测试通知' },
|
|
2038
|
-
template: 'blue'
|
|
2039
|
-
},
|
|
2040
|
-
elements: [
|
|
2041
|
-
{
|
|
2042
|
-
tag: 'div',
|
|
2043
|
-
text: { tag: 'lark_md', content: '**状态**: 这是一条测试通知' }
|
|
2044
|
-
},
|
|
2045
|
-
{
|
|
2046
|
-
tag: 'div',
|
|
2047
|
-
text: { tag: 'lark_md', content: '**时间**: ' + new Date().toLocaleString('zh-CN') }
|
|
2048
|
-
}
|
|
2049
|
-
]
|
|
2050
|
-
}
|
|
2051
|
-
});
|
|
2052
|
-
|
|
2053
|
-
const options = {
|
|
2054
|
-
hostname: urlObj.hostname,
|
|
2055
|
-
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
|
2056
|
-
path: urlObj.pathname + urlObj.search,
|
|
2057
|
-
method: 'POST',
|
|
2058
|
-
headers: {
|
|
2059
|
-
'Content-Type': 'application/json',
|
|
2060
|
-
'Content-Length': Buffer.byteLength(data)
|
|
2061
|
-
},
|
|
2062
|
-
timeout: 10000
|
|
2063
|
-
};
|
|
2064
|
-
|
|
2065
|
-
const requestModule = urlObj.protocol === 'https:' ? https : http;
|
|
2066
|
-
const request = requestModule.request(options, () => resolve());
|
|
2067
|
-
request.on('error', reject);
|
|
2068
|
-
request.on('timeout', () => {
|
|
2069
|
-
request.destroy(new Error('飞书测试通知超时'));
|
|
2070
|
-
});
|
|
2071
|
-
request.write(data);
|
|
2072
|
-
request.end();
|
|
2073
|
-
} catch (error) {
|
|
2074
|
-
reject(error);
|
|
2075
|
-
}
|
|
2076
|
-
});
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2079
|
-
function requestJson(url, data, headers = {}, extraOptions = {}) {
|
|
2080
|
-
return new Promise((resolve, reject) => {
|
|
2081
|
-
try {
|
|
2082
|
-
const urlObj = new URL(url);
|
|
2083
|
-
const body = JSON.stringify(data);
|
|
2084
|
-
const requestModule = urlObj.protocol === 'https:' ? https : http;
|
|
2085
|
-
const request = requestModule.request({
|
|
2086
|
-
hostname: urlObj.hostname,
|
|
2087
|
-
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
|
2088
|
-
path: urlObj.pathname + urlObj.search,
|
|
2089
|
-
method: 'POST',
|
|
2090
|
-
timeout: 10000,
|
|
2091
|
-
headers: {
|
|
2092
|
-
'Content-Type': 'application/json',
|
|
2093
|
-
'Content-Length': Buffer.byteLength(body),
|
|
2094
|
-
...headers
|
|
2095
|
-
},
|
|
2096
|
-
...extraOptions
|
|
2097
|
-
}, (response) => {
|
|
2098
|
-
response.resume();
|
|
2099
|
-
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
2100
|
-
resolve();
|
|
2101
|
-
} else {
|
|
2102
|
-
reject(new Error(`远程通知返回 HTTP ${response.statusCode}`));
|
|
2103
|
-
}
|
|
2104
|
-
});
|
|
2105
|
-
request.on('error', reject);
|
|
2106
|
-
request.on('timeout', () => {
|
|
2107
|
-
request.destroy(new Error('远程通知测试超时'));
|
|
2108
|
-
});
|
|
2109
|
-
request.write(body);
|
|
2110
|
-
request.end();
|
|
2111
|
-
} catch (error) {
|
|
2112
|
-
reject(error);
|
|
2113
|
-
}
|
|
2114
|
-
});
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
function requestDingTalkAccessToken(config = {}) {
|
|
2118
|
-
if (!config.clientId || !config.clientSecret) {
|
|
2119
|
-
return Promise.resolve('');
|
|
2120
|
-
}
|
|
2121
|
-
return new Promise((resolve, reject) => {
|
|
2122
|
-
try {
|
|
2123
|
-
const body = JSON.stringify({ appKey: config.clientId, appSecret: config.clientSecret });
|
|
2124
|
-
const request = https.request({
|
|
2125
|
-
hostname: 'api.dingtalk.com',
|
|
2126
|
-
path: '/v1.0/oauth2/accessToken',
|
|
2127
|
-
method: 'POST',
|
|
2128
|
-
timeout: 10000,
|
|
2129
|
-
headers: {
|
|
2130
|
-
'Content-Type': 'application/json',
|
|
2131
|
-
'Content-Length': Buffer.byteLength(body)
|
|
2132
|
-
}
|
|
2133
|
-
}, (response) => {
|
|
2134
|
-
let responseBody = '';
|
|
2135
|
-
response.setEncoding('utf8');
|
|
2136
|
-
response.on('data', (chunk) => { responseBody += chunk; });
|
|
2137
|
-
response.on('end', () => {
|
|
2138
|
-
try {
|
|
2139
|
-
const parsed = JSON.parse(responseBody);
|
|
2140
|
-
if (response.statusCode >= 200 && response.statusCode < 300 && parsed.accessToken) {
|
|
2141
|
-
resolve(parsed.accessToken);
|
|
2142
|
-
return;
|
|
2143
|
-
}
|
|
2144
|
-
reject(new Error(`钉钉 Access Token 获取失败: ${responseBody.slice(0, 300)}`));
|
|
2145
|
-
} catch (error) {
|
|
2146
|
-
reject(error);
|
|
2147
|
-
}
|
|
2148
|
-
});
|
|
2149
|
-
});
|
|
2150
|
-
request.on('error', reject);
|
|
2151
|
-
request.on('timeout', () => {
|
|
2152
|
-
request.destroy(new Error('钉钉 Access Token 获取超时'));
|
|
2153
|
-
});
|
|
2154
|
-
request.write(body);
|
|
2155
|
-
request.end();
|
|
2156
|
-
} catch (error) {
|
|
2157
|
-
reject(error);
|
|
2158
|
-
}
|
|
2159
|
-
});
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
function buildTestNotificationContext() {
|
|
2163
|
-
return {
|
|
2164
|
-
title: 'Coding Tool - 测试通知',
|
|
2165
|
-
message: '这是一条测试通知',
|
|
2166
|
-
source: 'test',
|
|
2167
|
-
eventType: 'test',
|
|
2168
|
-
timestamp: new Date().toLocaleString('zh-CN'),
|
|
2169
|
-
hostname: os.hostname()
|
|
2170
|
-
};
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
function formatProviderText(ctx) {
|
|
2174
|
-
return [
|
|
2175
|
-
ctx.title,
|
|
2176
|
-
`来源: ${ctx.source}`,
|
|
2177
|
-
`状态: ${ctx.message}`,
|
|
2178
|
-
`时间: ${ctx.timestamp}`,
|
|
2179
|
-
`设备: ${ctx.hostname}`
|
|
2180
|
-
].join('\n');
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
async function sendRemoteProviderTest(providerInput = {}) {
|
|
2184
|
-
const provider = normalizeRemoteProvider(providerInput);
|
|
2185
|
-
if (!provider) {
|
|
2186
|
-
throw createValidationError('远程通知渠道类型不正确');
|
|
2187
|
-
}
|
|
2188
|
-
validateRemoteProviderConfig({ ...provider, enabled: true });
|
|
2189
|
-
|
|
2190
|
-
const config = provider.config || {};
|
|
2191
|
-
const ctx = buildTestNotificationContext();
|
|
2192
|
-
switch (provider.type) {
|
|
2193
|
-
case 'feishuBot':
|
|
2194
|
-
return sendFeishuTest(config.webhookUrl);
|
|
2195
|
-
case 'qqBot': {
|
|
2196
|
-
const pathSuffix = config.targetType === 'group' ? '/send_group_msg' : '/send_private_msg';
|
|
2197
|
-
return requestJson(String(config.endpoint).replace(/\/$/, '') + pathSuffix, {
|
|
2198
|
-
[config.targetType === 'group' ? 'group_id' : 'user_id']: config.targetId,
|
|
2199
|
-
message: formatProviderText(ctx)
|
|
2200
|
-
}, config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {});
|
|
2201
|
-
}
|
|
2202
|
-
case 'wecomBot':
|
|
2203
|
-
return requestJson(config.webhookUrl, {
|
|
2204
|
-
msgtype: 'markdown',
|
|
2205
|
-
markdown: { content: `**${ctx.title}**\n> 状态: ${ctx.message}\n> 时间: ${ctx.timestamp}` }
|
|
2206
|
-
});
|
|
2207
|
-
case 'dingtalkBot':
|
|
2208
|
-
if (config.mode === 'app') {
|
|
2209
|
-
return sendDingTalkAppProviderTest(config, ctx);
|
|
2210
|
-
}
|
|
2211
|
-
return requestJson(config.webhookUrl, {
|
|
2212
|
-
msgtype: 'markdown',
|
|
2213
|
-
markdown: { title: ctx.title, text: `### ${ctx.title}\n\n- 状态: ${ctx.message}\n- 时间: ${ctx.timestamp}` }
|
|
2214
|
-
});
|
|
2215
|
-
case 'telegramBot':
|
|
2216
|
-
return requestTelegramMessage(config, ctx);
|
|
2217
|
-
case 'wechatBot':
|
|
2218
|
-
if (!config.botToken && config.tokenFile) {
|
|
2219
|
-
try {
|
|
2220
|
-
const raw = fs.readFileSync(config.tokenFile, 'utf8');
|
|
2221
|
-
const parsed = JSON.parse(raw);
|
|
2222
|
-
config.botToken = parsed.bot_token || parsed.token || '';
|
|
2223
|
-
} catch (error) {
|
|
2224
|
-
throw createValidationError('无法读取微信 token.json');
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
requireConfigValue(config, 'botToken', '微信 Bot Token');
|
|
2228
|
-
return requestJson('https://ilinkai.weixin.qq.com/ilink/bot/sendmessage', {
|
|
2229
|
-
msg: {
|
|
2230
|
-
from_user_id: '',
|
|
2231
|
-
to_user_id: config.targetUserId,
|
|
2232
|
-
client_id: createClientId('coding-tool'),
|
|
2233
|
-
message_type: 2,
|
|
2234
|
-
message_state: 2,
|
|
2235
|
-
item_list: [{ type: 1, text_item: { text: formatProviderText(ctx) } }],
|
|
2236
|
-
...(config.contextToken ? { context_token: config.contextToken } : {})
|
|
2237
|
-
},
|
|
2238
|
-
base_info: { channel_version: '2.1.8' }
|
|
2239
|
-
}, {
|
|
2240
|
-
AuthorizationType: 'ilink_bot_token',
|
|
2241
|
-
Authorization: `Bearer ${config.botToken}`,
|
|
2242
|
-
'X-WECHAT-UIN': Buffer.from(String(Math.floor(Math.random() * 0xffffffff))).toString('base64')
|
|
2243
|
-
});
|
|
2244
|
-
default:
|
|
2245
|
-
throw createValidationError('不支持的远程通知渠道');
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
|
-
function requestTelegramMessage(config = {}, ctx) {
|
|
2250
|
-
const url = `https://api.telegram.org/bot${config.botToken}/sendMessage`;
|
|
2251
|
-
const payload = {
|
|
2252
|
-
chat_id: config.chatId,
|
|
2253
|
-
text: formatProviderText(ctx)
|
|
2254
|
-
};
|
|
2255
|
-
const agent = config.proxy ? new HttpsProxyAgent(config.proxy) : null;
|
|
2256
|
-
return requestJson(url, payload, {}, agent ? { agent } : {});
|
|
2257
|
-
}
|
|
2258
|
-
|
|
2259
|
-
async function sendDingTalkAppProviderTest(config = {}, ctx) {
|
|
2260
|
-
const token = await requestDingTalkAccessToken(config);
|
|
2261
|
-
if (!token) {
|
|
2262
|
-
throw createValidationError('钉钉 App Access Token 获取失败');
|
|
2263
|
-
}
|
|
2264
|
-
const isGroup = config.targetType !== 'user';
|
|
2265
|
-
const payload = {
|
|
2266
|
-
robotCode: config.clientId,
|
|
2267
|
-
msgKey: 'sampleMarkdown',
|
|
2268
|
-
msgParam: JSON.stringify({
|
|
2269
|
-
title: ctx.title,
|
|
2270
|
-
text: `### ${ctx.title}\n\n- 状态: ${ctx.message}\n- 时间: ${ctx.timestamp}\n- 设备: ${ctx.hostname}`
|
|
2271
|
-
})
|
|
2272
|
-
};
|
|
2273
|
-
if (isGroup) {
|
|
2274
|
-
payload.openConversationId = config.targetId;
|
|
2275
|
-
} else {
|
|
2276
|
-
payload.userIds = [config.targetId];
|
|
2277
|
-
}
|
|
2278
|
-
return requestJson(
|
|
2279
|
-
'https://api.dingtalk.com/v1.0/robot/' + (isGroup ? 'groupMessages/send' : 'oToMessages/batchSend'),
|
|
2280
|
-
payload,
|
|
2281
|
-
{ 'x-acs-dingtalk-access-token': token }
|
|
2282
|
-
);
|
|
2283
|
-
}
|
|
2284
1399
|
|
|
2285
1400
|
function generateSystemNotificationCommand(type, message, platformOverride = os.platform()) {
|
|
2286
1401
|
const normalizedType = normalizeType(type);
|
|
@@ -2375,14 +1490,14 @@ function syncManagedNotificationAssets() {
|
|
|
2375
1490
|
return settings;
|
|
2376
1491
|
}
|
|
2377
1492
|
|
|
2378
|
-
function testNotification({ type, provider } = {}) {
|
|
1493
|
+
function testNotification({ type, provider, source = 'claude' } = {}) {
|
|
2379
1494
|
if (provider) {
|
|
2380
1495
|
return sendRemoteProviderTest(provider);
|
|
2381
1496
|
}
|
|
2382
1497
|
|
|
2383
1498
|
if (normalizeType(type) === 'browser') {
|
|
2384
1499
|
emitBrowserNotification({
|
|
2385
|
-
source
|
|
1500
|
+
source,
|
|
2386
1501
|
title: 'coding-tool-x',
|
|
2387
1502
|
message: '这是一条浏览器测试通知'
|
|
2388
1503
|
});
|
|
@@ -2412,6 +1527,8 @@ module.exports = {
|
|
|
2412
1527
|
normalizeRemoteNotificationsConfig,
|
|
2413
1528
|
validateRemoteProviderConfig,
|
|
2414
1529
|
_test: {
|
|
1530
|
+
getHookPlatformEntries,
|
|
1531
|
+
toPublicHookDefinition,
|
|
2415
1532
|
applyClaudeDisablePreference,
|
|
2416
1533
|
getManagedCommandType,
|
|
2417
1534
|
parseManagedType,
|