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.
Files changed (434) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +86 -15
  3. package/dist/web/assets/AgentsDrawer-BG6P3DX-.js +1 -0
  4. package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
  5. package/dist/web/assets/Analytics--z_FmL5P.js +1 -0
  6. package/dist/web/assets/Analytics-CyHhR126.css +1 -0
  7. package/dist/web/assets/AssetPathField-CrJtRKHy.js +1 -0
  8. package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
  9. package/dist/web/assets/ChatHistoryDrawer-BiuEryJj.js +1 -0
  10. package/dist/web/assets/CollapseItem-BAYEsL_Q.js +1 -0
  11. package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
  12. package/dist/web/assets/CommandsDrawer-YJ-U7BXN.js +1 -0
  13. package/dist/web/assets/ConfigExportDrawer-Ce_hAknW.js +1 -0
  14. package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
  15. package/dist/web/assets/ConfigTemplates-Bvr1NN6K.js +1 -0
  16. package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
  17. package/dist/web/assets/ConfigTemplatesDrawer-BjIeHfF-.js +1 -0
  18. package/dist/web/assets/ConfigTemplatesPanel-AV00tm_D.js +1 -0
  19. package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
  20. package/dist/web/assets/Divider-D28g1gwj.js +1 -0
  21. package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
  22. package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
  23. package/dist/web/assets/FavoritesDrawer-Cz95o4CD.js +1 -0
  24. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
  25. package/dist/web/assets/GatewayConvertDrawer-DLlbMz27.js +1 -0
  26. package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
  27. package/dist/web/assets/Home-D7CCOFQC.js +1 -0
  28. package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
  29. package/dist/web/assets/MarkdownEditor-rEZ-c72L.js +1 -0
  30. package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
  31. package/dist/web/assets/McpDrawer-DfoCyxU2.js +1 -0
  32. package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
  33. package/dist/web/assets/McpFormDrawer-SKu8HNUR.js +1 -0
  34. package/dist/web/assets/PluginManager-BbeK-3rV.js +1 -0
  35. package/dist/web/assets/PluginsDrawer-CWVJ7yjS.js +1 -0
  36. package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
  37. package/dist/web/assets/PluginsPanel-Cm2N3StG.js +1 -0
  38. package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
  39. package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
  40. package/dist/web/assets/ProjectList-CsjnsNPv.js +1 -0
  41. package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
  42. package/dist/web/assets/PromptsDrawer-EGd3djFa.js +1 -0
  43. package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
  44. package/dist/web/assets/RecentSessionsDrawer-D1vtoLAs.js +1 -0
  45. package/dist/web/assets/SessionCard-BgLg49U3.js +1 -0
  46. package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
  47. package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
  48. package/dist/web/assets/SessionList-TRiTklWk.js +1 -0
  49. package/dist/web/assets/SettingsDrawer-d5liXjHq.css +1 -0
  50. package/dist/web/assets/SettingsDrawer-rz2WejGY.js +1 -0
  51. package/dist/web/assets/SkillManager-BTjTUbHq.js +1 -0
  52. package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
  53. package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
  54. package/dist/web/assets/SkillsDrawer-pN0hbkK3.js +1 -0
  55. package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
  56. package/dist/web/assets/SkillsPanel-gmBWEoiF.js +1 -0
  57. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
  58. package/dist/web/assets/SpeedTestDrawer-DcNzuVJk.js +1 -0
  59. package/dist/web/assets/Upload-v5rV0qTT.js +1 -0
  60. package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
  61. package/dist/web/assets/WorkspaceDrawer-DxmicM4U.js +1 -0
  62. package/dist/web/assets/WorkspaceManager-CJHCjARN.js +1 -0
  63. package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
  64. package/dist/web/assets/WorkspacePanel-D8_Zq-du.js +1 -0
  65. package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
  66. package/dist/web/assets/client-UDwXBP9i.js +1 -0
  67. package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
  68. package/dist/web/assets/headers-cf-QapUq.js +1 -0
  69. package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
  70. package/dist/web/assets/index-C0EHRym0.css +1 -0
  71. package/dist/web/assets/index-DnEocV0M.js +2 -0
  72. package/dist/web/assets/light-Cp0t7v32.js +1 -0
  73. package/dist/web/assets/markdown-u082bbtj.js +1 -0
  74. package/dist/web/assets/platforms-CBkB45JP.js +1 -0
  75. package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
  76. package/dist/web/assets/project-config-B2baPzYL.js +1 -0
  77. package/dist/web/assets/sessions-Cmy31slD.js +1 -0
  78. package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
  79. package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
  80. package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
  81. package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
  82. package/dist/web/assets/useResponsiveDrawer-B3uojyvV.js +1 -0
  83. package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
  84. package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
  85. package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
  86. package/dist/web/assets/xml-CgY3RMlX.js +1 -0
  87. package/dist/web/index.html +12 -8
  88. package/docs/native-log-memory-2026-09-16.md +43 -0
  89. package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
  90. package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
  91. package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
  92. package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
  93. package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
  94. package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
  95. package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
  96. package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
  97. package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
  98. package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
  99. package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
  100. package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
  101. package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
  102. package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
  103. package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
  104. package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
  105. package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
  106. package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
  107. package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
  108. package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
  109. package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
  110. package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
  111. package/package.json +8 -4
  112. package/src/commands/channels.js +88 -191
  113. package/src/commands/cli-type.js +19 -11
  114. package/src/commands/daemon.js +94 -32
  115. package/src/commands/doctor.js +16 -9
  116. package/src/commands/export-config.js +23 -5
  117. package/src/commands/list.js +130 -52
  118. package/src/commands/logs.js +44 -27
  119. package/src/commands/platform-command-registry.js +78 -0
  120. package/src/commands/plugin.js +11 -5
  121. package/src/commands/port-config.js +34 -52
  122. package/src/commands/proxy-control.js +207 -145
  123. package/src/commands/proxy.js +2 -2
  124. package/src/commands/resume.js +87 -91
  125. package/src/commands/search.js +137 -67
  126. package/src/commands/stats.js +30 -22
  127. package/src/commands/switch.js +1 -1
  128. package/src/commands/toggle-proxy.js +94 -111
  129. package/src/commands/ui.js +67 -6
  130. package/src/commands/workspace.js +90 -8
  131. package/src/config/default.js +6 -53
  132. package/src/config/loader.js +61 -24
  133. package/src/config/model-metadata.js +49 -59
  134. package/src/config/model-metadata.json +4095 -99
  135. package/src/config/paths.js +181 -8
  136. package/src/index.js +134 -71
  137. package/src/platforms/access.js +196 -0
  138. package/src/{server/services → platforms}/agents-service.js +354 -197
  139. package/src/platforms/channel-auth-registry.js +162 -0
  140. package/src/platforms/channel-auth-service.js +82 -0
  141. package/src/{server/services → platforms}/commands-service.js +195 -115
  142. package/src/platforms/driver-registry.js +59 -0
  143. package/src/platforms/drivers/claude/api-operations.js +62 -0
  144. package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
  145. package/src/platforms/drivers/claude/channels.js +39 -0
  146. package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
  147. package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
  148. package/src/platforms/drivers/claude/health.js +22 -0
  149. package/src/platforms/drivers/claude/mcp.js +9 -0
  150. package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
  151. package/src/platforms/drivers/claude/native-config.js +46 -0
  152. package/src/platforms/drivers/claude/native-logs.js +72 -0
  153. package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
  154. package/src/platforms/drivers/claude/project-config.js +21 -0
  155. package/src/platforms/drivers/claude/projects.js +62 -0
  156. package/src/platforms/drivers/claude/prompts.js +9 -0
  157. package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
  158. package/src/platforms/drivers/claude/proxy.js +16 -0
  159. package/src/platforms/drivers/claude/resource-sync.js +9 -0
  160. package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
  161. package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
  162. package/src/platforms/drivers/claude/sessions.js +76 -0
  163. package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
  164. package/src/platforms/drivers/claude/statistics.js +16 -0
  165. package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
  166. package/src/platforms/drivers/codex/api-operations.js +9 -0
  167. package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
  168. package/src/platforms/drivers/codex/channels.js +45 -0
  169. package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
  170. package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
  171. package/src/platforms/drivers/codex/mcp.js +9 -0
  172. package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
  173. package/src/platforms/drivers/codex/native-config.js +47 -0
  174. package/src/platforms/drivers/codex/native-logs.js +166 -0
  175. package/src/platforms/drivers/codex/project-config.js +21 -0
  176. package/src/platforms/drivers/codex/projects.js +26 -0
  177. package/src/platforms/drivers/codex/prompts.js +9 -0
  178. package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
  179. package/src/platforms/drivers/codex/proxy.js +16 -0
  180. package/src/platforms/drivers/codex/resource-sync.js +9 -0
  181. package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
  182. package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
  183. package/src/platforms/drivers/codex/sessions.js +36 -0
  184. package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
  185. package/src/platforms/drivers/codex/statistics.js +16 -0
  186. package/src/platforms/drivers/gemini/api-operations.js +9 -0
  187. package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
  188. package/src/platforms/drivers/gemini/channels.js +39 -0
  189. package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
  190. package/src/platforms/drivers/gemini/mcp.js +9 -0
  191. package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
  192. package/src/platforms/drivers/gemini/native-config.js +39 -0
  193. package/src/platforms/drivers/gemini/native-logs.js +180 -0
  194. package/src/platforms/drivers/gemini/project-config.js +21 -0
  195. package/src/platforms/drivers/gemini/projects.js +26 -0
  196. package/src/platforms/drivers/gemini/prompts.js +9 -0
  197. package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
  198. package/src/platforms/drivers/gemini/proxy.js +16 -0
  199. package/src/platforms/drivers/gemini/resource-sync.js +9 -0
  200. package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
  201. package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
  202. package/src/platforms/drivers/gemini/sessions.js +47 -0
  203. package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
  204. package/src/platforms/drivers/gemini/statistics.js +16 -0
  205. package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
  206. package/src/platforms/drivers/generic-filesystem.js +166 -0
  207. package/src/platforms/drivers/generic-jsonl.js +141 -0
  208. package/src/platforms/drivers/generic-mcp.js +44 -0
  209. package/src/platforms/drivers/generic-openai-compatible.js +45 -0
  210. package/src/platforms/drivers/generic-prompt.js +41 -0
  211. package/src/platforms/drivers/legacy.js +677 -0
  212. package/src/platforms/drivers/native-config-snapshot.js +125 -0
  213. package/src/platforms/drivers/native-log-utils.js +1292 -0
  214. package/src/platforms/drivers/notification-hooks.js +578 -0
  215. package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
  216. package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
  217. package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
  218. package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
  219. package/src/platforms/drivers/omp/channels.js +58 -0
  220. package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
  221. package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
  222. package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
  223. package/src/platforms/drivers/omp/mcp.js +9 -0
  224. package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
  225. package/src/platforms/drivers/omp/native-config.js +46 -0
  226. package/src/platforms/drivers/omp/native-logs.js +52 -0
  227. package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
  228. package/src/platforms/drivers/omp/project-config.js +29 -0
  229. package/src/platforms/drivers/omp/projects.js +20 -0
  230. package/src/platforms/drivers/omp/prompts.js +9 -0
  231. package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
  232. package/src/platforms/drivers/omp/proxy.js +16 -0
  233. package/src/platforms/drivers/omp/resource-sync.js +9 -0
  234. package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
  235. package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
  236. package/src/platforms/drivers/omp/sessions.js +36 -0
  237. package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
  238. package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
  239. package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
  240. package/src/platforms/drivers/omp/statistics.js +16 -0
  241. package/src/platforms/drivers/opencode/api-operations.js +9 -0
  242. package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
  243. package/src/platforms/drivers/opencode/channels.js +40 -0
  244. package/src/platforms/drivers/opencode/conversion.js +41 -0
  245. package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
  246. package/src/platforms/drivers/opencode/mcp.js +9 -0
  247. package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
  248. package/src/platforms/drivers/opencode/native-config.js +26 -0
  249. package/src/platforms/drivers/opencode/native-logs.js +133 -0
  250. package/src/platforms/drivers/opencode/project-config.js +98 -0
  251. package/src/platforms/drivers/opencode/projects.js +20 -0
  252. package/src/platforms/drivers/opencode/prompts.js +9 -0
  253. package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
  254. package/src/platforms/drivers/opencode/proxy.js +16 -0
  255. package/src/platforms/drivers/opencode/resource-sync.js +9 -0
  256. package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
  257. package/src/platforms/drivers/opencode/sessions.js +48 -0
  258. package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
  259. package/src/platforms/drivers/opencode/statistics.js +16 -0
  260. package/src/platforms/drivers/secure-file-driver.js +272 -0
  261. package/src/platforms/drivers/unsupported.js +7 -0
  262. package/src/platforms/manifest-schema.js +398 -0
  263. package/src/platforms/manifests/claude.json +489 -0
  264. package/src/platforms/manifests/codex.json +448 -0
  265. package/src/platforms/manifests/gemini.json +447 -0
  266. package/src/platforms/manifests/omp.json +496 -0
  267. package/src/platforms/manifests/opencode.json +422 -0
  268. package/src/{server/services → platforms}/native-oauth-adapters.js +87 -417
  269. package/src/{server/services → platforms}/notification-hooks.js +140 -1023
  270. package/src/platforms/oauth-credentials-service.js +506 -0
  271. package/src/platforms/path-resolver.js +179 -0
  272. package/src/platforms/platform-path-context.js +305 -0
  273. package/src/platforms/registry.js +248 -0
  274. package/src/platforms/remote-notification-providers.js +678 -0
  275. package/src/platforms/runtime.js +259 -0
  276. package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
  277. package/src/plugins/plugin-loader.js +2 -2
  278. package/src/plugins/plugin-manager.js +2 -2
  279. package/src/plugins/registry.js +27 -10
  280. package/src/reset-config.js +21 -5
  281. package/src/server/api/agents.js +46 -72
  282. package/src/server/api/commands.js +16 -6
  283. package/src/server/api/config-registry.js +206 -59
  284. package/src/server/api/config-sync.js +3 -2
  285. package/src/server/api/config-templates.js +20 -4
  286. package/src/server/api/config.js +111 -102
  287. package/src/server/api/convert.js +54 -178
  288. package/src/server/api/dashboard.js +168 -102
  289. package/src/server/api/hooks.js +1 -1
  290. package/src/server/api/mcp.js +224 -84
  291. package/src/server/api/platform-api-config.js +102 -0
  292. package/src/server/api/platform-route-factory.js +215 -0
  293. package/src/server/api/platform-routes.js +26 -0
  294. package/src/server/api/platforms.js +110 -0
  295. package/src/server/api/plugins.js +57 -57
  296. package/src/server/api/project-config.js +168 -0
  297. package/src/server/api/prompts.js +58 -45
  298. package/src/server/api/settings.js +55 -8
  299. package/src/server/api/skills.js +262 -133
  300. package/src/server/api/ui-config.js +2 -2
  301. package/src/server/api/validation-errors.js +18 -4
  302. package/src/server/api/workspaces.js +47 -12
  303. package/src/server/index.js +164 -213
  304. package/src/server/platform-context.js +81 -0
  305. package/src/server/services/channel-balance.js +11 -20
  306. package/src/server/services/channel-health.js +21 -6
  307. package/src/server/services/channel-scheduler.js +29 -39
  308. package/src/server/services/channel-sync-utils.js +23 -13
  309. package/src/server/services/config-export-service.js +350 -319
  310. package/src/server/services/config-registry-service.js +280 -54
  311. package/src/server/services/config-sync-manager.js +172 -944
  312. package/src/server/services/config-templates-service.js +419 -353
  313. package/src/server/services/control-manifest-store.js +206 -0
  314. package/src/server/services/dashboard-snapshot-worker.js +656 -100
  315. package/src/server/services/effective-control-service.js +557 -0
  316. package/src/server/services/local-resource-index.js +314 -0
  317. package/src/server/services/mcp-service.js +607 -977
  318. package/src/server/services/model-detector.js +15 -10
  319. package/src/server/services/native-log-channel-resolver.js +214 -0
  320. package/src/server/services/native-log-observer.js +291 -0
  321. package/src/server/services/network-access.js +1 -1
  322. package/src/server/services/platform-catalog.js +82 -0
  323. package/src/server/services/platform-resolution.js +32 -29
  324. package/src/server/services/plugins-service.js +376 -122
  325. package/src/server/services/project-config-service.js +550 -0
  326. package/src/server/services/project-path-validation.js +31 -0
  327. package/src/server/services/prompts-service.js +267 -233
  328. package/src/server/services/proxy-runtime.js +7 -4
  329. package/src/server/services/remote-credential-cache.js +43 -0
  330. package/src/server/services/repo-scanner-base.js +182 -75
  331. package/src/server/services/request-logger.js +7 -4
  332. package/src/server/services/session-history-index.js +780 -213
  333. package/src/server/services/session-history-worker.js +228 -33
  334. package/src/server/services/session-snapshot-worker.js +124 -34
  335. package/src/server/services/session-snapshots.js +4 -2
  336. package/src/server/services/skill-artifact-store.js +355 -0
  337. package/src/server/services/skill-format-adapters.js +65 -0
  338. package/src/server/services/skill-projection-service.js +323 -0
  339. package/src/server/services/skill-refresh-task-service.js +275 -0
  340. package/src/server/services/skill-service.js +2020 -677
  341. package/src/server/services/snapshot-cache.js +30 -8
  342. package/src/server/services/speed-test.js +8 -8
  343. package/src/server/services/sqlite-connection.js +19 -3
  344. package/src/server/services/statistics-service.js +240 -57
  345. package/src/server/services/ui-config.js +107 -76
  346. package/src/server/services/usage-log-utils.js +98 -0
  347. package/src/server/services/web-build.js +1 -1
  348. package/src/server/services/workspace-service.js +59 -29
  349. package/src/server/utils/pricing.js +10 -19
  350. package/src/server/websocket-server.js +153 -28
  351. package/src/{server/services/base → shared}/base-channel-service.js +156 -28
  352. package/src/shared/capability-driver.js +104 -0
  353. package/src/shared/channel-auth-policy.js +27 -0
  354. package/src/shared/channel-driver.js +161 -0
  355. package/src/shared/driver-factories/api.js +180 -0
  356. package/src/shared/driver-factories/mcp.js +244 -0
  357. package/src/shared/driver-factories/projects.js +121 -0
  358. package/src/shared/driver-factories/prompts.js +58 -0
  359. package/src/shared/driver-factories/resource-sync.js +375 -0
  360. package/src/shared/driver-result.js +78 -0
  361. package/src/shared/mcp-format.js +189 -0
  362. package/src/shared/platforms.js +104 -167
  363. package/src/shared/project-config.js +512 -0
  364. package/src/shared/proxy-driver.js +57 -0
  365. package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
  366. package/src/ui/menu.js +91 -67
  367. package/src/utils/port-helper.js +35 -19
  368. package/src/utils/session.js +1 -1
  369. package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
  370. package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
  371. package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
  372. package/dist/web/assets/Home-DCEyLzwX.js +0 -1
  373. package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
  374. package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
  375. package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
  376. package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
  377. package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
  378. package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
  379. package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
  380. package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
  381. package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
  382. package/dist/web/assets/icons-nGdSKQn2.js +0 -1
  383. package/dist/web/assets/index-D5hyHPj7.js +0 -2
  384. package/dist/web/assets/index-bJRG3RXH.css +0 -1
  385. package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
  386. package/dist/web/assets/markdown-BfC0goYb.css +0 -10
  387. package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
  388. package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
  389. package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
  390. package/src/config/model-pricing.js +0 -35
  391. package/src/server/api/channels.js +0 -439
  392. package/src/server/api/claude-hooks.js +0 -56
  393. package/src/server/api/claude-statistics.js +0 -57
  394. package/src/server/api/codex-channels.js +0 -491
  395. package/src/server/api/codex-projects.js +0 -117
  396. package/src/server/api/codex-proxy.js +0 -293
  397. package/src/server/api/codex-sessions.js +0 -652
  398. package/src/server/api/codex-statistics.js +0 -57
  399. package/src/server/api/gemini-channels.js +0 -392
  400. package/src/server/api/gemini-projects.js +0 -103
  401. package/src/server/api/gemini-proxy.js +0 -231
  402. package/src/server/api/gemini-sessions.js +0 -550
  403. package/src/server/api/gemini-statistics.js +0 -57
  404. package/src/server/api/health-check.js +0 -31
  405. package/src/server/api/oauth-credentials.js +0 -198
  406. package/src/server/api/omp-channels.js +0 -575
  407. package/src/server/api/omp-projects.js +0 -88
  408. package/src/server/api/omp-proxy.js +0 -125
  409. package/src/server/api/omp-sessions.js +0 -386
  410. package/src/server/api/omp-statistics.js +0 -40
  411. package/src/server/api/opencode-channels.js +0 -471
  412. package/src/server/api/opencode-projects.js +0 -110
  413. package/src/server/api/opencode-proxy.js +0 -354
  414. package/src/server/api/opencode-sessions.js +0 -466
  415. package/src/server/api/opencode-statistics.js +0 -57
  416. package/src/server/api/projects.js +0 -120
  417. package/src/server/api/proxy.js +0 -330
  418. package/src/server/api/sessions.js +0 -810
  419. package/src/server/services/oauth-credentials-service.js +0 -945
  420. package/src/server/services/omp-session-log-observer.js +0 -138
  421. package/src/server/services/opencode-gateway-adapters.js +0 -43
  422. package/src/server/services/project-snapshots.js +0 -59
  423. package/src/server/services/proxy-log-helper.js +0 -298
  424. package/src/server/services/session-cache.js +0 -127
  425. package/src/server/services/session-converter.js +0 -582
  426. package/src/server/services/session-history-adapters/claude.js +0 -235
  427. /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
  428. /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
  429. /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
  430. /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
  431. /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
  432. /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
  433. /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
  434. /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
