coding-tool-x 3.2.0

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 (185) hide show
  1. package/CHANGELOG.md +599 -0
  2. package/LICENSE +21 -0
  3. package/README.md +439 -0
  4. package/bin/ctx.js +8 -0
  5. package/dist/web/assets/Analytics-DN_YsnkW.js +39 -0
  6. package/dist/web/assets/Analytics-DuYvId7u.css +1 -0
  7. package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
  8. package/dist/web/assets/ConfigTemplates-DpXIMy0p.js +1 -0
  9. package/dist/web/assets/Home-38JTUlYt.js +1 -0
  10. package/dist/web/assets/Home-CjupSEWE.css +1 -0
  11. package/dist/web/assets/PluginManager-CX2tgq2H.js +1 -0
  12. package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
  13. package/dist/web/assets/ProjectList-C1lDcsn6.js +1 -0
  14. package/dist/web/assets/ProjectList-oJIyIRkP.css +1 -0
  15. package/dist/web/assets/SessionList-C55tjV7i.css +1 -0
  16. package/dist/web/assets/SessionList-CZ7T6rVx.js +1 -0
  17. package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
  18. package/dist/web/assets/SkillManager-DLN9f79y.js +1 -0
  19. package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
  20. package/dist/web/assets/WorkspaceManager-DxlHZkpZ.js +1 -0
  21. package/dist/web/assets/icons-DRrXwWZi.js +1 -0
  22. package/dist/web/assets/index-CetESrXw.css +1 -0
  23. package/dist/web/assets/index-Cfvn-2Gb.js +2 -0
  24. package/dist/web/assets/markdown-BfC0goYb.css +10 -0
  25. package/dist/web/assets/markdown-C9MYpaSi.js +1 -0
  26. package/dist/web/assets/naive-ui-DlpKk-8M.js +1 -0
  27. package/dist/web/assets/vendors-DMjSfzlv.js +7 -0
  28. package/dist/web/assets/vue-vendor-DET08QYg.js +45 -0
  29. package/dist/web/favicon.ico +0 -0
  30. package/dist/web/index.html +20 -0
  31. package/dist/web/logo.png +0 -0
  32. package/docs/bannel.png +0 -0
  33. package/docs/home.png +0 -0
  34. package/docs/logo.png +0 -0
  35. package/docs/model-redirection.md +251 -0
  36. package/docs/multi-channel-load-balancing.md +249 -0
  37. package/package.json +80 -0
  38. package/src/commands/channels.js +551 -0
  39. package/src/commands/cli-type.js +101 -0
  40. package/src/commands/daemon.js +365 -0
  41. package/src/commands/doctor.js +333 -0
  42. package/src/commands/export-config.js +205 -0
  43. package/src/commands/list.js +222 -0
  44. package/src/commands/logs.js +261 -0
  45. package/src/commands/plugin.js +585 -0
  46. package/src/commands/port-config.js +135 -0
  47. package/src/commands/proxy-control.js +264 -0
  48. package/src/commands/proxy.js +152 -0
  49. package/src/commands/resume.js +137 -0
  50. package/src/commands/search.js +190 -0
  51. package/src/commands/security.js +37 -0
  52. package/src/commands/stats.js +398 -0
  53. package/src/commands/switch.js +48 -0
  54. package/src/commands/toggle-proxy.js +247 -0
  55. package/src/commands/ui.js +99 -0
  56. package/src/commands/update.js +97 -0
  57. package/src/commands/workspace.js +454 -0
  58. package/src/config/default.js +69 -0
  59. package/src/config/loader.js +149 -0
  60. package/src/config/model-metadata.js +167 -0
  61. package/src/config/model-metadata.json +125 -0
  62. package/src/config/model-pricing.js +35 -0
  63. package/src/config/paths.js +190 -0
  64. package/src/index.js +680 -0
  65. package/src/plugins/constants.js +15 -0
  66. package/src/plugins/event-bus.js +54 -0
  67. package/src/plugins/manifest-validator.js +129 -0
  68. package/src/plugins/plugin-api.js +128 -0
  69. package/src/plugins/plugin-installer.js +601 -0
  70. package/src/plugins/plugin-loader.js +229 -0
  71. package/src/plugins/plugin-manager.js +170 -0
  72. package/src/plugins/registry.js +152 -0
  73. package/src/plugins/schema/plugin-manifest.json +115 -0
  74. package/src/reset-config.js +94 -0
  75. package/src/server/api/agents.js +826 -0
  76. package/src/server/api/aliases.js +36 -0
  77. package/src/server/api/channels.js +368 -0
  78. package/src/server/api/claude-hooks.js +480 -0
  79. package/src/server/api/codex-channels.js +417 -0
  80. package/src/server/api/codex-projects.js +104 -0
  81. package/src/server/api/codex-proxy.js +195 -0
  82. package/src/server/api/codex-sessions.js +483 -0
  83. package/src/server/api/codex-statistics.js +57 -0
  84. package/src/server/api/commands.js +482 -0
  85. package/src/server/api/config-export.js +212 -0
  86. package/src/server/api/config-registry.js +357 -0
  87. package/src/server/api/config-sync.js +155 -0
  88. package/src/server/api/config-templates.js +248 -0
  89. package/src/server/api/config.js +521 -0
  90. package/src/server/api/convert.js +260 -0
  91. package/src/server/api/dashboard.js +142 -0
  92. package/src/server/api/env.js +144 -0
  93. package/src/server/api/favorites.js +77 -0
  94. package/src/server/api/gemini-channels.js +366 -0
  95. package/src/server/api/gemini-projects.js +91 -0
  96. package/src/server/api/gemini-proxy.js +173 -0
  97. package/src/server/api/gemini-sessions.js +376 -0
  98. package/src/server/api/gemini-statistics.js +57 -0
  99. package/src/server/api/health-check.js +31 -0
  100. package/src/server/api/mcp.js +399 -0
  101. package/src/server/api/opencode-channels.js +419 -0
  102. package/src/server/api/opencode-projects.js +99 -0
  103. package/src/server/api/opencode-proxy.js +207 -0
  104. package/src/server/api/opencode-sessions.js +327 -0
  105. package/src/server/api/opencode-statistics.js +57 -0
  106. package/src/server/api/plugins.js +463 -0
  107. package/src/server/api/pm2-autostart.js +269 -0
  108. package/src/server/api/projects.js +124 -0
  109. package/src/server/api/prompts.js +279 -0
  110. package/src/server/api/proxy.js +306 -0
  111. package/src/server/api/security.js +53 -0
  112. package/src/server/api/sessions.js +514 -0
  113. package/src/server/api/settings.js +142 -0
  114. package/src/server/api/skills.js +570 -0
  115. package/src/server/api/statistics.js +238 -0
  116. package/src/server/api/ui-config.js +64 -0
  117. package/src/server/api/workspaces.js +456 -0
  118. package/src/server/codex-proxy-server.js +681 -0
  119. package/src/server/dev-server.js +26 -0
  120. package/src/server/gemini-proxy-server.js +610 -0
  121. package/src/server/index.js +422 -0
  122. package/src/server/opencode-proxy-server.js +4771 -0
  123. package/src/server/proxy-server.js +669 -0
  124. package/src/server/services/agents-service.js +1137 -0
  125. package/src/server/services/alias.js +71 -0
  126. package/src/server/services/channel-health.js +234 -0
  127. package/src/server/services/channel-scheduler.js +240 -0
  128. package/src/server/services/channels.js +447 -0
  129. package/src/server/services/codex-channels.js +705 -0
  130. package/src/server/services/codex-config.js +90 -0
  131. package/src/server/services/codex-parser.js +322 -0
  132. package/src/server/services/codex-sessions.js +936 -0
  133. package/src/server/services/codex-settings-manager.js +619 -0
  134. package/src/server/services/codex-speed-test-template.json +24 -0
  135. package/src/server/services/codex-statistics-service.js +161 -0
  136. package/src/server/services/commands-service.js +574 -0
  137. package/src/server/services/config-export-service.js +1165 -0
  138. package/src/server/services/config-registry-service.js +828 -0
  139. package/src/server/services/config-sync-manager.js +941 -0
  140. package/src/server/services/config-sync-service.js +504 -0
  141. package/src/server/services/config-templates-service.js +913 -0
  142. package/src/server/services/enhanced-cache.js +196 -0
  143. package/src/server/services/env-checker.js +409 -0
  144. package/src/server/services/env-manager.js +436 -0
  145. package/src/server/services/favorites.js +165 -0
  146. package/src/server/services/format-converter.js +620 -0
  147. package/src/server/services/gemini-channels.js +459 -0
  148. package/src/server/services/gemini-config.js +73 -0
  149. package/src/server/services/gemini-sessions.js +689 -0
  150. package/src/server/services/gemini-settings-manager.js +263 -0
  151. package/src/server/services/gemini-statistics-service.js +157 -0
  152. package/src/server/services/health-check.js +85 -0
  153. package/src/server/services/mcp-client.js +790 -0
  154. package/src/server/services/mcp-service.js +1732 -0
  155. package/src/server/services/model-detector.js +1245 -0
  156. package/src/server/services/network-access.js +80 -0
  157. package/src/server/services/opencode-channels.js +366 -0
  158. package/src/server/services/opencode-gateway-adapters.js +1168 -0
  159. package/src/server/services/opencode-gateway-converter.js +639 -0
  160. package/src/server/services/opencode-sessions.js +931 -0
  161. package/src/server/services/opencode-settings-manager.js +478 -0
  162. package/src/server/services/opencode-statistics-service.js +161 -0
  163. package/src/server/services/plugins-service.js +1268 -0
  164. package/src/server/services/prompts-service.js +534 -0
  165. package/src/server/services/proxy-runtime.js +79 -0
  166. package/src/server/services/repo-scanner-base.js +708 -0
  167. package/src/server/services/request-logger.js +130 -0
  168. package/src/server/services/response-decoder.js +21 -0
  169. package/src/server/services/security-config.js +131 -0
  170. package/src/server/services/session-cache.js +127 -0
  171. package/src/server/services/session-converter.js +577 -0
  172. package/src/server/services/sessions.js +900 -0
  173. package/src/server/services/settings-manager.js +163 -0
  174. package/src/server/services/skill-service.js +1482 -0
  175. package/src/server/services/speed-test.js +1146 -0
  176. package/src/server/services/statistics-service.js +1043 -0
  177. package/src/server/services/ui-config.js +132 -0
  178. package/src/server/services/workspace-service.js +830 -0
  179. package/src/server/utils/pricing.js +73 -0
  180. package/src/server/websocket-server.js +513 -0
  181. package/src/ui/menu.js +139 -0
  182. package/src/ui/prompts.js +100 -0
  183. package/src/utils/format.js +43 -0
  184. package/src/utils/port-helper.js +108 -0
  185. package/src/utils/session.js +240 -0
