coding-tool-x 3.8.9 → 3.8.10

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 (124) hide show
  1. package/README.md +56 -388
  2. package/dist/web/assets/AgentsDrawer-Bam7kBjG.js +1 -0
  3. package/dist/web/assets/Analytics-D5dUWXEa.js +1 -0
  4. package/dist/web/assets/AssetPathField-CLqalYv-.js +1 -0
  5. package/dist/web/assets/{ChatHistoryDrawer-BTmQ1aQr.js → ChatHistoryDrawer-C1At65O3.js} +1 -1
  6. package/dist/web/assets/{CollapseItem-CwqJk3bE.js → CollapseItem-CVWojShK.js} +1 -1
  7. package/dist/web/assets/CommandsDrawer-BEoypGBf.js +1 -0
  8. package/dist/web/assets/ConfigExportDrawer-DBvFDDWK.js +1 -0
  9. package/dist/web/assets/{ConfigTemplates-gyERg9iC.js → ConfigTemplates-BpaWG-iC.js} +1 -1
  10. package/dist/web/assets/{ConfigTemplatesDrawer-B17RqFi4.js → ConfigTemplatesDrawer-C2lvyKMf.js} +1 -1
  11. package/dist/web/assets/ConfigTemplatesPanel-CorOY7kY.js +1 -0
  12. package/dist/web/assets/{Divider-DsdbkHrG.js → Divider-BLLhMTSt.js} +1 -1
  13. package/dist/web/assets/FavoritesDrawer-uW88N6te.js +1 -0
  14. package/dist/web/assets/GatewayConvertDrawer-i5Wt9Dd9.js +1 -0
  15. package/dist/web/assets/Home-BIxWvm29.js +1 -0
  16. package/dist/web/assets/{MarkdownEditor-9TTwTS-U.js → MarkdownEditor-Ul0bSGj-.js} +1 -1
  17. package/dist/web/assets/McpDrawer-Cc2xq77I.js +1 -0
  18. package/dist/web/assets/McpFormDrawer-DLHX_nrI.js +1 -0
  19. package/dist/web/assets/{PluginManager-BuRcWuBj.js → PluginManager-BGQ0xF2F.js} +1 -1
  20. package/dist/web/assets/{PluginsDrawer-_zZENaQn.js → PluginsDrawer-STwHEqiF.js} +1 -1
  21. package/dist/web/assets/PluginsPanel-1zuNP7da.js +1 -0
  22. package/dist/web/assets/ProjectList-_pe2nk9c.js +1 -0
  23. package/dist/web/assets/PromptsDrawer-CJN1xZk7.js +1 -0
  24. package/dist/web/assets/RecentSessionsDrawer-DX1HI8Nm.js +1 -0
  25. package/dist/web/assets/SessionCard-Op6PHvi0.js +1 -0
  26. package/dist/web/assets/SessionList-Da_TAEYr.js +1 -0
  27. package/dist/web/assets/SettingsDrawer-DKLd4Sjy.js +1 -0
  28. package/dist/web/assets/{SkillManager-S698Hns8.js → SkillManager-PcAa5EbF.js} +1 -1
  29. package/dist/web/assets/{SkillsDrawer-Ms4lPCow.js → SkillsDrawer-DPdU4p6C.js} +1 -1
  30. package/dist/web/assets/SkillsPanel-B9qzRqjd.js +1 -0
  31. package/dist/web/assets/Upload-e1M9Yp5z.js +1 -0
  32. package/dist/web/assets/{WorkspaceDrawer-L7HJRc6u.js → WorkspaceDrawer-oS6mB1VZ.js} +1 -1
  33. package/dist/web/assets/{WorkspaceManager-DsoNg2z8.js → WorkspaceManager-6VvRWe3r.js} +1 -1
  34. package/dist/web/assets/WorkspacePanel-CYq0Z8iY.js +1 -0
  35. package/dist/web/assets/client-BgcNg4Bt.js +1 -0
  36. package/dist/web/assets/{headers-B5JWGn3v.js → headers-DbowkmZJ.js} +1 -1
  37. package/dist/web/assets/{icons-DmFe4b5j.js → icons-B-AzZoeE.js} +1 -1
  38. package/dist/web/assets/index-BlUXw84U.js +2 -0
  39. package/dist/web/assets/index-Bn8BUnwP.css +1 -0
  40. package/dist/web/assets/{light-0eavxNvt.js → light-C2X7hegj.js} +1 -1
  41. package/dist/web/assets/platforms-DrzAtnjY.js +1 -0
  42. package/dist/web/assets/platforms-mqqe_82d.js +1 -0
  43. package/dist/web/assets/project-config-BVt5s-pH.js +1 -0
  44. package/dist/web/assets/sessions-53-Fr68H.js +1 -0
  45. package/dist/web/assets/{sessions-D4yQtfKb.js → sessions-CaWUaysM.js} +1 -1
  46. package/dist/web/assets/use-message-DBsCm7FJ.js +1 -0
  47. package/dist/web/assets/{useResponsiveDrawer-V_onl9NV.js → useResponsiveDrawer-ChHZrTfl.js} +1 -1
  48. package/dist/web/assets/{workspaces-mjyPVGS8.js → workspaces-DQuZd4qw.js} +1 -1
  49. package/dist/web/index.html +8 -8
  50. package/package.json +4 -2
  51. package/src/commands/export-config.js +17 -211
  52. package/src/config/model-metadata.json +180 -16
  53. package/src/platforms/channel-auth-service.js +1 -1
  54. package/src/platforms/commands-service.js +28 -0
  55. package/src/platforms/driver-registry.js +1 -0
  56. package/src/platforms/drivers/claude/channels-implementation.js +79 -90
  57. package/src/platforms/drivers/claude/native-config-implementation.js +3 -4
  58. package/src/platforms/drivers/codex/channels-implementation.js +34 -49
  59. package/src/platforms/drivers/codex/native-config-implementation.js +3 -51
  60. package/src/platforms/drivers/dsh/api-operations.js +1 -1
  61. package/src/platforms/drivers/dsh/channels-implementation.js +28 -8
  62. package/src/platforms/drivers/dsh/channels.js +0 -18
  63. package/src/platforms/drivers/dsh/common.js +7 -0
  64. package/src/platforms/drivers/dsh/native-config.js +7 -2
  65. package/src/platforms/drivers/gemini/channels-implementation.js +47 -55
  66. package/src/platforms/drivers/gemini/native-config-implementation.js +8 -20
  67. package/src/platforms/drivers/generic-native-config.js +20 -0
  68. package/src/platforms/drivers/native-config-snapshot.js +30 -4
  69. package/src/platforms/drivers/omp/channels-implementation.js +51 -17
  70. package/src/platforms/drivers/omp/native-config-implementation.js +10 -17
  71. package/src/platforms/drivers/opencode/channels-implementation.js +6 -0
  72. package/src/platforms/drivers/opencode/native-config-implementation.js +3 -67
  73. package/src/platforms/manifest-schema.js +35 -2
  74. package/src/platforms/manifests/claude.json +0 -8
  75. package/src/platforms/manifests/codex.json +0 -8
  76. package/src/platforms/manifests/dsh.json +6 -9
  77. package/src/platforms/manifests/gemini.json +0 -8
  78. package/src/platforms/manifests/omp.json +0 -8
  79. package/src/platforms/manifests/opencode.json +0 -8
  80. package/src/platforms/native-oauth-adapters.js +33 -33
  81. package/src/server/index.js +2 -0
  82. package/src/server/services/bounded-content.js +60 -0
  83. package/src/server/services/channel-balance.js +10 -4
  84. package/src/server/services/config-export-service.js +2 -2
  85. package/src/server/services/config-templates-service.js +5 -1
  86. package/src/server/services/plugins-service.js +162 -85
  87. package/src/server/services/session-history-maintenance-worker.js +71 -0
  88. package/src/server/services/session-history-maintenance.js +166 -0
  89. package/src/server/services/skill-service.js +23 -3
  90. package/src/server/services/speed-test.js +0 -72
  91. package/src/shared/channel-driver.js +16 -0
  92. package/src/shared/driver-factories/api.js +1 -0
  93. package/src/utils/native-config-patcher.js +474 -0
  94. package/dist/web/assets/AgentsDrawer-Cj5CJ4d9.js +0 -1
  95. package/dist/web/assets/Analytics-1doS-8uC.js +0 -1
  96. package/dist/web/assets/AssetPathField-C9TOPl7o.js +0 -1
  97. package/dist/web/assets/CommandsDrawer-CaZWDI_G.js +0 -1
  98. package/dist/web/assets/ConfigExportDrawer-C3vSZEsS.js +0 -1
  99. package/dist/web/assets/ConfigTemplatesPanel-DKIC_FqG.js +0 -1
  100. package/dist/web/assets/FavoritesDrawer-DaziQmBp.js +0 -1
  101. package/dist/web/assets/GatewayConvertDrawer-OMgWUUSc.js +0 -1
  102. package/dist/web/assets/Home-DOSUTfrE.js +0 -1
  103. package/dist/web/assets/McpDrawer-I8rl8lGH.js +0 -1
  104. package/dist/web/assets/McpFormDrawer-DEP3C7ut.js +0 -1
  105. package/dist/web/assets/PluginsPanel-Bs5qZ-TK.js +0 -1
  106. package/dist/web/assets/ProjectList-BSxuL2td.js +0 -1
  107. package/dist/web/assets/PromptsDrawer-CoZ-ZtsY.js +0 -1
  108. package/dist/web/assets/RecentSessionsDrawer-B_17aJBG.js +0 -1
  109. package/dist/web/assets/SessionCard-BL_x0d3t.js +0 -1
  110. package/dist/web/assets/SessionList-DhRYZ6vl.js +0 -1
  111. package/dist/web/assets/SettingsDrawer-fYb0DU7X.js +0 -1
  112. package/dist/web/assets/SkillsPanel-rqnqAOGL.js +0 -1
  113. package/dist/web/assets/SpeedTestDrawer-Bbn38SfV.js +0 -1
  114. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +0 -1
  115. package/dist/web/assets/Upload-CdyVHxCI.js +0 -1
  116. package/dist/web/assets/WorkspacePanel-Be8PVZah.js +0 -1
  117. package/dist/web/assets/client-UDwXBP9i.js +0 -1
  118. package/dist/web/assets/index-3Lp6z8TQ.css +0 -1
  119. package/dist/web/assets/index-BKlAWTH-.js +0 -2
  120. package/dist/web/assets/platforms-BmzzwnSw.js +0 -1
  121. package/dist/web/assets/platforms-DWXgmy2w.js +0 -1
  122. package/dist/web/assets/project-config-B2baPzYL.js +0 -1
  123. package/dist/web/assets/sessions-CC0j-KH_.js +0 -1
  124. package/dist/web/assets/use-message-DqCwefG6.js +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": "models.dev",