@@ -10,18 +10,42 @@ const path = require('path');
10
10
  const os = require('os');
11
11
  const toml = require('toml');
12
12
  const tomlStringify = require('@iarna/toml').stringify;
13
- const { RepoScannerBase } = require('./repo-scanner-base');
14
- const { NATIVE_PATHS } = require('../../config/paths');
15
- const { resolvePreferredHomeDir } = require('../../utils/home-dir');
13
+ const { RepoScannerBase } = require('../server/services/repo-scanner-base');
14
+ const { LocalResourceIndex } = require('../server/services/local-resource-index');
15
+ const { NATIVE_PATHS } = require('../config/paths');
16
+ const { resolvePreferredHomeDir } = require('../utils/home-dir');
17
+ const { createPlatformAccessError } = require('./access');
18
+ const { getPlatformContext } = require('../server/platform-context');
16
19
  const {
17
20
  normalizeSafeFileStem,
18
21
  normalizeSafeRelativePath,
19
22
  resolveInsideRoot
20
- } = require('./config-artifact-paths');
23
+ } = require('../shared/config-artifact-paths');
24
+
25
+ function readAgentMetadata(filePath) {
26
+ const fd = fs.openSync(filePath, 'r');
27
+ const buffer = Buffer.alloc(8192);
28
+ const chunks = [];
29
+ let total = 0;
30
+ try {
31
+ while (total < 1024 * 1024) {
32
+ const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
33
+ if (!bytesRead) break;
34
+ chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
35
+ total += bytesRead;
36
+ let text = Buffer.concat(chunks).toString('utf8');
37
+ if (text.charCodeAt(0) === 0xFEFF) text = text.slice(1);
38
+ if (total === bytesRead && !/^\s*---\s*(?:\n|$)/.test(text)) return {};
39
+ if (/^\s*---\s*\n[\s\S]*?\n\s*---\s*(?:\n|$)/.test(text)) return parseFrontmatter(text).frontmatter;
40
+ }
41
+ return {};
42
+ } finally {
43
+ fs.closeSync(fd);
44
+ }
45
+ }
21
46
 