@@ -0,0 +1,306 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const { startProxyServer, stopProxyServer, getProxyStatus } = require('../proxy-server');
4
+ const {
5
+ setProxyConfig,
6
+ restoreSettings,
7
+ isProxyConfig,
8
+ getCurrentProxyPort,
9
+ settingsExists,
10
+ hasBackup,
11
+ readSettings
12
+ } = require('../services/settings-manager');
13
+ const { getAllChannels } = require('../services/channels');
14
+ const { clearAllLogs } = require('../websocket-server');
15
+ const { PATHS, ensureStorageDirMigrated } = require('../../config/paths');
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const os = require('os');
19
+
20
+ function sanitizeChannelForResponse(channel) {
21
+ if (!channel) return null;
22
+ return {
23
+ id: channel.id,
24
+ name: channel.name,
25
+ baseUrl: channel.baseUrl,
26
+ websiteUrl: channel.websiteUrl
27
+ };
28
+ }
29
+
30
+ // 保存激活渠道ID
31
+ function saveActiveChannelId(channelId) {
32
+ ensureStorageDirMigrated();
33
+ const filePath = PATHS.activeChannel.claude;
34
+ const dir = path.dirname(filePath);
35
+ if (!fs.existsSync(dir)) {
36
+ fs.mkdirSync(dir, { recursive: true });
37
+ }
38
+ fs.writeFileSync(filePath, JSON.stringify({ activeChannelId: channelId }, null, 2), 'utf8');
39
+ }
40
+
41
+
42
+ // 加载上次激活的渠道ID
43
+ function loadActiveChannelId() {
44
+ ensureStorageDirMigrated();
45
+ const filePath = PATHS.activeChannel.claude;
46
+ try {
47
+ if (fs.existsSync(filePath)) {
48
+ const content = fs.readFileSync(filePath, 'utf8');
49
+ const data = JSON.parse(content);
50
+ return data.activeChannelId || null;
51
+ }
52
+ } catch (error) {
53
+ console.error('[Proxy] Error loading active channel ID:', error);
54
+ }
55
+ return null;
56
+ }
57
+
58
+ // 从 settings.json 找到当前激活的渠道(多级回退策略)
59
+ function findActiveChannelFromSettings() {
60
+ try {
61
+ const settings = readSettings();
62
+ const baseUrl = settings?.env?.ANTHROPIC_BASE_URL || '';
63
+
64
+ // 兼容多种 API Key 格式(与 channels.js 保持一致)
65
+ let apiKey = settings?.env?.ANTHROPIC_API_KEY || // 标准格式
66
+ settings?.env?.ANTHROPIC_AUTH_TOKEN || // 88code等平台格式
67
+ '';
68
+
69
+ // 如果 apiKey 仍为空,尝试从 apiKeyHelper 提取
70
+ if (!apiKey && settings?.apiKeyHelper) {
71
+ const match = settings.apiKeyHelper.match(/['\"]([^'\"]+)['\"]/)
72
+ if (match && match[1]) {
73
+ apiKey = match[1];
74
+ }
75
+ }
76
+
77
+ if (!baseUrl || !apiKey || baseUrl.includes('127.0.0.1')) {
78
+ console.log('[Proxy] Invalid settings: empty baseUrl/apiKey or localhost detected');
79
+ return null;
80
+ }
81
+
82
+ const channels = getAllChannels();
83
+
84
+ // Level 1: Exact match (baseUrl + apiKey)
85
+ let matchingChannel = channels.find(ch =>
86
+ ch.baseUrl === baseUrl && ch.apiKey === apiKey
87
+ );
88
+
89
+ if (matchingChannel) {
90
+ console.log(`[Proxy] Level 1 - Exact match: ${matchingChannel.name}`);
91
+ return matchingChannel;
92
+ }
93
+
94
+ // Level 2: Match by baseUrl only (when apiKey differs)
95
+ matchingChannel = channels.find(ch => ch.baseUrl === baseUrl);
96
+ if (matchingChannel) {
97
+ console.log(`[Proxy] Level 2 - Matched by baseUrl only: ${matchingChannel.name}`);
98
+ return matchingChannel;
99
+ }
100
+
101
+ // Level 3: Use active-channel.json for last known active channel
102
+ const activeChannelId = loadActiveChannelId();
103
+ if (activeChannelId) {
104
+ matchingChannel = channels.find(ch => ch.id === activeChannelId);
105
+ if (matchingChannel) {
106
+ console.log(`[Proxy] Level 3 - Using last active channel: ${matchingChannel.name}`);
107
+ return matchingChannel;
108
+ }
109
+ }
110
+
111
+ // Level 4: Return first enabled channel as last resort
112
+ matchingChannel = channels.find(ch => ch.enabled !== false);
113
+ if (matchingChannel) {
114
+ console.log(`[Proxy] Level 4 - Using first enabled channel: ${matchingChannel.name}`);
115
+ return matchingChannel;
116
+ }
117
+
118
+ console.log('[Proxy] No matching channel found after all fallback levels');
119
+ return null;
120
+ } catch (err) {
121
+ console.error('[Proxy] Error finding active channel:', err);
122
+ return null;
123
+ }
124
+ }
125
+
126
+ // 获取代理状态
127
+ router.get('/status', (req, res) => {
128
+ try {
129
+ const proxyStatus = getProxyStatus();
130
+ const channels = getAllChannels();
131
+ const configStatus = {
132
+ isProxyConfig: isProxyConfig(),
133
+ settingsExists: settingsExists(),
134
+ hasBackup: hasBackup(),
135
+ currentProxyPort: getCurrentProxyPort()
136
+ };
137
+
138
+ res.json({
139
+ proxy: proxyStatus,
140
+ config: configStatus,
141
+ channelsCount: channels.length,
142
+ enabledChannelsCount: channels.filter(ch => ch.enabled !== false).length
143
+ });
144
+ } catch (error) {
145
+ res.status(500).json({ error: error.message });
146
+ }
147
+ });
148
+
149
+ // 启动代理
150
+ router.post('/start', async (req, res) => {
151
+ try {
152
+ // 1. 检查配置文件是否存在
153
+ if (!settingsExists()) {
154
+ return res.status(400).json({
155
+ error: 'Claude Code settings.json not found. Please run Claude Code at least once.'
156
+ });
157
+ }
158
+
159
+ // Fix 4: Validate enabled channels count before starting proxy
160
+ const allChannels = getAllChannels();
161
+ const enabledCount = allChannels.filter(ch => ch.enabled !== false).length;
162
+ if (enabledCount === 0) {
163
+ return res.status(400).json({
164
+ error: '没有启用的渠道。请至少启用一个渠道后再启动动态切换。'
165
+ });
166
+ }
167
+
168
+ // 2. 从 settings.json 找到当前使用的渠道
169
+ const currentChannel = findActiveChannelFromSettings();
170
+ if (!currentChannel) {
171
+ return res.status(400).json({
172
+ error: '无法从 settings.json 识别当前渠道。请先激活一个渠道。'
173
+ });
174
+ }
175
+
176
+ // 3. 保存当前激活渠道ID(用于代理模式)
177
+ saveActiveChannelId(currentChannel.id);
178
+ console.log(`✅ Saved active channel: ${currentChannel.name} (${currentChannel.id})`);
179
+
180
+ // 4. 启动代理服务器
181
+ const proxyResult = await startProxyServer();
182
+
183
+ if (!proxyResult.success) {
184
+ return res.status(500).json({ error: 'Failed to start proxy server' });
185
+ }
186
+
187
+ // 5. 设置代理配置(备份并修改 settings.json)
188
+ setProxyConfig(proxyResult.port);
189
+
190
+ const updatedStatus = getProxyStatus();
191
+ const channels = getAllChannels();
192
+ const activeChannel = channels.find(ch => ch.enabled !== false);
193
+
194
+ // 6. 通过 WebSocket 推送代理状态更新
195
+ const { broadcastProxyState } = require('../websocket-server');
196
+ broadcastProxyState('claude', updatedStatus, activeChannel, channels);
197
+
198
+ res.json({
199
+ success: true,
200
+ port: proxyResult.port,
201
+ activeChannel: sanitizeChannelForResponse(currentChannel),
202
+ message: `代理已启动在端口 ${proxyResult.port},当前渠道: ${currentChannel.name}`
203
+ });
204
+ } catch (error) {
205
+ console.error('Error starting proxy:', error);
206
+ res.status(500).json({ error: error.message });
207
+ }
208
+ });
209
+
210
+ // 停止代理
211
+ router.post('/stop', async (req, res) => {
212
+ try {
213
+ // 1. 停止代理服务器
214
+ const proxyResult = await stopProxyServer();
215
+
216
+ // 2. 恢复配置(优先从备份,否则选择权重最高的启用渠道)
217
+ let restoredChannel = null;
218
+
219
+ // 优先尝试从备份恢复
220
+ if (hasBackup()) {
221
+ restoreSettings();
222
+ console.log('✅ Restored settings from backup');
223
+
224
+ // 尝试找到匹配的渠道
225
+ const channels = getAllChannels();
226
+ const currentSettings = require('../services/channels').getCurrentSettings();
227
+ if (currentSettings) {
228
+ restoredChannel = channels.find(ch =>
229
+ ch.baseUrl === currentSettings.baseUrl && ch.apiKey === currentSettings.apiKey
230
+ );
231
+ }
232
+ // Fallback: use first enabled channel
233
+ if (!restoredChannel) {
234
+ restoredChannel = channels.find(ch => ch.enabled !== false) || channels[0];
235
+ }
236
+ } else {
237
+ // 没有备份,选择权重最高的启用渠道
238
+ const { getBestChannelForRestore, updateClaudeSettings } = require('../services/channels');
239
+ restoredChannel = getBestChannelForRestore();
240
+
241
+ if (restoredChannel) {
242
+ updateClaudeSettings(restoredChannel.baseUrl, restoredChannel.apiKey);
243
+ console.log(`✅ Restored settings to best channel: ${restoredChannel.name}`);
244
+ }
245
+ }
246
+
247
+ // Enforce single-channel mode: disable all channels except the restored one
248
+ if (restoredChannel) {
249
+ const { applyChannelToSettings } = require('../services/channels');
250
+ applyChannelToSettings(restoredChannel.id);
251
+ console.log(`✅ Single-channel mode enforced: ${restoredChannel.name}`);
252
+ }
253
+
254
+ // 3. 删除备份文件和active-channel.json
255
+ if (hasBackup()) {
256
+ const backupPath = path.join(os.homedir(), '.claude', 'settings.json.cc-tool-backup');
257
+ if (fs.existsSync(backupPath)) {
258
+ fs.unlinkSync(backupPath);
259
+ console.log('✅ Removed backup file');
260
+ }
261
+ }
262
+
263
+ const activeChannelPath = PATHS.activeChannel.claude;
264
+ if (fs.existsSync(activeChannelPath)) {
265
+ fs.unlinkSync(activeChannelPath);
266
+ console.log('✅ Removed active-channel.json');
267
+ }
268
+
269
+ // 4. 通过 WebSocket 推送代理状态更新
270
+ const { broadcastProxyState } = require('../websocket-server');
271
+ const updatedStatus = getProxyStatus();
272
+ const channels = getAllChannels();
273
+ broadcastProxyState('claude', updatedStatus, null, channels);
274
+
275
+ if (restoredChannel) {
276
+ res.json({
277
+ success: true,
278
+ message: `代理已停止,配置已恢复到渠道: ${restoredChannel.name}`,
279
+ port: proxyResult.port,
280
+ restoredChannel: restoredChannel.name
281
+ });
282
+ } else {
283
+ res.json({
284
+ success: true,
285
+ message: '代理已停止(无配置可恢复)',
286
+ port: proxyResult.port
287
+ });
288
+ }
289
+ } catch (error) {
290
+ console.error('Error stopping proxy:', error);
291
+ res.status(500).json({ error: error.message });
292
+ }
293
+ });
294
+
295
+ // 清空日志
296
+ router.post('/logs/clear', (req, res) => {
297
+ try {
298
+ clearAllLogs();
299
+ res.json({ success: true, message: '日志已清空' });
300
+ } catch (error) {
301
+ console.error('Error clearing logs:', error);
302
+ res.status(500).json({ error: error.message });
303
+ }
304
+ });
305
+
306
+ module.exports = router;
@@ -0,0 +1,53 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const {
4
+ getSecurityStatus,
5
+ verifySecurityPassword,
6
+ setSecurityPassword
7
+ } = require('../services/security-config');
8
+
9
+ router.get('/', (req, res) => {
10
+ try {
11
+ const status = getSecurityStatus();
12
+ res.json({ success: true, ...status });
13
+ } catch (error) {
14
+ console.error('Error getting security status:', error);
15
+ res.status(500).json({ error: error.message });
16
+ }
17
+ });
18
+
19
+ router.post('/verify', (req, res) => {
20
+ try {
21
+ const { password } = req.body;
22
+ if (typeof password !== 'string' || !password) {
23
+ return res.status(400).json({ error: '请输入密码' });
24
+ }
25
+
26
+ const result = verifySecurityPassword(password);
27
+ if (result.reason === 'not_set') {
28
+ return res.status(400).json({ error: '尚未设置访问密码' });
29
+ }
30
+ if (!result.ok) {
31
+ return res.status(401).json({ error: '密码错误' });
32
+ }
33
+
34
+ res.json({ success: true });
35
+ } catch (error) {
36
+ console.error('Error verifying security password:', error);
37
+ res.status(500).json({ error: error.message });
38
+ }
39
+ });
40
+
41
+ router.post('/password', (req, res) => {
42
+ try {
43
+ const { currentPassword, newPassword } = req.body || {};
44
+ const result = setSecurityPassword({ currentPassword, newPassword });
45
+ res.json({ success: true, ...result });
46
+ } catch (error) {
47
+ const status = error.code === 'INVALID_PASSWORD' ? 401 : 400;
48
+ console.error('Error setting security password:', error);
49
+ res.status(status).json({ error: error.message });
50
+ }
51
+ });
52
+
53
+ module.exports = router;