3
3
  "sourceUrl": "https://models.dev/api.json",
4
- "lastUpdated": "2026-09-15",
4
+ "lastUpdated": "2026-09-24",
5
5
  "defaultModels": {
6
6
  "claude": [
7
7
  "claude-opus-4-7",
@@ -17,7 +17,8 @@
17
17
  "claude-opus-4-8",
18
18
  "claude-opus-4-5",
19
19
  "claude-haiku-4-5",
20
- "claude-sonnet-4-5"
20
+ "claude-sonnet-4-5",
21
+ "claude-opus-5-5"
21
22
  ],
22
23
  "codex": [
23
24
  "gpt-5.5",
@@ -53,7 +54,9 @@
53
54
  "gpt-4o",
54
55
  "gpt-4o-2024-08-06",
55
56
  "gpt-4o-mini",
56
- "gpt-6-astra"
57
+ "gpt-6-astra",
58
+ "gpt-6-luna",
59
+ "gpt-6-sol"
57
60
  ],
58
61
  "gemini": [
59
62
  "gemini-3.1-pro-preview",
@@ -106,6 +109,7 @@
106
109
  "glm-4.5v": "zai/glm-4.5v",
107
110
  "glm-4.6": "zai/glm-4.6",
108
111
  "glm-4.6v": "zai/glm-4.6v",
112
+ "glm-4.6v-flash": "zai/glm-4.6v-flash",
109
113
  "glm-4.7": "zai/glm-4.7",
110
114
  "glm-4.7-flash": "zai/glm-4.7-flash",
111
115
  "glm-4.7-flashx": "zai/glm-4.7-flashx",
@@ -115,12 +119,14 @@
115
119
  "glm-5.2": "zai/glm-5.2",
116
120
  "glm-5.3": "zai/glm-5.3",
117
121
  "glm-5.3-flash": "zai/glm-5.3-flash",
122
+ "glm-5.3-flashx": "zai/glm-5.3-flashx",
118
123
  "glm-5v-turbo": "zai/glm-5v-turbo",
119
124
  "grok-4.20-0309-non-reasoning": "xai/grok-4.20-0309-non-reasoning",
120
125
  "grok-4.20-0309-reasoning": "xai/grok-4.20-0309-reasoning",
121
126
  "grok-4.3": "xai/grok-4.3",
122
127
  "grok-4.5": "xai/grok-4.5",
123
128
  "grok-4.6": "xai/grok-4.6",
129
+ "grok-4.7": "xai/grok-4.7",
124
130
  "grok-build-0.1": "xai/grok-build-0.1",
125
131
  "kimi-k2-0711-preview": "moonshotai/kimi-k2-0711-preview",
126
132
  "kimi-k2-0905-preview": "moonshotai/kimi-k2-0905-preview",
@@ -1214,7 +1220,7 @@
1214
1220
  ],
1215
1221
  "limit": {
1216
1222
  "context": 1000000,
1217
- "output": 65536
1223
+ "output": 131072
1218
1224
  },
1219
1225
  "name": "Qwen3.7 Max",
1220
1226
  "pricing": {
@@ -1240,7 +1246,7 @@
1240
1246
  ],
1241
1247
  "limit": {
1242
1248
  "context": 1000000,
1243
- "output": 65536
1249
+ "output": 131072
1244
1250
  },
1245
1251
  "name": "Qwen3.7 Plus",
1246
1252
  "pricing": {
@@ -1580,6 +1586,33 @@
1580
1586
  "omp"
1581
1587
  ]
1582
1588
  },