22
47
  // 默认仓库源
23
48
  const DEFAULT_REPOS = [];
24
- const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
25
49
  const OPENCODE_CONFIG_DIR = NATIVE_PATHS.opencode.config;
26
50
  const CODEX_CONFIG_PATH = NATIVE_PATHS.codex.config;
27
51
  const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
@@ -35,7 +59,8 @@ const PLATFORM_CONFIG = {
35
59
  claude: {
36
60
  userAgentsDir: CLAUDE_AGENTS_DIR,
37
61
  projectAgentsDir: (projectPath) => path.join(projectPath, '.claude', 'agents'),
38
- repoType: 'agents'
62
+ repoType: 'agents',
63
+ capabilities: { projectScope: true, repoOperations: true }
39
64
  },
40
65
  opencode: {
41
66
  userAgentsDir: path.join(OPENCODE_CONFIG_DIR, 'agents'),
@@ -48,24 +73,33 @@ const PLATFORM_CONFIG = {
48
73
  }
49
74
  return modern;
50
75
  },
51
- repoType: 'opencode-agents'
76
+ repoType: 'opencode-agents',
77
+ capabilities: { projectScope: true, repoOperations: true }
52
78
  },
53
79
  codex: {
54
80
  userAgentsDir: CODEX_AGENTS_DIR,
55
81
  projectAgentsDir: () => null,
56
- repoType: 'agents'
82
+ repoType: 'agents',
83
+ capabilities: { projectScope: false, repoOperations: false }
57
84
  },
58
85
  gemini: {
59
86
  userAgentsDir: GEMINI_AGENTS_DIR,
60
87
  projectAgentsDir: (projectPath) => path.join(projectPath, '.gemini', 'agents'),
61
- repoType: 'gemini-agents'
88
+ repoType: 'gemini-agents',
89
+ capabilities: { projectScope: true, repoOperations: true }
62
90
  }