1589
+ "claude-opus-5-5": {
1590
+ "id": "claude-opus-5-5",
1591
+ "input": [
1592
+ "text",
1593
+ "image"
1594
+ ],
1595
+ "limit": {
1596
+ "context": 1000000,
1597
+ "output": 128000
1598
+ },
1599
+ "name": "Claude Opus 5.5",
1600
+ "pricing": {
1601
+ "cacheCreation": 5,
1602
+ "cacheRead": 0.2,
1603
+ "input": 4,
1604
+ "output": 20
1605
+ },
1606
+ "provider": "anthropic",
1607
+ "reasoning": true,
1608
+ "sourceId": "anthropic/claude-opus-5-5",
1609
+ "supportsTools": true,
1610
+ "toolTypes": [
1611
+ "claude",
1612
+ "opencode",
1613
+ "omp"
1614
+ ]
1615
+ },
1583
1616
  "claude-sonnet-4-5": {
1584
1617
  "id": "claude-sonnet-4-5",
1585
1618
  "input": [
@@ -1696,7 +1729,7 @@
1696
1729
  ],
1697
1730
  "limit": {
1698
1731
  "context": 1000000,
1699
- "output": 384000
1732
+ "output": 393216
1700
1733
  },
1701
1734
  "name": "DeepSeek V4.1 Flash",
1702
1735
  "pricing": {
@@ -1721,7 +1754,7 @@
1721
1754
  ],
1722
1755
  "limit": {
1723
1756
  "context": 1000000,
1724
- "output": 384000
1757
+ "output": 393216
1725
1758
  },
1726
1759
  "name": "DeepSeek V4 Flash",
1727
1760
  "pricing": {
@@ -1746,7 +1779,7 @@
1746
1779
  ],
1747
1780
  "limit": {
1748
1781
  "context": 1000000,
1749
- "output": 384000
1782
+ "output": 393216
1750
1783
  },
1751
1784
  "name": "DeepSeek V4 Flash Vision Exp",
1752
1785
  "pricing": {
@@ -1770,7 +1803,7 @@
1770
1803
  ],
1771
1804
  "limit": {
1772
1805
  "context": 1000000,
1773
- "output": 384000
1806
+ "output": 393216
1774
1807
  },
1775
1808
  "name": "DeepSeek V4 Pro",
1776
1809
  "pricing": {
@@ -1794,8 +1827,8 @@
1794
1827
  "image"
1795
1828
  ],
1796
1829
  "limit": {
1797
- "context": 131072,
1798
- "output": 65536
1830
+ "context": 128000,
1831
+ "output": 64000
1799
1832
  },
1800
1833
  "name": "Gemini 2.5 Computer Use Preview 10-2025",
1801
1834
  "pricing": {
@@ -1961,7 +1994,7 @@
1961
1994
  ],
1962
1995
  "limit": {
1963
1996
  "context": 65536,
1964
- "output": 65536
1997
+ "output": 4096
1965
1998
  },
1966
1999
  "name": "Nano Banana 2 Lite",
1967
2000
  "pricing": {
@@ -3012,6 +3045,60 @@
3012
3045
  "omp"
3013
3046
  ]
3014
3047
  },
3048
+ "gpt-6-luna": {
3049
+ "id": "gpt-6-luna",
3050
+ "input": [
3051
+ "text",
3052
+ "image"
3053
+ ],
3054
+ "limit": {
3055
+ "context": 1050000,
3056
+ "output": 128000
3057
+ },
3058
+ "name": "GPT-6 Luna",
3059
+ "pricing": {
3060
+ "cacheCreation": 0.125,
3061
+ "cacheRead": 0.01,
3062
+ "input": 0.1,
3063
+ "output": 0.5
3064
+ },
3065
+ "provider": "openai",
3066
+ "reasoning": true,
3067
+ "sourceId": "openai/gpt-6-luna",
3068
+ "supportsTools": true,
3069
+ "toolTypes": [
3070
+ "codex",
3071
+ "opencode",
3072
+ "omp"
3073
+ ]
3074
+ },
3075
+ "gpt-6-sol": {
3076
+ "id": "gpt-6-sol",
3077
+ "input": [
3078
+ "text",
3079
+ "image"
3080
+ ],
3081
+ "limit": {
3082
+ "context": 1050000,
3083
+ "output": 128000
3084
+ },
3085
+ "name": "GPT-6 Sol",
3086
+ "pricing": {
3087
+ "cacheCreation": 2.5,
3088
+ "cacheRead": 0.2,
3089
+ "input": 2,
3090
+ "output": 10
3091
+ },
3092
+ "provider": "openai",
3093
+ "reasoning": true,
3094
+ "sourceId": "openai/gpt-6-sol",
3095
+ "supportsTools": true,
3096
+ "toolTypes": [
3097
+ "codex",
3098
+ "opencode",
3099
+ "omp"
3100
+ ]
3101
+ },
3015
3102
  "gpt-realtime-2.1": {
3016
3103
  "id": "gpt-realtime-2.1",
3017
3104
  "input": [
@@ -3551,7 +3638,7 @@
3551
3638
  ],
3552
3639
  "limit": {
3553
3640
  "context": 1048576,
3554
- "output": 131072
3641
+ "output": 1048576
3555
3642
  },
3556
3643
  "name": "Kimi K3",
3557
3644
  "pricing": {
@@ -3744,6 +3831,31 @@
3744
3831
  "omp"
3745
3832
  ]
3746
3833
  },