63
91
  };
64
92
 
65
93
  function normalizePlatform(platform) {
66
- const normalized = typeof platform === 'string' && platform.trim() ? platform.trim() : 'claude';
67
- if (!SUPPORTED_PLATFORMS.includes(normalized)) {
68
- throw new Error(`不支持的平台: ${platform}`);
94
+ const normalized = typeof platform === 'string' && platform.trim()
95
+ ? platform.trim().toLowerCase()
96
+ : 'claude';
97
+ if (!Object.prototype.hasOwnProperty.call(PLATFORM_CONFIG, normalized)) {
98
+ throw createPlatformAccessError('unsupported', {
99
+ platform: normalized,
100
+ capability: 'agents',
101
+ message: `Agents are not supported for ${normalized}`
102
+ });
69
103
  }
70
104
  return normalized;
71
105
  }
@@ -137,31 +171,31 @@ function writeFileAtomic(filePath, content) {
137
171
  fs.renameSync(tempPath, filePath);
138
172
  }
139
173
 
140
- function readCodexTomlConfig() {
141
- if (!fs.existsSync(CODEX_CONFIG_PATH)) {
174
+ function readCodexTomlConfig(configPath = CODEX_CONFIG_PATH) {
175
+ if (!fs.existsSync(configPath)) {
142
176
  return {};
143
177
  }
144
178
 
145
179
  try {
146
- const content = fs.readFileSync(CODEX_CONFIG_PATH, 'utf-8');
180
+ const content = fs.readFileSync(configPath, 'utf-8');
147
181
  return toml.parse(content);
148
182
  } catch (err) {
149
183
  throw new Error(`读取 Codex config.toml 失败: ${err.message}`);
150
184
  }
151
185
  }
152
186
 
153
- function writeCodexTomlConfig(config) {
154
- ensureDir(path.dirname(CODEX_CONFIG_PATH));
155
- writeFileAtomic(CODEX_CONFIG_PATH, tomlStringify(config));
187
+ function writeCodexTomlConfig(config, configPath = CODEX_CONFIG_PATH) {
188
+ ensureDir(path.dirname(configPath));
189
+ writeFileAtomic(configPath, tomlStringify(config));
156
190
  }
157
191
 
158
192
  function isPlainObject(value) {
159
193
  return !!value && typeof value === 'object' && !Array.isArray(value);
160
194
  }
161
195
 
162
- function getCodexManagedAgentConfigPath(fileName) {
196
+ function getCodexManagedAgentConfigPath(fileName, agentsDir = CODEX_AGENTS_DIR) {
163
197
  const safeFileName = assertSafeAgentFileName(fileName);
164
- return resolveInsideRoot(CODEX_AGENTS_DIR, `${safeFileName}.toml`, 'Codex agent config path');
198
+ return resolveInsideRoot(agentsDir, `${safeFileName}.toml`, 'Codex agent config path');
165
199
  }
166
200
 
167
201
  function normalizeCodexConfigPath(configPath) {
@@ -207,34 +241,34 @@ function assertSafeCodexConfigPath(configPath) {
207
241
  return relative;
208
242
  }
209
243
 
210
- function resolveCodexConfigPath(configPath) {
244
+ function resolveCodexConfigPath(configPath, configBasePath = CODEX_CONFIG_PATH, homeDir = HOME_DIR) {
211
245
  const normalized = normalizeCodexConfigPath(configPath);
212
246
  if (!normalized) return '';
213
247
 
214
248
  if (normalized.startsWith('~/')) {
215
- return path.join(HOME_DIR, normalized.slice(2));
249
+ return path.join(homeDir, normalized.slice(2));
216
250
  }
217
251
 
218
252
  if (path.isAbsolute(normalized)) {
219
253
  return normalized;
220
254
  }
221
255
 
222
- return path.resolve(path.dirname(CODEX_CONFIG_PATH), normalized);
256
+ return path.resolve(path.dirname(configBasePath), normalized);
223
257
  }
224
258
 
225
- function isManagedCodexConfigPath(configPath) {
226
- const resolved = resolveCodexConfigPath(configPath);
259
+ function isManagedCodexConfigPath(configPath, configBasePath = CODEX_CONFIG_PATH, agentsDir = CODEX_AGENTS_DIR, homeDir = HOME_DIR) {
260
+ const resolved = resolveCodexConfigPath(configPath, configBasePath, homeDir);
227
261
  if (!resolved) return false;
228
- const managedRoot = path.resolve(CODEX_AGENTS_DIR) + path.sep;
229
- return resolved.startsWith(managedRoot) || resolved === path.resolve(CODEX_AGENTS_DIR);
262
+ const managedRoot = path.resolve(agentsDir) + path.sep;
263
+ return resolved.startsWith(managedRoot) || resolved === path.resolve(agentsDir);
230
264
  }
231
265
 
232
- function getManagedCodexConfigResolvedPath(configPath) {
266
+ function getManagedCodexConfigResolvedPath(configPath, configBasePath = CODEX_CONFIG_PATH, agentsDir = CODEX_AGENTS_DIR, homeDir = HOME_DIR) {
233
267
  const normalized = normalizeCodexConfigPath(configPath);
234
- if (!normalized || !isManagedCodexConfigPath(normalized)) {
268
+ if (!normalized || !isManagedCodexConfigPath(normalized, configBasePath, agentsDir, homeDir)) {
235
269
  return '';
236
270
  }
237
- return resolveCodexConfigPath(normalized);
271
+ return resolveCodexConfigPath(normalized, configBasePath, homeDir);
238
272
  }
239
273
 
240
274
  function normalizeCodexConfigMode(mode) {
@@ -317,6 +351,7 @@ function readCodexAgentConfigFile(configFilePath) {
317
351
  * 解析 YAML frontmatter
318
352
  */
319
353
  function parseFrontmatter(content) {
354
+
320
355
  const result = {
321
356
  frontmatter: {},
322
357
  body: content
@@ -354,6 +389,32 @@ function parseFrontmatter(content) {
354
389
 
355
390
  return result;
356
391
  }
392
+ function readCodexTomlMetadata(filePath) {
393
+ const fd = fs.openSync(filePath, 'r');
394
+ const buffer = Buffer.alloc(8192);
395
+ const chunks = [];
396
+ let total = 0;
397
+ let updatedAt = 0;
398
+ try {
399
+ updatedAt = fs.fstatSync(fd).mtimeMs;
400
+ while (total < 1024 * 1024) {
401
+ const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
402
+ if (!bytesRead) break;
403
+ chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
404
+ total += bytesRead;
405
+ let content = Buffer.concat(chunks).toString('utf8');
406
+ if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
407
+ if (/^\s*model\s*=.*(?:\n|$)/m.test(content)) return { data: toml.parse(content), content, updatedAt };
408
+ }
409
+ let content = Buffer.concat(chunks).toString('utf8');
410
+ if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
411
+ return { data: toml.parse(content), content, updatedAt };
412
+ } catch (error) {
413
+ return { data: {}, content: '', updatedAt, error };
414
+ } finally {
415
+ fs.closeSync(fd);
416
+ }
417
+ }
357
418
 
358
419
  /**
359
420
  * 生成 frontmatter 字符串
@@ -536,21 +597,122 @@ class AgentsRepoScanner extends RepoScannerBase {
536
597
  * Agents 服务类
537
598
  */
538
599
  class AgentsService {
539
- constructor(platform = 'claude') {
600
+ constructor(platform = 'claude', { registry } = {}) {
540
601
  this.platform = normalizePlatform(platform);
541
602
  const config = PLATFORM_CONFIG[this.platform];
542
-
543
- this.userAgentsDir = config.userAgentsDir;
603
+ this.registry = registry || (() => {
604
+ try {
605
+ return getPlatformContext().registry;
606
+ } catch {
607
+ return null;
608
+ }
609
+ })();
610
+ this.pathContext = null;
611
+ try {
612
+ if (typeof this.registry?.resolvePathContext === 'function') {
613
+ this.pathContext = this.registry.resolvePathContext(this.platform);
614
+ }
615
+ } catch {
616
+ this.pathContext = null;
617
+ }
618
+ const native = this.pathContext?.customized
619
+ ? (this.pathContext.native || {})
620
+ : (NATIVE_PATHS[this.platform] || {});
621
+ this.homeDir = this.pathContext?.customized ? this.pathContext.home : HOME_DIR;
622
+ this.codexConfigPath = this.platform === 'codex'
623
+ ? (native.config || CODEX_CONFIG_PATH)
624
+ : CODEX_CONFIG_PATH;
625
+ this.codexAgentsDir = this.platform === 'codex'
626
+ ? (native.agents || path.join(native.dir || path.dirname(this.codexConfigPath), 'agents'))
627
+ : CODEX_AGENTS_DIR;
628
+ const configuredUserAgentsDir = this.pathContext?.customized
629
+ ? (native.agents
630
+ || (this.platform === 'opencode' ? path.join(native.config || OPENCODE_CONFIG_DIR, 'agents') : null)
631
+ || (native.dir ? path.join(native.dir, 'agents') : null))
632
+ : null;
633
+ this.userAgentsDir = configuredUserAgentsDir || config.userAgentsDir;
544
634
  if (this.platform === 'opencode') {
545
635
  const legacyUserDir = config.legacyUserAgentsDir;
546
- if (legacyUserDir && fs.existsSync(legacyUserDir) && !fs.existsSync(this.userAgentsDir)) {
547
- this.userAgentsDir = legacyUserDir;
548
- }
636
+ const configuredLegacyUserDir = this.pathContext?.customized && native.config
637
+ ? path.join(native.config, 'agent')
638
+ : legacyUserDir;
639
+ if (configuredLegacyUserDir && fs.existsSync(configuredLegacyUserDir) && !fs.existsSync(this.userAgentsDir)) this.userAgentsDir = configuredLegacyUserDir;
549
640
  }
550
-
551
641
  this.projectAgentsDir = config.projectAgentsDir;
552
642
  this.repoScanner = new AgentsRepoScanner(this.platform, this.userAgentsDir);
553
643
  ensureDir(this.userAgentsDir);
644
+ this._localIndexes = new Map();
645
+ this._localIndexLimit = 32;
646
+ }
647
+ _readCodexTomlConfig() {
648
+ return readCodexTomlConfig(this.codexConfigPath);
649
+ }
650
+ _writeCodexTomlConfig(config) {
651
+ return writeCodexTomlConfig(config, this.codexConfigPath);
652
+ }
653
+ _resolveCodexConfigPath(configPath) {
654
+ return resolveCodexConfigPath(configPath, this.codexConfigPath, this.homeDir);
655
+ }
656
+ _isManagedCodexConfigPath(configPath) {
657
+ return isManagedCodexConfigPath(configPath, this.codexConfigPath, this.codexAgentsDir, this.homeDir);
658
+ }
659
+ _getCodexManagedAgentConfigPath(fileName) {
660
+ return getCodexManagedAgentConfigPath(fileName, this.codexAgentsDir);
661
+ }
662
+ _inferCodexConfigMode(configFile) {
663
+ const normalized = normalizeCodexConfigPath(configFile);
664
+ if (!normalized) return 'none';
665
+ return this._isManagedCodexConfigPath(normalized) ? 'managed' : 'custom';
666
+ }
667
+ _configUpdatedAt() {
668
+ return fs.existsSync(this.codexConfigPath) ? fs.statSync(this.codexConfigPath).mtime.getTime() : 0;
669
+ }
670
+ getCapabilities() {
671
+ return { ...(PLATFORM_CONFIG[this.platform]?.capabilities || { projectScope: true, repoOperations: true }) };
672
+ }
673
+ getRepoOperationError(operation = '') {
674
+ const suffix = operation === 'install' ? '安装' : operation === 'uninstall' ? '卸载' : '';
675
+ return `${this.platform === 'codex' ? 'Codex 平台' : '该平台'}暂不支持远程仓库代理${suffix}`;
676
+ }
677
+ _getLocalIndex(scope, projectPath = null) {
678
+ const root = scope === 'user' ? this.userAgentsDir : this.getProjectAgentsDir(projectPath);
679
+ if (!root) return null;
680
+ const key = `${this.platform}:${scope}:${path.resolve(root)}`;
681
+ if (!this._localIndexes.has(key)) {
682
+ if (this._localIndexes.size >= this._localIndexLimit) {
683
+ const [oldKey, oldIndex] = this._localIndexes.entries().next().value;
684
+ oldIndex.dispose();
685
+ this._localIndexes.delete(oldKey);
686
+ }
687
+ this._localIndexes.set(key, new LocalResourceIndex({ key, roots: [root],
688
+ scanFile: (descriptor) => {
689
+ if (!descriptor.fullPath.endsWith('.md') || descriptor.relativePath.includes(path.sep)) return null;
690
+ const frontmatter = readAgentMetadata(descriptor.fullPath);
691
+ const fileName = path.basename(descriptor.relativePath, '.md');
692
+ return { name: frontmatter.name || fileName, fileName, scope, path: descriptor.relativePath, fullPath: descriptor.fullPath, description: frontmatter.description || '', tools: frontmatter.tools || '', model: frontmatter.model || '', permissionMode: frontmatter.permissionMode || '', skills: frontmatter.skills || '', updatedAt: descriptor.stat.mtime.getTime() };
693
+ },
694
+ detailFile: (summary) => {
695
+ const content = fs.readFileSync(summary.fullPath, 'utf-8');
696
+ const { frontmatter, body } = parseFrontmatter(content);
697
+ return { systemPrompt: body, fullContent: content, updatedAt: fs.statSync(summary.fullPath).mtime.getTime(), name: frontmatter.name || summary.fileName, description: frontmatter.description || '', tools: frontmatter.tools || '', model: frontmatter.model || '', permissionMode: frontmatter.permissionMode || '', skills: frontmatter.skills || '' };
698
+ }
699
+ }));
700
+ }
701
+ const index = this._localIndexes.get(key);
702
+ this._localIndexes.delete(key);
703
+ this._localIndexes.set(key, index);
704
+ return index;
705
+ }
706
+
707
+ dispose() {
708
+ for (const index of this._localIndexes.values()) index.dispose();
709
+ this._localIndexes.clear();
710
+ this._codexIndex?.dispose();
711
+ this._codexIndex = null;
712
+ }
713
+
714
+ _invalidateLocal(scope, projectPath = null) {
715
+ this._getLocalIndex(scope, projectPath)?.invalidate();
554
716
  }
555
717
 
556
718
  getProjectAgentsDir(projectPath) {
@@ -575,52 +737,20 @@ class AgentsService {
575
737
  return { safeFileName, relativePath, fullPath };
576
738
  }
577
739
 
578
- /**
579
- * 获取所有代理列表
580
- * @param {string} projectPath - 项目路径(可选,用于获取项目级代理)
581
- */
582
740
  listAgents(projectPath = null) {
583
- if (this.platform === 'codex') {
584
- return this.listCodexAgents();
585
- }
586
-
587
- const agents = [];
588
-
589
- // 获取用户级代理
590
- const userAgents = scanAgentsDir(this.userAgentsDir, this.userAgentsDir, 'user');
591
- agents.push(...userAgents);
592
-
593
- // 获取项目级代理(如果提供了项目路径)
594
- if (projectPath) {
595
- const projectAgentsDir = this.getProjectAgentsDir(projectPath);
596
- const projectAgents = scanAgentsDir(projectAgentsDir, projectAgentsDir, 'project');
597
- agents.push(...projectAgents);
598
- }
599
-
600
- // 按名称排序
741
+ if (this.platform === 'codex') return this.listCodexAgents(false);
742
+ const userAgents = this._getLocalIndex('user').listSync();
743
+ const projectAgents = projectPath ? this._getLocalIndex('project', projectPath).listSync() : [];
744
+ const agents = [...userAgents, ...projectAgents];
601
745
  agents.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
602
-
603
- return {
604
- agents,
605
- total: agents.length,
606
- userCount: userAgents.length,
607
- projectCount: agents.length - userAgents.length
608
- };
746
+ return { agents, total: agents.length, userCount: userAgents.length, projectCount: projectAgents.length };
609
747
  }
610
748
 
611
- /**
612
- * 获取所有代理(包括远程仓库)
613
- */
749
+ /** 获取所有代理(包括远程仓库) */
614
750
  async listAllAgents(projectPath = null, forceRefresh = false) {
615
751
  if (this.platform === 'codex') {
616
- const { agents, userCount, projectCount } = this.listCodexAgents();
617
- return {
618
- agents,
619
- total: agents.length,
620
- userCount,
621
- projectCount,
622
- remoteCount: 0
623
- };
752
+ const { agents, userCount, projectCount } = this.listCodexAgents(false);
753
+ return { agents, total: agents.length, userCount, projectCount, remoteCount: 0 };
624
754
  }
625
755
 
626
756
  // 获取本地代理
@@ -666,36 +796,12 @@ class AgentsService {
666
796
  */
667
797
  getAgent(fileName, scope, projectPath = null) {
668
798
  const safeFileName = assertSafeAgentFileName(fileName);
669
-
670
- if (this.platform === 'codex') {
671
- return this.getCodexAgent(safeFileName, scope);
672
- }
673
-
799
+ if (this.platform === 'codex') return this.getCodexAgent(safeFileName, scope);
674
800
  const baseDir = this._getBaseDir(scope, projectPath);
675
- const { relativePath, fullPath: filePath } = this._resolveAgentFilePath(baseDir, safeFileName);
676
-
677
- if (!fs.existsSync(filePath)) {
678
- return null;
679
- }
680
-
681
- const content = fs.readFileSync(filePath, 'utf-8');
682
- const { frontmatter, body } = parseFrontmatter(content);
683
-
684
- return {
685
- name: frontmatter.name || safeFileName,
686
- fileName: safeFileName,
687
- scope,
688
- path: relativePath,
689
- fullPath: filePath,
690
- description: frontmatter.description || '',
691
- tools: frontmatter.tools || '',
692
- model: frontmatter.model || '',
693
- permissionMode: frontmatter.permissionMode || '',
694
- skills: frontmatter.skills || '',
695
- systemPrompt: body,
696
- fullContent: content,
697
- updatedAt: fs.statSync(filePath).mtime.getTime()
698
- };
801
+ const { relativePath } = this._resolveAgentFilePath(baseDir, safeFileName);
802
+ const detail = this._getLocalIndex(scope, projectPath)?.getSync(relativePath);
803
+ if (!detail) return null;
804
+ return { ...detail, fileName: safeFileName, scope, path: relativePath };
699
805
  }
700
806
 
701
807
  /**
@@ -734,9 +840,8 @@ class AgentsService {
734
840
  if (skills) frontmatterData.skills = skills;
735
841
 
736
842
  const content = generateFrontmatter(frontmatterData, this.platform) + '\n\n' + (systemPrompt || '');
737
-
738
843
  fs.writeFileSync(filePath, content, 'utf-8');
739
-
844
+ this._invalidateLocal(scope, projectPath);
740
845
  return this.getAgent(safeFileName, scope, projectPath);
741
846
  }
742
847
 
@@ -768,9 +873,8 @@ class AgentsService {
768
873
  if (skills) frontmatterData.skills = skills;
769
874
 
770
875
  const content = generateFrontmatter(frontmatterData, this.platform) + '\n\n' + (systemPrompt || '');
771
-
772
876
  fs.writeFileSync(filePath, content, 'utf-8');
773
-
877
+ this._invalidateLocal(scope, projectPath);
774
878
  return this.getAgent(safeFileName, scope, projectPath);
775
879
  }
776
880
 
@@ -792,7 +896,7 @@ class AgentsService {
792
896
  }
793
897
 
794
898
  fs.unlinkSync(filePath);
795
-
899
+ this._invalidateLocal(scope, projectPath);
796
900
  return { success: true, message: '代理已删除' };
797
901
  }
798
902
 
@@ -864,28 +968,24 @@ class AgentsService {
864
968
  * 从远程仓库安装代理
865
969
  */
866
970
  async installFromRemote(agent) {
867
- if (!agent || typeof agent !== 'object') {
868
- throw new Error('无效的代理安装参数');
869
- }
971
+ if (!agent || typeof agent !== 'object') throw new Error('无效的代理安装参数');
870
972
  const safeFileName = assertSafeAgentFileName(agent.fileName);
871
973
  const safeRepoPath = assertSafeRepoPath(agent.repoPath);
872
- return this.repoScanner.installAgent({
873
- ...agent,
874
- fileName: safeFileName,
875
- repoPath: safeRepoPath
876
- });
974
+ const result = await this.repoScanner.installAgent({ ...agent, fileName: safeFileName, repoPath: safeRepoPath });
975
+ if (result && result.success !== false) this._invalidateLocal('user');
976
+ return result;
877
977
  }
878
978
 
879
- /**
880
- * 卸载代理
881
- */
979
+ /** 卸载代理 */
882
980
  uninstallAgent(fileName) {
883
981
  const safeFileName = assertSafeAgentFileName(fileName);
884
- return this.repoScanner.uninstall(`${safeFileName}.md`);
982
+ const result = this.repoScanner.uninstall(`${safeFileName}.md`);
983
+ if (result && result.success !== false) this._invalidateLocal('user');
984
+ return result;
885
985
  }
886
986
 
887
- listCodexAgents() {
888
- const config = readCodexTomlConfig();
987
+ _readCodexAgents(includeDetails = true) {
988
+ const config = this._readCodexTomlConfig();
889
989
  const agentsTable = isPlainObject(config.agents) ? config.agents : {};
890
990
  const agentsByName = new Map();
891
991
 
@@ -895,26 +995,19 @@ class AgentsService {
895
995
  }
896
996
 
897
997
  const configFile = normalizeCodexConfigPath(value.config_file);
898
- const resolvedConfigFile = resolveCodexConfigPath(configFile);
899
- const configMode = inferCodexConfigMode(configFile);
998
+ const resolvedConfigFile = this._resolveCodexConfigPath(configFile);
999
+ const configMode = this._inferCodexConfigMode(configFile);
900
1000
  const fullPath = configFile || `${key}.toml`;
901
1001
  let model = '';
902
1002
  let fullContent = '';
903
1003
  let configReadError = '';
904
- let updatedAt = fs.existsSync(CODEX_CONFIG_PATH) ? fs.statSync(CODEX_CONFIG_PATH).mtime.getTime() : Date.now();
905
-
906
- if (configFile) {
1004
+ let updatedAt = this.codexConfigPath && fs.existsSync(this.codexConfigPath) ? fs.statSync(this.codexConfigPath).mtime.getTime() : Date.now();
1005
+ if (includeDetails && configFile) {
907
1006
  const parsedConfigFile = readCodexAgentConfigFile(resolvedConfigFile);
908
1007
  fullContent = parsedConfigFile.content;
909
- if (isPlainObject(parsedConfigFile.data) && typeof parsedConfigFile.data.model === 'string') {
910
- model = parsedConfigFile.data.model;
911
- }
912
- if (parsedConfigFile.updatedAt) {
913
- updatedAt = Math.max(updatedAt, parsedConfigFile.updatedAt);
914
- }
915
- if (parsedConfigFile.error) {
916
- configReadError = parsedConfigFile.error;
917
- }
1008
+ if (isPlainObject(parsedConfigFile.data) && typeof parsedConfigFile.data.model === 'string') model = parsedConfigFile.data.model;
1009
+ if (parsedConfigFile.updatedAt) updatedAt = Math.max(updatedAt, parsedConfigFile.updatedAt);
1010
+ if (parsedConfigFile.error) configReadError = parsedConfigFile.error;
918
1011
  }
919
1012
 
920
1013
  agentsByName.set(key, {
@@ -939,9 +1032,9 @@ class AgentsService {
939
1032
  });
940
1033
  }
941
1034
 
942
- if (fs.existsSync(CODEX_AGENTS_DIR)) {
1035
+ if (fs.existsSync(this.codexAgentsDir)) {
943
1036
  try {
944
- const entries = fs.readdirSync(CODEX_AGENTS_DIR, { withFileTypes: true });
1037
+ const entries = fs.readdirSync(this.codexAgentsDir, { withFileTypes: true });
945
1038
  for (const entry of entries) {
946
1039
  if (!entry.isFile() || !entry.name.endsWith('.toml')) {
947
1040
  continue;
@@ -954,35 +1047,25 @@ class AgentsService {
954
1047
  continue;
955
1048
  }
956
1049
 
957
- const configFilePath = path.join(CODEX_AGENTS_DIR, entry.name);
958
- const parsedConfigFile = readCodexAgentConfigFile(configFilePath);
1050
+ const configFilePath = path.join(this.codexAgentsDir, entry.name);
1051
+ const parsedConfigFile = includeDetails ? readCodexAgentConfigFile(configFilePath) : { content: '', data: null, updatedAt: fs.statSync(configFilePath).mtime.getTime(), error: null };
959
1052
  const existing = agentsByName.get(fileName);
960
- const model = isPlainObject(parsedConfigFile.data) && typeof parsedConfigFile.data.model === 'string'
961
- ? parsedConfigFile.data.model
962
- : '';
1053
+ const model = isPlainObject(parsedConfigFile.data) && typeof parsedConfigFile.data.model === 'string' ? parsedConfigFile.data.model : '';
963
1054
  const updatedAt = parsedConfigFile.updatedAt || fs.statSync(configFilePath).mtime.getTime();
964
1055
 
965
1056
  if (existing) {
966
1057
  if (!existing.configFile) {
967
1058
  existing.configFile = configFilePath;
968
- existing.configMode = inferCodexConfigMode(configFilePath);
1059
+ existing.configMode = this._inferCodexConfigMode(configFilePath);
969
1060
  existing.path = configFilePath;
970
1061
  existing.fullPath = configFilePath;
971
1062
  existing.resolvedConfigFile = configFilePath;
972
1063
  }
973
- if (!existing.fullContent && parsedConfigFile.content) {
974
- existing.fullContent = parsedConfigFile.content;
975
- }
976
- if (!existing.model && model) {
977
- existing.model = model;
978
- }
979
- if (parsedConfigFile.error && !existing.configReadError) {
980
- existing.configReadError = parsedConfigFile.error;
981
- }
1064
+ if (includeDetails && !existing.fullContent && parsedConfigFile.content) existing.fullContent = parsedConfigFile.content;
1065
+ if (!existing.model && model) existing.model = model;
1066
+ if (parsedConfigFile.error && !existing.configReadError) existing.configReadError = parsedConfigFile.error;
982
1067
  existing.updatedAt = Math.max(existing.updatedAt || 0, updatedAt);
983
- existing.source = existing.source === 'codex-config'
984
- ? 'codex-config+native-file'
985
- : existing.source;
1068
+ existing.source = existing.source === 'codex-config' ? 'codex-config+native-file' : existing.source;
986
1069
  continue;
987
1070
  }
988
1071
 
@@ -1000,7 +1083,7 @@ class AgentsService {
1000
1083
  systemPrompt: '',
1001
1084
  fullContent: parsedConfigFile.content,
1002
1085
  configFile: configFilePath,
1003
- configMode: inferCodexConfigMode(configFilePath),
1086
+ configMode: this._inferCodexConfigMode(configFilePath),
1004
1087
  configReadError: parsedConfigFile.error || '',
1005
1088
  resolvedConfigFile: configFilePath,
1006
1089
  updatedAt,
@@ -1012,27 +1095,101 @@ class AgentsService {
1012
1095
  }
1013
1096
  }
1014
1097
 
1015
- const agents = Array.from(agentsByName.values());
1016
1098
 
1099
+ const agents = Array.from(agentsByName.values());
1017
1100
  agents.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
1101
+ return { agents, total: agents.length, userCount: agents.length, projectCount: 0 };
1102
+ }
1103
+ _getCodexIndex() {
1104
+ if (!this._codexIndex) {
1105
+ this._codexIndex = new LocalResourceIndex({
1106
+ key: `${this.platform}:codex`, roots: [this.codexAgentsDir], ttlMs: 1000,
1107
+ scanFile: ({ fullPath, relativePath, stat }) => {
1108
+ if (!fullPath.endsWith('.toml') || relativePath.includes(path.sep)) return null;
1109
+ const fileName = path.basename(fullPath, '.toml');
1110
+ const metadata = readCodexTomlMetadata(fullPath);
1111
+ const data = metadata.data || {};
1112
+ return { name: fileName, fileName, scope: 'user', path: fullPath, fullPath, description: '', tools: '', model: typeof data.model === 'string' ? data.model : '', permissionMode: '', skills: '', configFile: fullPath, configMode: 'managed', resolvedConfigFile: fullPath, configReadError: metadata.error?.message || '', updatedAt: stat.mtime.getTime(), source: 'native-file' };
1113
+ },
1114
+ detailFile: (summary) => this._hydrateCodexAgent(summary)
1115
+ });
1116
+ }
1117
+ return this._codexIndex;
1118
+ }
1018
1119
 
1120
+ _hydrateCodexAgent(summary) {
1121
+ const configPath = summary.resolvedConfigFile || this._resolveCodexConfigPath(summary.configFile) || (summary.source === 'native-file' ? summary.fullPath : '');
1122
+ if (!configPath) return { ...summary, fullContent: '', systemPrompt: '', configReadError: '' };
1123
+ const parsed = readCodexAgentConfigFile(configPath);
1124
+ const data = parsed.data || {};
1019
1125
  return {
1020
- agents,
1021
- total: agents.length,
1022
- userCount: agents.length,
1023
- projectCount: 0
1126
+ ...summary,
1127
+ model: typeof data.model === 'string' ? data.model : summary.model || '',
1128
+ fullContent: parsed.content,
1129
+ systemPrompt: '',
1130
+ configReadError: parsed.error || summary.configReadError || '',
1131
+ updatedAt: parsed.updatedAt || summary.updatedAt
1024
1132
  };
1025
1133
  }
1026
1134
 
1027
- getCodexAgent(fileName, scope) {
1028
- assertSafeAgentFileName(fileName);
1029
-
1030
- if (scope !== 'user') {
1031
- return null;
1135
+ listCodexAgents(includeDetails = true) {
1136
+ const indexed = this._getCodexIndex().listSync();
1137
+ const config = this._readCodexTomlConfig();
1138
+ const table = isPlainObject(config.agents) ? config.agents : {};
1139
+ const merged = new Map(indexed.map((agent) => [agent.fileName, { ...agent }]));
1140
+ for (const [fileName, entry] of Object.entries(table)) {
1141
+ if (!isPlainObject(entry)) continue;
1142
+ const configFile = normalizeCodexConfigPath(entry.config_file);
1143
+ const resolvedConfigFile = this._resolveCodexConfigPath(configFile);
1144
+ const existing = merged.get(fileName);
1145
+ const externalMetadata = configFile && !this._isManagedCodexConfigPath(configFile) && fs.existsSync(resolvedConfigFile)
1146
+ ? readCodexTomlMetadata(resolvedConfigFile)
1147
+ : { data: {}, error: null };
1148
+ const summary = {
1149
+ ...(existing || {
1150
+ name: fileName, fileName, scope: 'user', path: configFile || `${fileName}.toml`,
1151
+ fullPath: resolvedConfigFile || `${fileName}.toml`, tools: '', model: '',
1152
+ permissionMode: '', skills: '', updatedAt: this._configUpdatedAt(), source: 'codex-config'
1153
+ }),
1154
+ description: entry.description || existing?.description || '',
1155
+ model: existing?.model || (typeof externalMetadata.data?.model === 'string' ? externalMetadata.data.model : ''),
1156
+ configReadError: externalMetadata.error?.message || existing?.configReadError || '',
1157
+ updatedAt: externalMetadata.updatedAt || existing?.updatedAt || this._configUpdatedAt(),
1158
+ configFile: configFile || existing?.configFile || '',
1159
+ configMode: this._inferCodexConfigMode(configFile || existing?.configFile || ''),
1160
+ resolvedConfigFile: resolvedConfigFile || existing?.resolvedConfigFile || existing?.fullPath || '',
1161
+ source: existing ? 'codex-config+native-file' : 'codex-config'
1162
+ };
1163
+ merged.set(fileName, summary);
1032
1164
  }
1165
+ const agents = Array.from(merged.values()).map((summary) => includeDetails ? this._hydrateCodexAgent(summary) : summary);
1166
+ agents.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
1167
+ return { agents, total: agents.length, userCount: agents.length, projectCount: 0 };
1168
+ }
1033
1169
 
1034
- const { agents } = this.listCodexAgents();
1035
- return agents.find(agent => agent.fileName === fileName) || null;
1170
+ getCodexAgent(fileName, scope) {
1171
+ const safeFileName = assertSafeAgentFileName(fileName);
1172
+ if (scope !== 'user') return null;
1173
+ const config = this._readCodexTomlConfig();
1174
+ const entry = isPlainObject(config.agents) && isPlainObject(config.agents[safeFileName]) ? config.agents[safeFileName] : null;
1175
+ const managedPath = path.join(this.codexAgentsDir, `${safeFileName}.toml`);
1176
+ const configFile = normalizeCodexConfigPath(entry?.config_file);
1177
+ const resolvedConfigFile = this._resolveCodexConfigPath(configFile);
1178
+ const nativePath = !configFile && fs.existsSync(managedPath) ? managedPath : '';
1179
+ const exists = Boolean(entry || nativePath || (resolvedConfigFile && fs.existsSync(resolvedConfigFile)));
1180
+ if (!exists) return null;
1181
+ const targetPath = resolvedConfigFile || nativePath;
1182
+ const summary = {
1183
+ name: safeFileName, fileName: safeFileName, scope: 'user',
1184
+ path: targetPath || `${safeFileName}.toml`, fullPath: targetPath || `${safeFileName}.toml`,
1185
+ description: entry?.description || '', tools: '', model: '', permissionMode: '', skills: '',
1186
+ configFile: configFile || nativePath,
1187
+ configMode: this._inferCodexConfigMode(configFile || nativePath),
1188
+ resolvedConfigFile: targetPath,
1189
+ updatedAt: this._configUpdatedAt(),
1190
+ source: entry ? 'codex-config' : 'native-file'
1191
+ };
1192
+ return this._hydrateCodexAgent(summary);
1036
1193
  }
1037
1194
 
1038
1195
  createCodexAgent({ fileName, scope, description, model, configMode, configFile, configContent }) {
@@ -1046,7 +1203,7 @@ class AgentsService {
1046
1203
  throw new Error('代理描述不能为空');
1047
1204
  }
1048
1205
 
1049
- const config = readCodexTomlConfig();
1206
+ const config = this._readCodexTomlConfig();
1050
1207
  config.features = isPlainObject(config.features) ? config.features : {};
1051
1208
  config.features.multi_agent = true;
1052
1209
  config.agents = isPlainObject(config.agents) ? config.agents : {};
@@ -1064,20 +1221,20 @@ class AgentsService {
1064
1221
  if (!normalizedMode) {
1065
1222
  const trimmedModel = (model || '').trim();
1066
1223
  if (trimmedModel) {
1067
- ensureDir(CODEX_AGENTS_DIR);
1068
- const configFilePath = getCodexManagedAgentConfigPath(fileName);
1224
+ ensureDir(this.codexAgentsDir);
1225
+ const configFilePath = this._getCodexManagedAgentConfigPath(fileName);
1069
1226
  writeFileAtomic(configFilePath, tomlStringify({ model: trimmedModel }));
1070
1227
  agentConfig.config_file = configFilePath;
1071
1228
  }
1072
1229
  } else if (normalizedMode === 'managed') {
1073
- ensureDir(CODEX_AGENTS_DIR);
1074
- const configFilePath = getCodexManagedAgentConfigPath(fileName);
1230
+ ensureDir(this.codexAgentsDir);
1231
+ const configFilePath = this._getCodexManagedAgentConfigPath(fileName);
1075
1232
  const content = resolveCodexConfigContent({ configContent, model });
1076
1233
  writeFileAtomic(configFilePath, content);
1077
1234
  agentConfig.config_file = configFilePath;
1078
1235
  } else if (normalizedMode === 'custom') {
1079
1236
  const safeConfigFile = assertSafeCodexConfigPath(configFile);
1080
- const resolvedPath = resolveCodexConfigPath(safeConfigFile);
1237
+ const resolvedPath = this._resolveCodexConfigPath(safeConfigFile);
1081
1238
  ensureDir(path.dirname(resolvedPath));
1082
1239
  const content = resolveCodexConfigContent({ configContent, model });
1083
1240
  writeFileAtomic(resolvedPath, content);
@@ -1085,8 +1242,8 @@ class AgentsService {
1085
1242
  }
1086
1243
 
1087
1244
  config.agents[fileName] = agentConfig;
1088
- writeCodexTomlConfig(config);
1089
-
1245
+ this._writeCodexTomlConfig(config);
1246
+ this._codexIndex?.invalidate();
1090
1247
  return this.getCodexAgent(fileName, scope);
1091
1248
  }
1092
1249
 
@@ -1097,7 +1254,7 @@ class AgentsService {
1097
1254
  throw new Error('Codex 仅支持用户级代理');
1098
1255
  }
1099
1256
 
1100
- const config = readCodexTomlConfig();
1257
+ const config = this._readCodexTomlConfig();
1101
1258
  config.features = isPlainObject(config.features) ? config.features : {};
1102
1259
  config.features.multi_agent = true;
1103
1260
  config.agents = isPlainObject(config.agents) ? config.agents : {};
@@ -1111,16 +1268,16 @@ class AgentsService {
1111
1268
  agentConfig.description = (description || '').trim();
1112
1269
 
1113
1270
  const existingConfigFile = normalizeCodexConfigPath(agentConfig.config_file);
1114
- const isExistingManagedConfig = isManagedCodexConfigPath(existingConfigFile);
1115
- const resolvedExistingConfigFile = isExistingManagedConfig ? resolveCodexConfigPath(existingConfigFile) : '';
1271
+ const isExistingManagedConfig = this._isManagedCodexConfigPath(existingConfigFile);
1272
+ const resolvedExistingConfigFile = isExistingManagedConfig ? this._resolveCodexConfigPath(existingConfigFile) : '';
1116
1273
  const normalizedMode = normalizeCodexConfigMode(configMode);
1117
1274
 
1118
1275
  if (!normalizedMode) {
1119
1276
  const trimmedModel = (model || '').trim();
1120
1277
  if (trimmedModel) {
1121
- ensureDir(CODEX_AGENTS_DIR);
1122
- const configFilePath = isExistingManagedConfig ? existingConfigFile : getCodexManagedAgentConfigPath(fileName);
1123
- const resolvedConfigFilePath = resolveCodexConfigPath(configFilePath);
1278
+ ensureDir(this.codexAgentsDir);
1279
+ const configFilePath = isExistingManagedConfig ? existingConfigFile : this._getCodexManagedAgentConfigPath(fileName);
1280
+ const resolvedConfigFilePath = this._resolveCodexConfigPath(configFilePath);
1124
1281
  const parsedConfigFile = readCodexAgentConfigFile(resolvedConfigFilePath);
1125
1282
  const configFileData = isPlainObject(parsedConfigFile?.data) ? parsedConfigFile.data : {};
1126
1283
  configFileData.model = trimmedModel;
@@ -1139,9 +1296,9 @@ class AgentsService {
1139
1296
  fs.unlinkSync(resolvedExistingConfigFile);
1140
1297
  }
1141
1298
  } else if (normalizedMode === 'managed') {
1142
- ensureDir(CODEX_AGENTS_DIR);
1143
- const configFilePath = getCodexManagedAgentConfigPath(fileName);
1144
- const resolvedConfigFilePath = resolveCodexConfigPath(configFilePath);
1299
+ ensureDir(this.codexAgentsDir);
1300
+ const configFilePath = this._getCodexManagedAgentConfigPath(fileName);
1301
+ const resolvedConfigFilePath = this._resolveCodexConfigPath(configFilePath);
1145
1302
  const existingManagedContent = fs.existsSync(resolvedConfigFilePath)
1146
1303
  ? fs.readFileSync(resolvedConfigFilePath, 'utf-8')
1147
1304
  : '';
@@ -1170,7 +1327,7 @@ class AgentsService {
1170
1327
  targetConfigPath = assertSafeCodexConfigPath(targetConfigPath);
1171
1328
  }
1172
1329
 
1173
- const resolvedTargetConfigPath = resolveCodexConfigPath(targetConfigPath);
1330
+ const resolvedTargetConfigPath = this._resolveCodexConfigPath(targetConfigPath);
1174
1331
  const existingContent = fs.existsSync(resolvedTargetConfigPath)
1175
1332
  ? fs.readFileSync(resolvedTargetConfigPath, 'utf-8')
1176
1333
  : '';
@@ -1190,8 +1347,8 @@ class AgentsService {
1190
1347
  }
1191
1348
 
1192
1349
  config.agents[fileName] = agentConfig;
1193
- writeCodexTomlConfig(config);
1194
-
1350
+ this._writeCodexTomlConfig(config);
1351
+ this._codexIndex?.invalidate();
1195
1352
  return this.getCodexAgent(fileName, scope);
1196
1353
  }
1197
1354
 
@@ -1202,7 +1359,7 @@ class AgentsService {
1202
1359
  throw new Error('Codex 仅支持用户级代理');
1203
1360
  }
1204
1361
 
1205
- const config = readCodexTomlConfig();
1362
+ const config = this._readCodexTomlConfig();
1206
1363
  config.agents = isPlainObject(config.agents) ? config.agents : {};
1207
1364
 
1208
1365
  const existingAgent = config.agents[fileName];
@@ -1211,17 +1368,17 @@ class AgentsService {
1211
1368
  }
1212
1369
 
1213
1370
  const existingConfigFile = normalizeCodexConfigPath(existingAgent.config_file);
1214
- const resolvedExistingConfigFile = resolveCodexConfigPath(existingConfigFile);
1371
+ const resolvedExistingConfigFile = this._resolveCodexConfigPath(existingConfigFile);
1215
1372
  if (existingConfigFile &&
1216
- isManagedCodexConfigPath(existingConfigFile) &&
1373
+ this._isManagedCodexConfigPath(existingConfigFile) &&
1217
1374
  resolvedExistingConfigFile &&
1218
1375
  fs.existsSync(resolvedExistingConfigFile)) {
1219
1376
  fs.unlinkSync(resolvedExistingConfigFile);
1220
1377
  }
1221
1378
 
1222
1379
  delete config.agents[fileName];
1223
- writeCodexTomlConfig(config);
1224
-
1380
+ this._writeCodexTomlConfig(config);
1381
+ this._codexIndex?.invalidate();
1225
1382
  return { success: true, message: '代理已删除' };
1226
1383
  }
1227
1384
  }