3834
+ "xai/grok-4.7": {
3835
+ "id": "xai/grok-4.7",
3836
+ "input": [
3837
+ "text",
3838
+ "image"
3839
+ ],
3840
+ "limit": {
3841
+ "context": 500000,
3842
+ "output": 500000
3843
+ },
3844
+ "name": "Grok 4.7",
3845
+ "pricing": {
3846
+ "cacheRead": 0.5,
3847
+ "input": 2,
3848
+ "output": 6
3849
+ },
3850
+ "provider": "xai",
3851
+ "reasoning": true,
3852
+ "sourceId": "xai/grok-4.7",
3853
+ "supportsTools": true,
3854
+ "toolTypes": [
3855
+ "opencode",
3856
+ "omp"
3857
+ ]
3858
+ },
3747
3859
  "xai/grok-build-0.1": {
3748
3860
  "id": "xai/grok-build-0.1",
3749
3861
  "input": [
@@ -3917,6 +4029,32 @@
3917
4029
  "omp"
3918
4030
  ]
3919
4031
  },
4032
+ "zai/glm-4.6v-flash": {
4033
+ "id": "zai/glm-4.6v-flash",
4034
+ "input": [
4035
+ "text",
4036
+ "image"
4037
+ ],
4038
+ "limit": {
4039
+ "context": 128000,
4040
+ "output": 32768
4041
+ },
4042
+ "name": "GLM-4.6V-Flash",
4043
+ "pricing": {
4044
+ "cacheCreation": 0,
4045
+ "cacheRead": 0,
4046
+ "input": 0,
4047
+ "output": 0
4048
+ },
4049
+ "provider": "zai",
4050
+ "reasoning": true,
4051
+ "sourceId": "zai/glm-4.6v-flash",
4052
+ "supportsTools": true,
4053
+ "toolTypes": [
4054
+ "opencode",
4055
+ "omp"
4056
+ ]
4057
+ },
3920
4058
  "zai/glm-4.7": {
3921
4059
  "id": "zai/glm-4.7",
3922
4060
  "input": [
@@ -4130,9 +4268,9 @@
4130
4268
  "name": "GLM-5.3-Flash",
4131
4269
  "pricing": {
4132
4270
  "cacheCreation": 0,
4133
- "cacheRead": 0.015,
4134
- "input": 0.075,
4135
- "output": 0.25
4271
+ "cacheRead": 0.03,
4272
+ "input": 0.15,
4273
+ "output": 0.5
4136
4274
  },
4137
4275
  "provider": "zai",
4138
4276
  "reasoning": true,
@@ -4143,6 +4281,32 @@
4143
4281
  "omp"
4144
4282
  ]
4145
4283
  },
4284
+ "zai/glm-5.3-flashx": {
4285
+ "id": "zai/glm-5.3-flashx",
4286
+ "input": [
4287
+ "text",
4288
+ "image"
4289
+ ],
4290
+ "limit": {
4291
+ "context": 1000000,
4292
+ "output": 131072
4293
+ },
4294
+ "name": "GLM-5.3-FlashX",
4295
+ "pricing": {
4296
+ "cacheCreation": 0,
4297
+ "cacheRead": 0.075,
4298
+ "input": 0.37,
4299
+ "output": 1.25
4300
+ },
4301
+ "provider": "zai",
4302
+ "reasoning": true,
4303
+ "sourceId": "zai/glm-5.3-flashx",
4304
+ "supportsTools": true,
4305
+ "toolTypes": [
4306
+ "opencode",
4307
+ "omp"
4308
+ ]
4309
+ },
4146
4310
  "zai/glm-5v-turbo": {
4147
4311
  "id": "zai/glm-5v-turbo",
4148
4312
  "input": [
@@ -3,7 +3,7 @@
3
3
  const { getChannelAuthAdapter, listChannelAuthPlatforms, safeRef } = require('./channel-auth-registry');
4
4
 
5
5
  const quotaCache = new Map();
6
- const QUOTA_TTL_MS = 5 * 60 * 1000;
6
+ const QUOTA_TTL_MS = 60 * 1000;
7
7
 
8
8
  function getAdapter(platform) {
9
9
  const adapter = getChannelAuthAdapter(platform);
@@ -600,6 +600,34 @@ class CommandsService {
600
600
  return { ...detail, name: safeName, namespace: safeNamespace || null, scope, path: relativePath };
601
601
  }
602
602
 
603
+ /**
604
+ * Read an existing command by its scanned relative path.
605
+ * Existing files may predate the current command-name rules, so reading
606
+ * them must not re-validate the filename as if it were a new command.
607
+ */
608
+ getCommandByPath(commandPath, scope = 'user', projectPath = null) {
609
+ const baseDir = this._getBaseDir(scope, projectPath);
610
+ const relativePath = normalizeSafeRelativePath(commandPath, 'command path');
611
+ const extension = getCommandFileExtension(this.platform);
612
+ if (!relativePath.toLowerCase().endsWith(extension)) {
613
+ throw new Error('Invalid command path');
614
+ }
615
+
616
+ const detail = this._getLocalIndex(scope, projectPath)?.getSync(relativePath);
617
+ if (!detail) return null;
618
+
619
+ const name = path.posix.basename(relativePath, extension);
620
+ const namespace = path.posix.dirname(relativePath);
621
+ return {
622
+ ...detail,
623
+ name,
624
+ namespace: namespace === '.' ? null : namespace,
625
+ scope,
626
+ path: relativePath,
627
+ fullPath: path.join(baseDir, relativePath)
628
+ };
629
+ }
630
+
603
631
  /** 创建命令 */
604
632
  createCommand({ name, scope, projectPath, namespace, description, allowedTools, argumentHint, agent, model, subtask, body }) {
605
633
  const safeName = normalizeCommandName(name);
@@ -47,6 +47,7 @@ function getDriverRegistry() {
47
47
  'generic-openai-compatible': createGenericOpenAICompatibleDriver,
48
48
  'generic-mcp': createGenericMcpDriver,
49
49
  'generic-prompt': createGenericPromptDriver,
50
+ 'generic-native-config': require('./drivers/generic-native-config').createDriver,
50
51
  'opencode-conversion': require('./drivers/opencode/conversion').createDriver,
51
52
  'claude-health': require('./drivers/claude/health').createDriver,
52
53
  'dsh-api': require('./drivers/dsh/api-operations').createDriver,
@@ -3,8 +3,9 @@ const path = require('path');
3
3
  const BaseChannelService = require('../../../shared/base-channel-service');
4
4
  const { isProxyConfig } = require('./native-config-implementation');
5
5
  const { PATHS, NATIVE_PATHS } = require('../../../config/paths');
6
- const { clearNativeOAuth, readNativeOAuth, clearClaudeChannelConfig } = require('../../native-oauth-adapters');
6
+ const { readNativeOAuth, clearClaudeChannelConfig } = require('../../native-oauth-adapters');
7
7
  const { isWindowsLikePlatform } = require('../../../utils/home-dir');
8
+ const { updateJsoncFile } = require('../../../utils/native-config-patcher');
8
9
  const { normalizeGatewaySourceType } = require('../../../shared/proxy-utils');
9
10
  const {
10
11
  createSkippedResult,
@@ -96,62 +97,46 @@ function buildApiKeyHelperCommand(value) {
96
97
 
97
98
  // ── Claude 原生设置写入 ──
98
99
 
99
- function updateClaudeSettingsWithModelConfig(channel) {
100
- clearNativeOAuth('claude');
100
+ function updateClaudeSettingsWithModelConfig(channel, managedChannels = []) {
101
101
  const settingsPath = getClaudeSettingsPath();
102
- fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
103
-
104
- let settings = {};
105
- if (fs.existsSync(settingsPath)) {
106
- settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
107
- }
108
-
109
- if (!settings.env) {
110
- settings.env = {};
111
- }
112
-
113
102
  const { baseUrl, apiKey, modelConfig, presetId, proxyUrl } = channel;
114
-
115
- settings.env.ANTHROPIC_BASE_URL = baseUrl;
116
- settings.env.ANTHROPIC_API_KEY = apiKey;
117
- delete settings.env.ANTHROPIC_AUTH_TOKEN;
118
- delete settings.env.CLAUDE_CODE_OAUTH_TOKEN;
119
-
120
- if (presetId && presetId !== 'official' && modelConfig) {
121
- if (modelConfig.model) {
122
- settings.env.ANTHROPIC_MODEL = modelConfig.model;
123
- }
124
- if (modelConfig.haikuModel) {
125
- settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = modelConfig.haikuModel;
126
- }
127
- if (modelConfig.sonnetModel) {
128
- settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = modelConfig.sonnetModel;
129
- }
130
- if (modelConfig.opusModel) {
131
- settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL = modelConfig.opusModel;
103
+ const managedProxyUrls = new Set([
104
+ ...managedChannels.map(item => item?.proxyUrl),
105
+ ...service.loadChannels().channels.map(item => item?.proxyUrl)
106
+ ].map(value => String(value || '').trim()).filter(Boolean));
107
+ updateJsoncFile(settingsPath, (settings) => {
108
+ if (!settings.env || typeof settings.env !== 'object' || Array.isArray(settings.env)) settings.env = {};
109
+ settings.env.ANTHROPIC_BASE_URL = baseUrl;
110
+ settings.env.ANTHROPIC_API_KEY = apiKey;
111
+ delete settings.env.ANTHROPIC_AUTH_TOKEN;
112
+ delete settings.env.CLAUDE_CODE_OAUTH_TOKEN;
113
+
114
+ const modelKeys = {
115
+ ANTHROPIC_MODEL: modelConfig?.model,
116
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: modelConfig?.haikuModel,
117
+ ANTHROPIC_DEFAULT_SONNET_MODEL: modelConfig?.sonnetModel,
118
+ ANTHROPIC_DEFAULT_OPUS_MODEL: modelConfig?.opusModel
119
+ };
120
+ if (presetId && presetId !== 'official' && modelConfig) {
121
+ Object.entries(modelKeys).forEach(([key, value]) => {
122
+ if (value) settings.env[key] = value;
123
+ else delete settings.env[key];
124
+ });
125
+ } else {
126
+ Object.keys(modelKeys).forEach(key => delete settings.env[key]);
132
127
  }
133
- } else {
134
- delete settings.env.ANTHROPIC_MODEL;
135
- delete settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
136
- delete settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL;
137
- delete settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL;
138
- }
139
-
140
- if (proxyUrl) {
141
- settings.env.HTTPS_PROXY = proxyUrl;
142
- settings.env.HTTP_PROXY = proxyUrl;
143
- } else {
144
- delete settings.env.HTTPS_PROXY;
145
- delete settings.env.HTTP_PROXY;
146
- delete settings.env.NO_PROXY;
147
- }
148
128
 
149
- if (settings.env && Object.keys(settings.env).length === 0) {
150
- delete settings.env;
151
- }
152
-
153
- settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
154
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
129
+ if (proxyUrl) {
130
+ settings.env.HTTPS_PROXY = proxyUrl;
131
+ settings.env.HTTP_PROXY = proxyUrl;
132
+ } else {
133
+ ['HTTPS_PROXY', 'HTTP_PROXY'].forEach((key) => {
134
+ if (managedProxyUrls.has(String(settings.env[key] || '').trim())) delete settings.env[key];
135
+ });
136
+ }
137
+ if (Object.keys(settings.env).length === 0) delete settings.env;
138
+ settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
139
+ });
155
140
  }
156
141
 
157
142
  function createClaudeOAuthError(message, code, statusCode) {
@@ -213,35 +198,20 @@ function validateClaudeOAuthMutation(channel, operation) {
213
198
 
214
199
  function updateClaudeSettings(baseUrl, apiKey) {
215
200
  const settingsPath = getClaudeSettingsPath();
216
- fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
217
-
218
- let settings = {};
219
- if (fs.existsSync(settingsPath)) {
220
- settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
221
- }
222
-
223
- if (!settings.env) {
224
- settings.env = {};
225
- }
226
-
227
- const useAuthToken = settings.env.ANTHROPIC_AUTH_TOKEN !== undefined;
228
- const useApiKey = settings.env.ANTHROPIC_API_KEY !== undefined;
229
-
230
- settings.env.ANTHROPIC_BASE_URL = baseUrl;
231
-
232
- if (useAuthToken || (!useAuthToken && !useApiKey)) {
233
- settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
234
- delete settings.env.ANTHROPIC_API_KEY;
235
- } else {
236
- settings.env.ANTHROPIC_API_KEY = apiKey;
237
- }
238
-
239
- if (settings.env && Object.keys(settings.env).length === 0) {
240
- delete settings.env;
241
- }
242
-
243
- settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
244
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
201
+ updateJsoncFile(settingsPath, (settings) => {
202
+ if (!settings.env || typeof settings.env !== 'object' || Array.isArray(settings.env)) settings.env = {};
203
+ const useAuthToken = settings.env.ANTHROPIC_AUTH_TOKEN !== undefined;
204
+ const useApiKey = settings.env.ANTHROPIC_API_KEY !== undefined;
205
+ settings.env.ANTHROPIC_BASE_URL = baseUrl;
206
+ if (useAuthToken || (!useAuthToken && !useApiKey)) {
207
+ settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
208
+ delete settings.env.ANTHROPIC_API_KEY;
209
+ } else {
210
+ settings.env.ANTHROPIC_API_KEY = apiKey;
211
+ }
212
+ if (Object.keys(settings.env).length === 0) delete settings.env;
213
+ settings.apiKeyHelper = buildApiKeyHelperCommand(apiKey);
214
+ });
245
215
  }
246
216
 
247
217
  function resolveCurrentManagedChannel(channels = []) {
@@ -334,9 +304,9 @@ class ClaudeChannelService extends BaseChannelService {
334
304
  this._cacheInitialized = true;
335
305
  }
336
306
 
337
- _onAfterCreate(channel, _allChannels) {
307
+ _onAfterCreate(channel, allChannels) {
338
308
  if (!isClaudeProxyRunning() && channel.enabled !== false && !isOpenAiCompatibleGateway(channel)) {
339
- this._applyToNativeSettings(channel);
309
+ this._applyToNativeSettings(channel, allChannels);
340
310
  }
341
311
  }
342
312
 
@@ -347,7 +317,7 @@ class ClaudeChannelService extends BaseChannelService {
347
317
 
348
318
  if (oldChannel.enabled === false && nextChannel.enabled !== false) {
349
319
  if (!isOpenAiCompatibleGateway(nextChannel)) {
350
- this._applyToNativeSettings(nextChannel);
320
+ this._applyToNativeSettings(nextChannel, [...allChannels, oldChannel]);
351
321
  }
352
322
  return;
353
323
  }
@@ -358,19 +328,38 @@ class ClaudeChannelService extends BaseChannelService {
358
328
  && activeChannel?.id === nextChannel.id
359
329
  && !isOpenAiCompatibleGateway(nextChannel)
360
330
  ) {
361
- this._applyToNativeSettings(nextChannel);
331
+ this._applyToNativeSettings(nextChannel, [...allChannels, oldChannel]);
332
+ return;
333
+ }
334
+ if (oldChannel.enabled !== false && nextChannel.enabled === false) {
335
+ if (activeChannel && !isOpenAiCompatibleGateway(activeChannel)) {
336
+ this._applyToNativeSettings(activeChannel, [...allChannels, oldChannel]);
337
+ } else if (!activeChannel) {
338
+ clearClaudeChannelConfig([...allChannels, oldChannel].map(item => item?.proxyUrl));
339
+ }
362
340
  }
363
341
  }
364
342
 
365
- _onAfterDelete(_channel, allChannels) {
343
+ _onAfterDelete(channel, allChannels) {
366
344
  if (isClaudeProxyRunning()) {
367
345
  return;
368
346
  }
369
347
 
370
348
  const activeChannel = resolveCurrentManagedChannel(allChannels);
371
349
  if (activeChannel && !isOpenAiCompatibleGateway(activeChannel)) {
372
- this._applyToNativeSettings(activeChannel);
350
+ this._applyToNativeSettings(activeChannel, [...allChannels, channel]);
351
+ } else if (!activeChannel) {
352
+ clearClaudeChannelConfig([...allChannels, channel].map(item => item?.proxyUrl));
353
+ }
354
+ }
355
+
356
+ disableAllChannels() {
357
+ const channels = this.loadChannels().channels;
358
+ const result = super.disableAllChannels();
359
+ if (!isClaudeProxyRunning()) {
360
+ clearClaudeChannelConfig(channels.map(item => item?.proxyUrl));
373
361
  }
362
+ return result;
374
363
  }
375
364
 
376
365
  applyChannelToSettings(channelId) {
@@ -388,12 +377,12 @@ class ClaudeChannelService extends BaseChannelService {
388
377
  return super.applyChannelToSettings(channelId);
389
378
  }
390
379
 
391
- _applyToNativeSettings(channel) {
380
+ _applyToNativeSettings(channel, managedChannels = []) {
392
381
  if (channel.authMode === 'oauth') {
393
382
  applyNativeClaudeOAuth(channel);
394
383
  return;
395
384
  }
396
- updateClaudeSettingsWithModelConfig(channel);
385
+ updateClaudeSettingsWithModelConfig(channel, managedChannels);
397
386
  }
398
387
 
399
388
  getEffectiveApiKey(channel) {
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const { NATIVE_PATHS } = require('../../../config/paths');
3
3
  const { isWindowsLikePlatform } = require('../../../utils/home-dir');
4
+ const { readJsoncFile, writeJsoncFile } = require('../../../utils/native-config-patcher');
4
5
  const DEFAULT_NATIVE_PATHS = NATIVE_PATHS.claude;
5
6
  let nativePaths = DEFAULT_NATIVE_PATHS;
6
7
 
@@ -40,8 +41,7 @@ function hasBackup() {
40
41
  // 读取配置文件
41
42
  function readSettings() {
42
43
  try {
43
- const content = fs.readFileSync(getSettingsPath(), 'utf8');
44
- return JSON.parse(content);
44
+ return readJsoncFile(getSettingsPath());
45
45
  } catch (err) {
46
46
  throw new Error('Failed to read settings.json: ' + err.message);
47
47
  }
@@ -50,8 +50,7 @@ function readSettings() {
50
50
  // 写入配置文件
51
51
  function writeSettings(settings) {
52
52
  try {
53
- const content = JSON.stringify(settings, null, 2);
54
- fs.writeFileSync(getSettingsPath(), content, 'utf8');
53
+ writeJsoncFile(getSettingsPath(), settings);
55
54
  } catch (err) {
56
55
  throw new Error('Failed to write settings.json: ' + err.message);
57
56
  }