@kaitranntt/ccs 6.7.0 → 6.7.1

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 (623) hide show
  1. package/README.md +45 -23
  2. package/dist/api/index.d.ts +7 -0
  3. package/dist/api/index.d.ts.map +1 -0
  4. package/dist/api/index.js +24 -0
  5. package/dist/api/index.js.map +1 -0
  6. package/dist/api/services/index.d.ts +10 -0
  7. package/dist/api/services/index.d.ts.map +1 -0
  8. package/dist/api/services/index.js +26 -0
  9. package/dist/api/services/index.js.map +1 -0
  10. package/dist/api/services/profile-reader.d.ts +28 -0
  11. package/dist/api/services/profile-reader.d.ts.map +1 -0
  12. package/dist/api/services/profile-reader.js +148 -0
  13. package/dist/api/services/profile-reader.js.map +1 -0
  14. package/dist/api/services/profile-types.d.ts +42 -0
  15. package/dist/api/services/profile-types.d.ts.map +1 -0
  16. package/dist/api/services/profile-types.js +8 -0
  17. package/dist/api/services/profile-types.js.map +1 -0
  18. package/dist/api/services/profile-writer.d.ts +6 -0
  19. package/dist/api/services/profile-writer.d.ts.map +1 -0
  20. package/dist/api/services/profile-writer.js +176 -0
  21. package/dist/api/services/profile-writer.js.map +1 -0
  22. package/dist/api/services/validation-service.d.ts +26 -0
  23. package/dist/api/services/validation-service.d.ts.map +1 -0
  24. package/dist/api/services/validation-service.js +77 -0
  25. package/dist/api/services/validation-service.js.map +1 -0
  26. package/dist/auth/auth-commands.d.ts +16 -22
  27. package/dist/auth/auth-commands.d.ts.map +1 -1
  28. package/dist/auth/auth-commands.js +29 -467
  29. package/dist/auth/auth-commands.js.map +1 -1
  30. package/dist/auth/commands/create-command.d.ts +11 -0
  31. package/dist/auth/commands/create-command.d.ts.map +1 -0
  32. package/dist/auth/commands/create-command.js +137 -0
  33. package/dist/auth/commands/create-command.js.map +1 -0
  34. package/dist/auth/commands/default-command.d.ts +15 -0
  35. package/dist/auth/commands/default-command.d.ts.map +1 -0
  36. package/dist/auth/commands/default-command.js +71 -0
  37. package/dist/auth/commands/default-command.js.map +1 -0
  38. package/dist/auth/commands/index.d.ts +12 -0
  39. package/dist/auth/commands/index.d.ts.map +1 -0
  40. package/dist/auth/commands/index.js +25 -0
  41. package/dist/auth/commands/index.js.map +1 -0
  42. package/dist/auth/commands/list-command.d.ts +11 -0
  43. package/dist/auth/commands/list-command.d.ts.map +1 -0
  44. package/dist/auth/commands/list-command.js +124 -0
  45. package/dist/auth/commands/list-command.js.map +1 -0
  46. package/dist/auth/commands/remove-command.d.ts +11 -0
  47. package/dist/auth/commands/remove-command.d.ts.map +1 -0
  48. package/dist/auth/commands/remove-command.js +102 -0
  49. package/dist/auth/commands/remove-command.js.map +1 -0
  50. package/dist/auth/commands/show-command.d.ts +11 -0
  51. package/dist/auth/commands/show-command.d.ts.map +1 -0
  52. package/dist/auth/commands/show-command.js +103 -0
  53. package/dist/auth/commands/show-command.js.map +1 -0
  54. package/dist/auth/commands/types.d.ts +50 -0
  55. package/dist/auth/commands/types.d.ts.map +1 -0
  56. package/dist/auth/commands/types.js +26 -0
  57. package/dist/auth/commands/types.js.map +1 -0
  58. package/dist/auth/index.d.ts +8 -0
  59. package/dist/auth/index.d.ts.map +1 -0
  60. package/dist/auth/index.js +16 -0
  61. package/dist/auth/index.js.map +1 -0
  62. package/dist/auth/profile-detector.d.ts +0 -5
  63. package/dist/auth/profile-detector.d.ts.map +1 -1
  64. package/dist/auth/profile-detector.js +1 -9
  65. package/dist/auth/profile-detector.js.map +1 -1
  66. package/dist/auth/profile-registry.d.ts +0 -4
  67. package/dist/auth/profile-registry.d.ts.map +1 -1
  68. package/dist/auth/profile-registry.js +3 -10
  69. package/dist/auth/profile-registry.js.map +1 -1
  70. package/dist/ccs.js +21 -4
  71. package/dist/ccs.js.map +1 -1
  72. package/dist/cliproxy/auth/auth-types.d.ts +85 -0
  73. package/dist/cliproxy/auth/auth-types.d.ts.map +1 -0
  74. package/dist/cliproxy/auth/auth-types.js +100 -0
  75. package/dist/cliproxy/auth/auth-types.js.map +1 -0
  76. package/dist/cliproxy/auth/environment-detector.d.ts +30 -0
  77. package/dist/cliproxy/auth/environment-detector.d.ts.map +1 -0
  78. package/dist/cliproxy/auth/environment-detector.js +124 -0
  79. package/dist/cliproxy/auth/environment-detector.js.map +1 -0
  80. package/dist/cliproxy/auth/index.d.ts +11 -0
  81. package/dist/cliproxy/auth/index.d.ts.map +1 -0
  82. package/dist/cliproxy/auth/index.js +35 -0
  83. package/dist/cliproxy/auth/index.js.map +1 -0
  84. package/dist/cliproxy/auth/oauth-handler.d.ts +29 -0
  85. package/dist/cliproxy/auth/oauth-handler.d.ts.map +1 -0
  86. package/dist/cliproxy/auth/oauth-handler.js +205 -0
  87. package/dist/cliproxy/auth/oauth-handler.js.map +1 -0
  88. package/dist/cliproxy/auth/oauth-process.d.ts +27 -0
  89. package/dist/cliproxy/auth/oauth-process.d.ts.map +1 -0
  90. package/dist/cliproxy/auth/oauth-process.js +209 -0
  91. package/dist/cliproxy/auth/oauth-process.js.map +1 -0
  92. package/dist/cliproxy/auth/token-manager.d.ts +53 -0
  93. package/dist/cliproxy/auth/token-manager.d.ts.map +1 -0
  94. package/dist/cliproxy/auth/token-manager.js +250 -0
  95. package/dist/cliproxy/auth/token-manager.js.map +1 -0
  96. package/dist/cliproxy/auth-handler.d.ts +13 -103
  97. package/dist/cliproxy/auth-handler.d.ts.map +1 -1
  98. package/dist/cliproxy/auth-handler.js +35 -766
  99. package/dist/cliproxy/auth-handler.js.map +1 -1
  100. package/dist/cliproxy/binary/downloader.d.ts +29 -0
  101. package/dist/cliproxy/binary/downloader.d.ts.map +1 -0
  102. package/dist/cliproxy/binary/downloader.js +218 -0
  103. package/dist/cliproxy/binary/downloader.js.map +1 -0
  104. package/dist/cliproxy/binary/extractor.d.ts +12 -0
  105. package/dist/cliproxy/binary/extractor.d.ts.map +1 -0
  106. package/dist/cliproxy/binary/extractor.js +27 -0
  107. package/dist/cliproxy/binary/extractor.js.map +1 -0
  108. package/dist/cliproxy/binary/index.d.ts +13 -0
  109. package/dist/cliproxy/binary/index.d.ts.map +1 -0
  110. package/dist/cliproxy/binary/index.js +53 -0
  111. package/dist/cliproxy/binary/index.js.map +1 -0
  112. package/dist/cliproxy/binary/installer.d.ts +24 -0
  113. package/dist/cliproxy/binary/installer.d.ts.map +1 -0
  114. package/dist/cliproxy/binary/installer.js +117 -0
  115. package/dist/cliproxy/binary/installer.js.map +1 -0
  116. package/dist/cliproxy/binary/lifecycle.d.ts +11 -0
  117. package/dist/cliproxy/binary/lifecycle.d.ts.map +1 -0
  118. package/dist/cliproxy/binary/lifecycle.js +106 -0
  119. package/dist/cliproxy/binary/lifecycle.js.map +1 -0
  120. package/dist/cliproxy/binary/tar-extractor.d.ts +9 -0
  121. package/dist/cliproxy/binary/tar-extractor.d.ts.map +1 -0
  122. package/dist/cliproxy/binary/tar-extractor.js +118 -0
  123. package/dist/cliproxy/binary/tar-extractor.js.map +1 -0
  124. package/dist/cliproxy/binary/types.d.ts +24 -0
  125. package/dist/cliproxy/binary/types.d.ts.map +1 -0
  126. package/dist/cliproxy/binary/types.js +14 -0
  127. package/dist/cliproxy/binary/types.js.map +1 -0
  128. package/dist/cliproxy/binary/updater.d.ts +7 -0
  129. package/dist/cliproxy/binary/updater.d.ts.map +1 -0
  130. package/dist/cliproxy/binary/updater.js +18 -0
  131. package/dist/cliproxy/binary/updater.js.map +1 -0
  132. package/dist/cliproxy/binary/verifier.d.ts +18 -0
  133. package/dist/cliproxy/binary/verifier.d.ts.map +1 -0
  134. package/dist/cliproxy/binary/verifier.js +82 -0
  135. package/dist/cliproxy/binary/verifier.js.map +1 -0
  136. package/dist/cliproxy/binary/version-cache.d.ts +46 -0
  137. package/dist/cliproxy/binary/version-cache.d.ts.map +1 -0
  138. package/dist/cliproxy/binary/version-cache.js +171 -0
  139. package/dist/cliproxy/binary/version-cache.js.map +1 -0
  140. package/dist/cliproxy/binary/version-checker.d.ts +19 -0
  141. package/dist/cliproxy/binary/version-checker.d.ts.map +1 -0
  142. package/dist/cliproxy/binary/version-checker.js +77 -0
  143. package/dist/cliproxy/binary/version-checker.js.map +1 -0
  144. package/dist/cliproxy/binary/zip-extractor.d.ts +9 -0
  145. package/dist/cliproxy/binary/zip-extractor.d.ts.map +1 -0
  146. package/dist/cliproxy/binary/zip-extractor.js +110 -0
  147. package/dist/cliproxy/binary/zip-extractor.js.map +1 -0
  148. package/dist/cliproxy/binary-manager.d.ts +16 -170
  149. package/dist/cliproxy/binary-manager.d.ts.map +1 -1
  150. package/dist/cliproxy/binary-manager.js +38 -865
  151. package/dist/cliproxy/binary-manager.js.map +1 -1
  152. package/dist/cliproxy/services/binary-service.d.ts +65 -0
  153. package/dist/cliproxy/services/binary-service.d.ts.map +1 -0
  154. package/dist/cliproxy/services/binary-service.js +140 -0
  155. package/dist/cliproxy/services/binary-service.js.map +1 -0
  156. package/dist/cliproxy/services/index.d.ts +8 -0
  157. package/dist/cliproxy/services/index.d.ts.map +1 -0
  158. package/dist/cliproxy/services/index.js +31 -0
  159. package/dist/cliproxy/services/index.js.map +1 -0
  160. package/dist/cliproxy/services/proxy-lifecycle-service.d.ts +38 -0
  161. package/dist/cliproxy/services/proxy-lifecycle-service.d.ts.map +1 -0
  162. package/dist/cliproxy/services/proxy-lifecycle-service.js +41 -0
  163. package/dist/cliproxy/services/proxy-lifecycle-service.js.map +1 -0
  164. package/dist/cliproxy/services/variant-config-adapter.d.ts +38 -0
  165. package/dist/cliproxy/services/variant-config-adapter.d.ts.map +1 -0
  166. package/dist/cliproxy/services/variant-config-adapter.js +172 -0
  167. package/dist/cliproxy/services/variant-config-adapter.js.map +1 -0
  168. package/dist/cliproxy/services/variant-service.d.ts +37 -0
  169. package/dist/cliproxy/services/variant-service.d.ts.map +1 -0
  170. package/dist/cliproxy/services/variant-service.js +128 -0
  171. package/dist/cliproxy/services/variant-service.js.map +1 -0
  172. package/dist/cliproxy/services/variant-settings.d.ts +32 -0
  173. package/dist/cliproxy/services/variant-settings.d.ts.map +1 -0
  174. package/dist/cliproxy/services/variant-settings.js +128 -0
  175. package/dist/cliproxy/services/variant-settings.js.map +1 -0
  176. package/dist/commands/api-command.d.ts +3 -6
  177. package/dist/commands/api-command.d.ts.map +1 -1
  178. package/dist/commands/api-command.js +84 -445
  179. package/dist/commands/api-command.js.map +1 -1
  180. package/dist/commands/cliproxy-command.d.ts +0 -3
  181. package/dist/commands/cliproxy-command.d.ts.map +1 -1
  182. package/dist/commands/cliproxy-command.js +182 -621
  183. package/dist/commands/cliproxy-command.js.map +1 -1
  184. package/dist/commands/index.d.ts +17 -0
  185. package/dist/commands/index.d.ts.map +1 -0
  186. package/dist/commands/index.js +33 -0
  187. package/dist/commands/index.js.map +1 -0
  188. package/dist/config/unified-config-loader.d.ts +7 -0
  189. package/dist/config/unified-config-loader.d.ts.map +1 -1
  190. package/dist/config/unified-config-loader.js +11 -1
  191. package/dist/config/unified-config-loader.js.map +1 -1
  192. package/dist/delegation/executor/index.d.ts +7 -0
  193. package/dist/delegation/executor/index.d.ts.map +1 -0
  194. package/dist/delegation/executor/index.js +28 -0
  195. package/dist/delegation/executor/index.js.map +1 -0
  196. package/dist/delegation/executor/result-aggregator.d.ts +30 -0
  197. package/dist/delegation/executor/result-aggregator.d.ts.map +1 -0
  198. package/dist/delegation/executor/result-aggregator.js +66 -0
  199. package/dist/delegation/executor/result-aggregator.js.map +1 -0
  200. package/dist/delegation/executor/stream-parser.d.ts +28 -0
  201. package/dist/delegation/executor/stream-parser.d.ts.map +1 -0
  202. package/dist/delegation/executor/stream-parser.js +140 -0
  203. package/dist/delegation/executor/stream-parser.js.map +1 -0
  204. package/dist/delegation/executor/types.d.ts +121 -0
  205. package/dist/delegation/executor/types.d.ts.map +1 -0
  206. package/dist/delegation/executor/types.js +6 -0
  207. package/dist/delegation/executor/types.js.map +1 -0
  208. package/dist/delegation/headless-executor.d.ts +13 -91
  209. package/dist/delegation/headless-executor.d.ts.map +1 -1
  210. package/dist/delegation/headless-executor.js +84 -291
  211. package/dist/delegation/headless-executor.js.map +1 -1
  212. package/dist/delegation/index.d.ts +11 -0
  213. package/dist/delegation/index.d.ts.map +1 -0
  214. package/dist/delegation/index.js +33 -0
  215. package/dist/delegation/index.js.map +1 -0
  216. package/dist/delegation/result-formatter.d.ts +1 -30
  217. package/dist/delegation/result-formatter.d.ts.map +1 -1
  218. package/dist/delegation/result-formatter.js.map +1 -1
  219. package/dist/errors/cleanup-registry.d.ts +64 -0
  220. package/dist/errors/cleanup-registry.d.ts.map +1 -0
  221. package/dist/errors/cleanup-registry.js +141 -0
  222. package/dist/errors/cleanup-registry.js.map +1 -0
  223. package/dist/errors/error-handler.d.ts +45 -0
  224. package/dist/errors/error-handler.d.ts.map +1 -0
  225. package/dist/errors/error-handler.js +150 -0
  226. package/dist/errors/error-handler.js.map +1 -0
  227. package/dist/errors/error-types.d.ts +102 -0
  228. package/dist/errors/error-types.d.ts.map +1 -0
  229. package/dist/errors/error-types.js +158 -0
  230. package/dist/errors/error-types.js.map +1 -0
  231. package/dist/errors/exit-codes.d.ts +49 -0
  232. package/dist/errors/exit-codes.d.ts.map +1 -0
  233. package/dist/errors/exit-codes.js +72 -0
  234. package/dist/errors/exit-codes.js.map +1 -0
  235. package/dist/errors/index.d.ts +29 -0
  236. package/dist/errors/index.d.ts.map +1 -0
  237. package/dist/errors/index.js +62 -0
  238. package/dist/errors/index.js.map +1 -0
  239. package/dist/glmt/glmt-transformer.d.ts +29 -248
  240. package/dist/glmt/glmt-transformer.d.ts.map +1 -1
  241. package/dist/glmt/glmt-transformer.js +63 -718
  242. package/dist/glmt/glmt-transformer.js.map +1 -1
  243. package/dist/glmt/index.d.ts +14 -0
  244. package/dist/glmt/index.d.ts.map +1 -0
  245. package/dist/glmt/index.js +41 -0
  246. package/dist/glmt/index.js.map +1 -0
  247. package/dist/glmt/pipeline/content-transformer.d.ts +39 -0
  248. package/dist/glmt/pipeline/content-transformer.d.ts.map +1 -0
  249. package/dist/glmt/pipeline/content-transformer.js +162 -0
  250. package/dist/glmt/pipeline/content-transformer.js.map +1 -0
  251. package/dist/glmt/pipeline/index.d.ts +12 -0
  252. package/dist/glmt/pipeline/index.d.ts.map +1 -0
  253. package/dist/glmt/pipeline/index.js +20 -0
  254. package/dist/glmt/pipeline/index.js.map +1 -0
  255. package/dist/glmt/pipeline/request-transformer.d.ts +31 -0
  256. package/dist/glmt/pipeline/request-transformer.d.ts.map +1 -0
  257. package/dist/glmt/pipeline/request-transformer.js +116 -0
  258. package/dist/glmt/pipeline/request-transformer.js.map +1 -0
  259. package/dist/glmt/pipeline/response-builder.d.ts +52 -0
  260. package/dist/glmt/pipeline/response-builder.d.ts.map +1 -0
  261. package/dist/glmt/pipeline/response-builder.js +205 -0
  262. package/dist/glmt/pipeline/response-builder.js.map +1 -0
  263. package/dist/glmt/pipeline/stream-parser.d.ts +59 -0
  264. package/dist/glmt/pipeline/stream-parser.d.ts.map +1 -0
  265. package/dist/glmt/pipeline/stream-parser.js +241 -0
  266. package/dist/glmt/pipeline/stream-parser.js.map +1 -0
  267. package/dist/glmt/pipeline/tool-call-handler.d.ts +22 -0
  268. package/dist/glmt/pipeline/tool-call-handler.d.ts.map +1 -0
  269. package/dist/glmt/pipeline/tool-call-handler.js +87 -0
  270. package/dist/glmt/pipeline/tool-call-handler.js.map +1 -0
  271. package/dist/glmt/pipeline/types.d.ts +159 -0
  272. package/dist/glmt/pipeline/types.d.ts.map +1 -0
  273. package/dist/glmt/pipeline/types.js +6 -0
  274. package/dist/glmt/pipeline/types.js.map +1 -0
  275. package/dist/management/checks/cliproxy-check.d.ts +37 -0
  276. package/dist/management/checks/cliproxy-check.d.ts.map +1 -0
  277. package/dist/management/checks/cliproxy-check.js +187 -0
  278. package/dist/management/checks/cliproxy-check.js.map +1 -0
  279. package/dist/management/checks/config-check.d.ts +27 -0
  280. package/dist/management/checks/config-check.d.ts.map +1 -0
  281. package/dist/management/checks/config-check.js +158 -0
  282. package/dist/management/checks/config-check.js.map +1 -0
  283. package/dist/management/checks/env-check.d.ts +17 -0
  284. package/dist/management/checks/env-check.d.ts.map +1 -0
  285. package/dist/management/checks/env-check.js +71 -0
  286. package/dist/management/checks/env-check.js.map +1 -0
  287. package/dist/management/checks/index.d.ts +12 -0
  288. package/dist/management/checks/index.d.ts.map +1 -0
  289. package/dist/management/checks/index.js +48 -0
  290. package/dist/management/checks/index.js.map +1 -0
  291. package/dist/management/checks/oauth-check.d.ts +16 -0
  292. package/dist/management/checks/oauth-check.d.ts.map +1 -0
  293. package/dist/management/checks/oauth-check.js +68 -0
  294. package/dist/management/checks/oauth-check.js.map +1 -0
  295. package/dist/management/checks/profile-check.d.ts +36 -0
  296. package/dist/management/checks/profile-check.d.ts.map +1 -0
  297. package/dist/management/checks/profile-check.js +165 -0
  298. package/dist/management/checks/profile-check.js.map +1 -0
  299. package/dist/management/checks/symlink-check.d.ts +30 -0
  300. package/dist/management/checks/symlink-check.d.ts.map +1 -0
  301. package/dist/management/checks/symlink-check.js +204 -0
  302. package/dist/management/checks/symlink-check.js.map +1 -0
  303. package/dist/management/checks/system-check.d.ts +25 -0
  304. package/dist/management/checks/system-check.d.ts.map +1 -0
  305. package/dist/management/checks/system-check.js +136 -0
  306. package/dist/management/checks/system-check.js.map +1 -0
  307. package/dist/management/checks/types.d.ts +64 -0
  308. package/dist/management/checks/types.d.ts.map +1 -0
  309. package/dist/management/checks/types.js +63 -0
  310. package/dist/management/checks/types.js.map +1 -0
  311. package/dist/management/doctor.d.ts +3 -92
  312. package/dist/management/doctor.d.ts.map +1 -1
  313. package/dist/management/doctor.js +15 -831
  314. package/dist/management/doctor.js.map +1 -1
  315. package/dist/management/index.d.ts +15 -0
  316. package/dist/management/index.d.ts.map +1 -0
  317. package/dist/management/index.js +56 -0
  318. package/dist/management/index.js.map +1 -0
  319. package/dist/management/repair/auto-repair.d.ts +13 -0
  320. package/dist/management/repair/auto-repair.d.ts.map +1 -0
  321. package/dist/management/repair/auto-repair.js +170 -0
  322. package/dist/management/repair/auto-repair.js.map +1 -0
  323. package/dist/management/repair/index.d.ts +5 -0
  324. package/dist/management/repair/index.d.ts.map +1 -0
  325. package/dist/management/repair/index.js +9 -0
  326. package/dist/management/repair/index.js.map +1 -0
  327. package/dist/types/delegation.d.ts +1 -13
  328. package/dist/types/delegation.d.ts.map +1 -1
  329. package/dist/types/utils.d.ts +27 -0
  330. package/dist/types/utils.d.ts.map +1 -1
  331. package/dist/ui/assets/{accounts-p1_nf0Jy.js → accounts-BqDNXZu4.js} +1 -1
  332. package/dist/ui/assets/analytics-DQMyOsqI.js +1 -0
  333. package/dist/ui/assets/api-Bzyq4XP7.js +1 -0
  334. package/dist/ui/assets/{card-CCDc-Mx9.js → card-CIN0KDsX.js} +1 -1
  335. package/dist/ui/assets/cliproxy-5iKdPrEG.js +3 -0
  336. package/dist/ui/assets/cliproxy-control-panel-CJ2CfYod.js +1 -0
  337. package/dist/ui/assets/{code-editor-Br9x-r-E.js → code-editor-CDmNFLQ6.js} +1 -1
  338. package/dist/ui/assets/confirm-dialog-CaiDQikc.js +1 -0
  339. package/dist/ui/assets/copilot-nS6iAyk2.js +4 -0
  340. package/dist/ui/assets/form-utils-DP6ILe7Z.js +20 -0
  341. package/dist/ui/assets/health-DYOQfC7u.js +1 -0
  342. package/dist/ui/assets/{icons-BOsxPbiD.js → icons-ZmwVoUeR.js} +1 -1
  343. package/dist/ui/assets/index-C6Dah-xh.js +46 -0
  344. package/dist/ui/assets/index-It66SkKf.css +1 -0
  345. package/dist/ui/assets/{radix-ui-DFHQr9A5.js → radix-ui-CV3R9pD6.js} +3 -3
  346. package/dist/ui/assets/settings-DABC9b5G.js +1 -0
  347. package/dist/ui/assets/{shared-BfYhSN4-.js → shared-BwU4OKQc.js} +1 -1
  348. package/dist/ui/assets/{switch-CMk95lwf.js → switch-BQ6sBBUv.js} +1 -1
  349. package/dist/ui/assets/{tanstack-C4gT2P7V.js → tanstack-Df9bCj5R.js} +1 -1
  350. package/dist/ui/index.html +6 -6
  351. package/dist/utils/claude-spawner.d.ts +54 -0
  352. package/dist/utils/claude-spawner.d.ts.map +1 -0
  353. package/dist/utils/claude-spawner.js +118 -0
  354. package/dist/utils/claude-spawner.js.map +1 -0
  355. package/dist/utils/delegation-validator.d.ts +8 -4
  356. package/dist/utils/delegation-validator.d.ts.map +1 -1
  357. package/dist/utils/delegation-validator.js.map +1 -1
  358. package/dist/utils/helpers.d.ts +0 -11
  359. package/dist/utils/helpers.d.ts.map +1 -1
  360. package/dist/utils/helpers.js +1 -57
  361. package/dist/utils/helpers.js.map +1 -1
  362. package/dist/utils/index.d.ts +15 -0
  363. package/dist/utils/index.d.ts.map +1 -0
  364. package/dist/utils/index.js +50 -0
  365. package/dist/utils/index.js.map +1 -0
  366. package/dist/utils/time.d.ts +10 -0
  367. package/dist/utils/time.d.ts.map +1 -0
  368. package/dist/utils/time.js +27 -0
  369. package/dist/utils/time.js.map +1 -0
  370. package/dist/utils/ui/boxes.d.ts +24 -0
  371. package/dist/utils/ui/boxes.d.ts.map +1 -0
  372. package/dist/utils/ui/boxes.js +108 -0
  373. package/dist/utils/ui/boxes.js.map +1 -0
  374. package/dist/utils/ui/colors.d.ts +25 -0
  375. package/dist/utils/ui/colors.d.ts.map +1 -0
  376. package/dist/utils/ui/colors.js +70 -0
  377. package/dist/utils/ui/colors.js.map +1 -0
  378. package/dist/utils/ui/index.d.ts +51 -0
  379. package/dist/utils/ui/index.d.ts.map +1 -0
  380. package/dist/utils/ui/index.js +96 -0
  381. package/dist/utils/ui/index.js.map +1 -0
  382. package/dist/utils/ui/indicators.d.ts +23 -0
  383. package/dist/utils/ui/indicators.d.ts.map +1 -0
  384. package/dist/utils/ui/indicators.js +39 -0
  385. package/dist/utils/ui/indicators.js.map +1 -0
  386. package/dist/utils/ui/init.d.ts +30 -0
  387. package/dist/utils/ui/init.d.ts.map +1 -0
  388. package/dist/utils/ui/init.js +102 -0
  389. package/dist/utils/ui/init.js.map +1 -0
  390. package/dist/utils/ui/spinner.d.ts +13 -0
  391. package/dist/utils/ui/spinner.d.ts.map +1 -0
  392. package/dist/utils/ui/spinner.js +89 -0
  393. package/dist/utils/ui/spinner.js.map +1 -0
  394. package/dist/utils/ui/tables.d.ts +12 -0
  395. package/dist/utils/ui/tables.d.ts.map +1 -0
  396. package/dist/utils/ui/tables.js +69 -0
  397. package/dist/utils/ui/tables.js.map +1 -0
  398. package/dist/utils/ui/tasks.d.ts +26 -0
  399. package/dist/utils/ui/tasks.d.ts.map +1 -0
  400. package/dist/utils/ui/tasks.js +102 -0
  401. package/dist/utils/ui/tasks.js.map +1 -0
  402. package/dist/utils/ui/text.d.ts +24 -0
  403. package/dist/utils/ui/text.d.ts.map +1 -0
  404. package/dist/utils/ui/text.js +60 -0
  405. package/dist/utils/ui/text.js.map +1 -0
  406. package/dist/utils/ui/types.d.ts +36 -0
  407. package/dist/utils/ui/types.d.ts.map +1 -0
  408. package/dist/utils/ui/types.js +33 -0
  409. package/dist/utils/ui/types.js.map +1 -0
  410. package/dist/utils/ui.d.ts +3 -138
  411. package/dist/utils/ui.d.ts.map +1 -1
  412. package/dist/utils/ui.js +40 -567
  413. package/dist/utils/ui.js.map +1 -1
  414. package/dist/utils/websearch/gemini-cli.d.ts +36 -0
  415. package/dist/utils/websearch/gemini-cli.d.ts.map +1 -0
  416. package/dist/utils/websearch/gemini-cli.js +132 -0
  417. package/dist/utils/websearch/gemini-cli.js.map +1 -0
  418. package/dist/utils/websearch/grok-cli.d.ts +26 -0
  419. package/dist/utils/websearch/grok-cli.d.ts.map +1 -0
  420. package/dist/utils/websearch/grok-cli.js +81 -0
  421. package/dist/utils/websearch/grok-cli.js.map +1 -0
  422. package/dist/utils/websearch/hook-config.d.ts +21 -0
  423. package/dist/utils/websearch/hook-config.d.ts.map +1 -0
  424. package/dist/utils/websearch/hook-config.js +183 -0
  425. package/dist/utils/websearch/hook-config.js.map +1 -0
  426. package/dist/utils/websearch/hook-env.d.ts +16 -0
  427. package/dist/utils/websearch/hook-env.d.ts.map +1 -0
  428. package/dist/utils/websearch/hook-env.js +62 -0
  429. package/dist/utils/websearch/hook-env.js.map +1 -0
  430. package/dist/utils/websearch/hook-installer.d.ts +27 -0
  431. package/dist/utils/websearch/hook-installer.d.ts.map +1 -0
  432. package/dist/utils/websearch/hook-installer.js +141 -0
  433. package/dist/utils/websearch/hook-installer.js.map +1 -0
  434. package/dist/utils/websearch/index.d.ts +15 -0
  435. package/dist/utils/websearch/index.d.ts.map +1 -0
  436. package/dist/utils/websearch/index.js +44 -0
  437. package/dist/utils/websearch/index.js.map +1 -0
  438. package/dist/utils/websearch/opencode-cli.d.ts +26 -0
  439. package/dist/utils/websearch/opencode-cli.d.ts.map +1 -0
  440. package/dist/utils/websearch/opencode-cli.js +81 -0
  441. package/dist/utils/websearch/opencode-cli.js.map +1 -0
  442. package/dist/utils/websearch/status.d.ts +35 -0
  443. package/dist/utils/websearch/status.d.ts.map +1 -0
  444. package/dist/utils/websearch/status.js +185 -0
  445. package/dist/utils/websearch/status.js.map +1 -0
  446. package/dist/utils/websearch/types.d.ts +85 -0
  447. package/dist/utils/websearch/types.d.ts.map +1 -0
  448. package/dist/utils/websearch/types.js +10 -0
  449. package/dist/utils/websearch/types.js.map +1 -0
  450. package/dist/utils/websearch-manager.d.ts +7 -186
  451. package/dist/utils/websearch-manager.d.ts.map +1 -1
  452. package/dist/utils/websearch-manager.js +37 -710
  453. package/dist/utils/websearch-manager.js.map +1 -1
  454. package/dist/web-server/data-aggregator.d.ts +3 -51
  455. package/dist/web-server/data-aggregator.d.ts.map +1 -1
  456. package/dist/web-server/data-aggregator.js +18 -386
  457. package/dist/web-server/data-aggregator.js.map +1 -1
  458. package/dist/web-server/health/cliproxy-checks.d.ts +23 -0
  459. package/dist/web-server/health/cliproxy-checks.d.ts.map +1 -0
  460. package/dist/web-server/health/cliproxy-checks.js +145 -0
  461. package/dist/web-server/health/cliproxy-checks.js.map +1 -0
  462. package/dist/web-server/health/config-checks.d.ts +19 -0
  463. package/dist/web-server/health/config-checks.d.ts.map +1 -0
  464. package/dist/web-server/health/config-checks.js +174 -0
  465. package/dist/web-server/health/config-checks.js.map +1 -0
  466. package/dist/web-server/health/environment-checks.d.ts +11 -0
  467. package/dist/web-server/health/environment-checks.d.ts.map +1 -0
  468. package/dist/web-server/health/environment-checks.js +40 -0
  469. package/dist/web-server/health/environment-checks.js.map +1 -0
  470. package/dist/web-server/health/index.d.ts +13 -0
  471. package/dist/web-server/health/index.d.ts.map +1 -0
  472. package/dist/web-server/health/index.js +41 -0
  473. package/dist/web-server/health/index.js.map +1 -0
  474. package/dist/web-server/health/oauth-checks.d.ts +11 -0
  475. package/dist/web-server/health/oauth-checks.d.ts.map +1 -0
  476. package/dist/web-server/health/oauth-checks.js +34 -0
  477. package/dist/web-server/health/oauth-checks.js.map +1 -0
  478. package/dist/web-server/health/profile-checks.d.ts +19 -0
  479. package/dist/web-server/health/profile-checks.d.ts.map +1 -0
  480. package/dist/web-server/health/profile-checks.js +152 -0
  481. package/dist/web-server/health/profile-checks.js.map +1 -0
  482. package/dist/web-server/health/symlink-checks.d.ts +15 -0
  483. package/dist/web-server/health/symlink-checks.d.ts.map +1 -0
  484. package/dist/web-server/health/symlink-checks.js +173 -0
  485. package/dist/web-server/health/symlink-checks.js.map +1 -0
  486. package/dist/web-server/health/system-checks.d.ts +19 -0
  487. package/dist/web-server/health/system-checks.d.ts.map +1 -0
  488. package/dist/web-server/health/system-checks.js +127 -0
  489. package/dist/web-server/health/system-checks.js.map +1 -0
  490. package/dist/web-server/health/types.d.ts +34 -0
  491. package/dist/web-server/health/types.d.ts.map +1 -0
  492. package/dist/web-server/health/types.js +8 -0
  493. package/dist/web-server/health/types.js.map +1 -0
  494. package/dist/web-server/health/websearch-checks.d.ts +11 -0
  495. package/dist/web-server/health/websearch-checks.d.ts.map +1 -0
  496. package/dist/web-server/health/websearch-checks.js +53 -0
  497. package/dist/web-server/health/websearch-checks.js.map +1 -0
  498. package/dist/web-server/health-service.d.ts +4 -30
  499. package/dist/web-server/health-service.d.ts.map +1 -1
  500. package/dist/web-server/health-service.js +28 -695
  501. package/dist/web-server/health-service.js.map +1 -1
  502. package/dist/web-server/index.d.ts.map +1 -1
  503. package/dist/web-server/index.js +2 -5
  504. package/dist/web-server/index.js.map +1 -1
  505. package/dist/web-server/routes/cliproxy-auth-routes.d.ts +6 -0
  506. package/dist/web-server/routes/cliproxy-auth-routes.d.ts.map +1 -0
  507. package/dist/web-server/routes/cliproxy-auth-routes.js +202 -0
  508. package/dist/web-server/routes/cliproxy-auth-routes.js.map +1 -0
  509. package/dist/web-server/routes/cliproxy-stats-routes.d.ts +6 -0
  510. package/dist/web-server/routes/cliproxy-stats-routes.d.ts.map +1 -0
  511. package/dist/web-server/routes/cliproxy-stats-routes.js +253 -0
  512. package/dist/web-server/routes/cliproxy-stats-routes.js.map +1 -0
  513. package/dist/web-server/routes/config-routes.d.ts +6 -0
  514. package/dist/web-server/routes/config-routes.d.ts.map +1 -0
  515. package/dist/web-server/routes/config-routes.js +145 -0
  516. package/dist/web-server/routes/config-routes.js.map +1 -0
  517. package/dist/web-server/routes/copilot-routes.d.ts +6 -0
  518. package/dist/web-server/routes/copilot-routes.d.ts.map +1 -0
  519. package/dist/web-server/routes/copilot-routes.js +195 -0
  520. package/dist/web-server/routes/copilot-routes.js.map +1 -0
  521. package/dist/web-server/routes/copilot-settings-routes.d.ts +6 -0
  522. package/dist/web-server/routes/copilot-settings-routes.d.ts.map +1 -0
  523. package/dist/web-server/routes/copilot-settings-routes.js +122 -0
  524. package/dist/web-server/routes/copilot-settings-routes.js.map +1 -0
  525. package/dist/web-server/routes/health-routes.d.ts +6 -0
  526. package/dist/web-server/routes/health-routes.d.ts.map +1 -0
  527. package/dist/web-server/routes/health-routes.js +30 -0
  528. package/dist/web-server/routes/health-routes.js.map +1 -0
  529. package/dist/web-server/routes/index.d.ts +8 -0
  530. package/dist/web-server/routes/index.d.ts.map +1 -0
  531. package/dist/web-server/routes/index.js +54 -0
  532. package/dist/web-server/routes/index.js.map +1 -0
  533. package/dist/web-server/routes/misc-routes.d.ts +6 -0
  534. package/dist/web-server/routes/misc-routes.d.ts.map +1 -0
  535. package/dist/web-server/routes/misc-routes.js +219 -0
  536. package/dist/web-server/routes/misc-routes.js.map +1 -0
  537. package/dist/web-server/routes/profile-routes.d.ts +6 -0
  538. package/dist/web-server/routes/profile-routes.d.ts.map +1 -0
  539. package/dist/web-server/routes/profile-routes.js +164 -0
  540. package/dist/web-server/routes/profile-routes.js.map +1 -0
  541. package/dist/web-server/routes/provider-routes.d.ts +6 -0
  542. package/dist/web-server/routes/provider-routes.d.ts.map +1 -0
  543. package/dist/web-server/routes/provider-routes.js +146 -0
  544. package/dist/web-server/routes/provider-routes.js.map +1 -0
  545. package/dist/web-server/routes/route-helpers.d.ts +55 -0
  546. package/dist/web-server/routes/route-helpers.d.ts.map +1 -0
  547. package/dist/web-server/routes/route-helpers.js +201 -0
  548. package/dist/web-server/routes/route-helpers.js.map +1 -0
  549. package/dist/web-server/routes/settings-routes.d.ts +6 -0
  550. package/dist/web-server/routes/settings-routes.d.ts.map +1 -0
  551. package/dist/web-server/routes/settings-routes.js +207 -0
  552. package/dist/web-server/routes/settings-routes.js.map +1 -0
  553. package/dist/web-server/routes/variant-routes.d.ts +6 -0
  554. package/dist/web-server/routes/variant-routes.d.ts.map +1 -0
  555. package/dist/web-server/routes/variant-routes.js +158 -0
  556. package/dist/web-server/routes/variant-routes.js.map +1 -0
  557. package/dist/web-server/routes/websearch-routes.d.ts +6 -0
  558. package/dist/web-server/routes/websearch-routes.d.ts.map +1 -0
  559. package/dist/web-server/routes/websearch-routes.js +130 -0
  560. package/dist/web-server/routes/websearch-routes.js.map +1 -0
  561. package/dist/web-server/services/index.d.ts +7 -0
  562. package/dist/web-server/services/index.d.ts.map +1 -0
  563. package/dist/web-server/services/index.js +21 -0
  564. package/dist/web-server/services/index.js.map +1 -0
  565. package/dist/web-server/services/usage-aggregator.d.ts +7 -0
  566. package/dist/web-server/services/usage-aggregator.d.ts.map +1 -0
  567. package/dist/web-server/services/usage-aggregator.js +23 -0
  568. package/dist/web-server/services/usage-aggregator.js.map +1 -0
  569. package/dist/web-server/usage/aggregator.d.ts +54 -0
  570. package/dist/web-server/usage/aggregator.d.ts.map +1 -0
  571. package/dist/web-server/usage/aggregator.js +470 -0
  572. package/dist/web-server/usage/aggregator.js.map +1 -0
  573. package/dist/web-server/usage/data-aggregator.d.ts +55 -0
  574. package/dist/web-server/usage/data-aggregator.d.ts.map +1 -0
  575. package/dist/web-server/usage/data-aggregator.js +391 -0
  576. package/dist/web-server/usage/data-aggregator.js.map +1 -0
  577. package/dist/web-server/usage/disk-cache.d.ts +46 -0
  578. package/dist/web-server/usage/disk-cache.d.ts.map +1 -0
  579. package/dist/web-server/usage/disk-cache.js +162 -0
  580. package/dist/web-server/usage/disk-cache.js.map +1 -0
  581. package/dist/web-server/usage/handlers.d.ts +50 -0
  582. package/dist/web-server/usage/handlers.d.ts.map +1 -0
  583. package/dist/web-server/usage/handlers.js +524 -0
  584. package/dist/web-server/usage/handlers.js.map +1 -0
  585. package/dist/web-server/usage/index.d.ts +12 -0
  586. package/dist/web-server/usage/index.d.ts.map +1 -0
  587. package/dist/web-server/usage/index.js +54 -0
  588. package/dist/web-server/usage/index.js.map +1 -0
  589. package/dist/web-server/usage/routes.d.ts +11 -0
  590. package/dist/web-server/usage/routes.d.ts.map +1 -0
  591. package/dist/web-server/usage/routes.js +37 -0
  592. package/dist/web-server/usage/routes.js.map +1 -0
  593. package/dist/web-server/usage/types.d.ts +119 -0
  594. package/dist/web-server/usage/types.d.ts.map +1 -0
  595. package/dist/web-server/usage/types.js +9 -0
  596. package/dist/web-server/usage/types.js.map +1 -0
  597. package/dist/web-server/usage-disk-cache.d.ts +3 -42
  598. package/dist/web-server/usage-disk-cache.d.ts.map +1 -1
  599. package/dist/web-server/usage-disk-cache.js +5 -144
  600. package/dist/web-server/usage-disk-cache.js.map +1 -1
  601. package/dist/web-server/usage-routes.d.ts +3 -31
  602. package/dist/web-server/usage-routes.d.ts.map +1 -1
  603. package/dist/web-server/usage-routes.js +8 -1139
  604. package/dist/web-server/usage-routes.js.map +1 -1
  605. package/dist/web-server/usage-types.d.ts +3 -115
  606. package/dist/web-server/usage-types.d.ts.map +1 -1
  607. package/dist/web-server/usage-types.js +17 -3
  608. package/dist/web-server/usage-types.js.map +1 -1
  609. package/package.json +1 -1
  610. package/dist/ui/assets/analytics-D1k2nYcF.js +0 -1
  611. package/dist/ui/assets/api-D9VVmZ1j.js +0 -1
  612. package/dist/ui/assets/cliproxy-CTt_Nsft.js +0 -1
  613. package/dist/ui/assets/cliproxy-control-panel-DKMAi4_4.js +0 -1
  614. package/dist/ui/assets/copilot-C6F8PT_L.js +0 -4
  615. package/dist/ui/assets/form-utils-BuXDJb0w.js +0 -20
  616. package/dist/ui/assets/health-DiAvJaSn.js +0 -1
  617. package/dist/ui/assets/index-Brq6EBKZ.css +0 -1
  618. package/dist/ui/assets/index-ByU8ZhED.js +0 -48
  619. package/dist/ui/assets/settings-CEomZLGl.js +0 -1
  620. package/dist/web-server/routes.d.ts +0 -7
  621. package/dist/web-server/routes.d.ts.map +0 -1
  622. package/dist/web-server/routes.js +0 -1891
  623. package/dist/web-server/routes.js.map +0 -1
@@ -1,1891 +0,0 @@
1
- "use strict";
2
- /**
3
- * REST API Routes (Phase 03)
4
- *
5
- * Implements CRUD operations for profiles, cliproxy variants, and accounts.
6
- */
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || function (mod) {
24
- if (mod && mod.__esModule) return mod;
25
- var result = {};
26
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
27
- __setModuleDefault(result, mod);
28
- return result;
29
- };
30
- Object.defineProperty(exports, "__esModule", { value: true });
31
- exports.apiRoutes = void 0;
32
- const express_1 = require("express");
33
- const fs = __importStar(require("fs"));
34
- const path = __importStar(require("path"));
35
- const config_manager_1 = require("../utils/config-manager");
36
- const helpers_1 = require("../utils/helpers");
37
- const health_service_1 = require("./health-service");
38
- const auth_handler_1 = require("../cliproxy/auth-handler");
39
- const project_selection_handler_1 = require("../cliproxy/project-selection-handler");
40
- const stats_fetcher_1 = require("../cliproxy/stats-fetcher");
41
- const config_generator_1 = require("../cliproxy/config-generator");
42
- const openai_compat_manager_1 = require("../cliproxy/openai-compat-manager");
43
- const account_manager_1 = require("../cliproxy/account-manager");
44
- const config_generator_2 = require("../cliproxy/config-generator");
45
- const session_tracker_1 = require("../cliproxy/session-tracker");
46
- const service_manager_1 = require("../cliproxy/service-manager");
47
- const binary_manager_1 = require("../cliproxy/binary-manager");
48
- // Unified config imports
49
- const unified_config_loader_1 = require("../config/unified-config-loader");
50
- const migration_manager_1 = require("../config/migration-manager");
51
- const secrets_manager_1 = require("../config/secrets-manager");
52
- const unified_config_loader_2 = require("../config/unified-config-loader");
53
- const unified_config_types_1 = require("../config/unified-config-types");
54
- const sensitive_keys_1 = require("../utils/sensitive-keys");
55
- const reserved_names_1 = require("../config/reserved-names");
56
- const websearch_manager_1 = require("../utils/websearch-manager");
57
- exports.apiRoutes = (0, express_1.Router)();
58
- /**
59
- * Helper: Read config safely with fallback
60
- */
61
- function readConfigSafe() {
62
- try {
63
- return (0, config_manager_1.loadConfig)();
64
- }
65
- catch {
66
- return { profiles: {} };
67
- }
68
- }
69
- /**
70
- * Helper: Write config atomically
71
- */
72
- function writeConfig(config) {
73
- const configPath = (0, config_manager_1.getConfigPath)();
74
- const tempPath = configPath + '.tmp';
75
- fs.writeFileSync(tempPath, JSON.stringify(config, null, 2) + '\n');
76
- fs.renameSync(tempPath, configPath);
77
- }
78
- /**
79
- * Helper: Check if profile is configured (has valid settings file)
80
- */
81
- function isConfigured(profileName, config) {
82
- const settingsPath = config.profiles[profileName];
83
- if (!settingsPath)
84
- return false;
85
- try {
86
- const expandedPath = (0, helpers_1.expandPath)(settingsPath);
87
- if (!fs.existsSync(expandedPath))
88
- return false;
89
- const settings = (0, config_manager_1.loadSettings)(expandedPath);
90
- return !!(settings.env?.ANTHROPIC_BASE_URL && settings.env?.ANTHROPIC_AUTH_TOKEN);
91
- }
92
- catch {
93
- return false;
94
- }
95
- }
96
- /**
97
- * Helper: Create settings file for profile
98
- */
99
- function createSettingsFile(name, baseUrl, apiKey, models = {}) {
100
- const settingsPath = path.join((0, config_manager_1.getCcsDir)(), `${name}.settings.json`);
101
- const { model, opusModel, sonnetModel, haikuModel } = models;
102
- const settings = {
103
- env: {
104
- ANTHROPIC_BASE_URL: baseUrl,
105
- ANTHROPIC_AUTH_TOKEN: apiKey,
106
- ...(model && { ANTHROPIC_MODEL: model }),
107
- ...(opusModel && { ANTHROPIC_DEFAULT_OPUS_MODEL: opusModel }),
108
- ...(sonnetModel && { ANTHROPIC_DEFAULT_SONNET_MODEL: sonnetModel }),
109
- ...(haikuModel && { ANTHROPIC_DEFAULT_HAIKU_MODEL: haikuModel }),
110
- },
111
- };
112
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
113
- return `~/.ccs/${name}.settings.json`;
114
- }
115
- /**
116
- * Helper: Update settings file
117
- */
118
- function updateSettingsFile(name, updates) {
119
- const settingsPath = path.join((0, config_manager_1.getCcsDir)(), `${name}.settings.json`);
120
- if (!fs.existsSync(settingsPath)) {
121
- throw new Error('Settings file not found');
122
- }
123
- const settings = (0, config_manager_1.loadSettings)(settingsPath);
124
- if (updates.baseUrl) {
125
- settings.env = settings.env || {};
126
- settings.env.ANTHROPIC_BASE_URL = updates.baseUrl;
127
- }
128
- if (updates.apiKey) {
129
- settings.env = settings.env || {};
130
- settings.env.ANTHROPIC_AUTH_TOKEN = updates.apiKey;
131
- }
132
- if (updates.model !== undefined) {
133
- settings.env = settings.env || {};
134
- if (updates.model) {
135
- settings.env.ANTHROPIC_MODEL = updates.model;
136
- }
137
- else {
138
- delete settings.env.ANTHROPIC_MODEL;
139
- }
140
- }
141
- // Handle model mapping fields
142
- if (updates.opusModel !== undefined) {
143
- settings.env = settings.env || {};
144
- if (updates.opusModel) {
145
- settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL = updates.opusModel;
146
- }
147
- else {
148
- delete settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL;
149
- }
150
- }
151
- if (updates.sonnetModel !== undefined) {
152
- settings.env = settings.env || {};
153
- if (updates.sonnetModel) {
154
- settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = updates.sonnetModel;
155
- }
156
- else {
157
- delete settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL;
158
- }
159
- }
160
- if (updates.haikuModel !== undefined) {
161
- settings.env = settings.env || {};
162
- if (updates.haikuModel) {
163
- settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = updates.haikuModel;
164
- }
165
- else {
166
- delete settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
167
- }
168
- }
169
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
170
- }
171
- /**
172
- * Helper: Create cliproxy variant settings
173
- * Includes base URL and auth token for proper Claude CLI integration
174
- */
175
- function createCliproxySettings(name, provider, model) {
176
- const settingsPath = path.join((0, config_manager_1.getCcsDir)(), `${name}.settings.json`);
177
- // Get base env vars from provider config (includes BASE_URL, AUTH_TOKEN)
178
- const baseEnv = (0, config_generator_2.getClaudeEnvVars)(provider);
179
- const settings = {
180
- env: {
181
- ANTHROPIC_BASE_URL: baseEnv.ANTHROPIC_BASE_URL || '',
182
- ANTHROPIC_AUTH_TOKEN: baseEnv.ANTHROPIC_AUTH_TOKEN || '',
183
- ANTHROPIC_MODEL: model || baseEnv.ANTHROPIC_MODEL || '',
184
- ANTHROPIC_DEFAULT_OPUS_MODEL: model || baseEnv.ANTHROPIC_DEFAULT_OPUS_MODEL || '',
185
- ANTHROPIC_DEFAULT_SONNET_MODEL: model || baseEnv.ANTHROPIC_DEFAULT_SONNET_MODEL || '',
186
- ANTHROPIC_DEFAULT_HAIKU_MODEL: baseEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL || model || '',
187
- },
188
- };
189
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
190
- return `~/.ccs/${name}.settings.json`;
191
- }
192
- // ==================== Profile CRUD ====================
193
- /**
194
- * GET /api/profiles - List all profiles
195
- */
196
- exports.apiRoutes.get('/profiles', (_req, res) => {
197
- const config = readConfigSafe();
198
- const profiles = Object.entries(config.profiles).map(([name, settingsPath]) => ({
199
- name,
200
- settingsPath,
201
- configured: isConfigured(name, config),
202
- }));
203
- res.json({ profiles });
204
- });
205
- /**
206
- * POST /api/profiles - Create new profile
207
- */
208
- exports.apiRoutes.post('/profiles', (req, res) => {
209
- const { name, baseUrl, apiKey, model, opusModel, sonnetModel, haikuModel } = req.body;
210
- if (!name || !baseUrl || !apiKey) {
211
- res.status(400).json({ error: 'Missing required fields: name, baseUrl, apiKey' });
212
- return;
213
- }
214
- // Validate reserved names
215
- if ((0, reserved_names_1.isReservedName)(name)) {
216
- res.status(400).json({
217
- error: `Profile name '${name}' is reserved`,
218
- reserved: reserved_names_1.RESERVED_PROFILE_NAMES,
219
- });
220
- return;
221
- }
222
- const config = readConfigSafe();
223
- if (config.profiles[name]) {
224
- res.status(409).json({ error: 'Profile already exists' });
225
- return;
226
- }
227
- // Ensure .ccs directory exists
228
- if (!fs.existsSync((0, config_manager_1.getCcsDir)())) {
229
- fs.mkdirSync((0, config_manager_1.getCcsDir)(), { recursive: true });
230
- }
231
- // Create settings file with model mapping
232
- const settingsPath = createSettingsFile(name, baseUrl, apiKey, {
233
- model,
234
- opusModel,
235
- sonnetModel,
236
- haikuModel,
237
- });
238
- // Update config
239
- config.profiles[name] = settingsPath;
240
- writeConfig(config);
241
- res.status(201).json({ name, settingsPath });
242
- });
243
- /**
244
- * PUT /api/profiles/:name - Update profile
245
- */
246
- exports.apiRoutes.put('/profiles/:name', (req, res) => {
247
- const { name } = req.params;
248
- const { baseUrl, apiKey, model, opusModel, sonnetModel, haikuModel } = req.body;
249
- const config = readConfigSafe();
250
- if (!config.profiles[name]) {
251
- res.status(404).json({ error: 'Profile not found' });
252
- return;
253
- }
254
- try {
255
- updateSettingsFile(name, { baseUrl, apiKey, model, opusModel, sonnetModel, haikuModel });
256
- res.json({ name, updated: true });
257
- }
258
- catch (error) {
259
- res.status(500).json({ error: error.message });
260
- }
261
- });
262
- /**
263
- * DELETE /api/profiles/:name - Delete profile
264
- */
265
- exports.apiRoutes.delete('/profiles/:name', (req, res) => {
266
- const { name } = req.params;
267
- const config = readConfigSafe();
268
- if (!config.profiles[name]) {
269
- res.status(404).json({ error: 'Profile not found' });
270
- return;
271
- }
272
- // Delete settings file
273
- const settingsPath = path.join((0, config_manager_1.getCcsDir)(), `${name}.settings.json`);
274
- if (fs.existsSync(settingsPath)) {
275
- fs.unlinkSync(settingsPath);
276
- }
277
- // Remove from config
278
- delete config.profiles[name];
279
- writeConfig(config);
280
- res.json({ name, deleted: true });
281
- });
282
- // ==================== CLIProxy CRUD ====================
283
- /**
284
- * GET /api/cliproxy - List cliproxy variants
285
- */
286
- exports.apiRoutes.get('/cliproxy', (_req, res) => {
287
- const config = readConfigSafe();
288
- const variants = Object.entries(config.cliproxy || {}).map(([name, variant]) => ({
289
- name,
290
- provider: variant.provider,
291
- settings: variant.settings,
292
- account: variant.account || 'default', // Include account field
293
- }));
294
- res.json({ variants });
295
- });
296
- /**
297
- * POST /api/cliproxy - Create cliproxy variant
298
- */
299
- exports.apiRoutes.post('/cliproxy', (req, res) => {
300
- const { name, provider, model, account } = req.body;
301
- if (!name || !provider) {
302
- res.status(400).json({ error: 'Missing required fields: name, provider' });
303
- return;
304
- }
305
- // Reject reserved names as variant names (prevents collision with built-in providers)
306
- if ((0, reserved_names_1.isReservedName)(name)) {
307
- res.status(400).json({
308
- error: `Cannot use reserved name '${name}' as variant name`,
309
- reserved: reserved_names_1.RESERVED_PROFILE_NAMES,
310
- });
311
- return;
312
- }
313
- const config = readConfigSafe();
314
- config.cliproxy = config.cliproxy || {};
315
- if (config.cliproxy[name]) {
316
- res.status(409).json({ error: 'Variant already exists' });
317
- return;
318
- }
319
- // Ensure .ccs directory exists
320
- if (!fs.existsSync((0, config_manager_1.getCcsDir)())) {
321
- fs.mkdirSync((0, config_manager_1.getCcsDir)(), { recursive: true });
322
- }
323
- // Create settings file for variant
324
- const settingsPath = createCliproxySettings(name, provider, model);
325
- // Include account if specified (defaults to 'default' if not provided)
326
- config.cliproxy[name] = {
327
- provider,
328
- settings: settingsPath,
329
- ...(account && { account }),
330
- };
331
- writeConfig(config);
332
- res.status(201).json({ name, provider, settings: settingsPath, account: account || 'default' });
333
- });
334
- /**
335
- * PUT /api/cliproxy/:name - Update cliproxy variant
336
- */
337
- exports.apiRoutes.put('/cliproxy/:name', (req, res) => {
338
- const { name } = req.params;
339
- const { provider, account, model } = req.body;
340
- const config = readConfigSafe();
341
- if (!config.cliproxy?.[name]) {
342
- res.status(404).json({ error: 'Variant not found' });
343
- return;
344
- }
345
- const variant = config.cliproxy[name];
346
- // Update fields if provided
347
- if (provider) {
348
- variant.provider = provider;
349
- }
350
- if (account !== undefined) {
351
- if (account) {
352
- variant.account = account;
353
- }
354
- else {
355
- delete variant.account; // Remove account to use default
356
- }
357
- }
358
- // Update model in settings file if provided
359
- if (model !== undefined) {
360
- const settingsPath = path.join((0, config_manager_1.getCcsDir)(), `${name}.settings.json`);
361
- if (fs.existsSync(settingsPath)) {
362
- const settings = (0, config_manager_1.loadSettings)(settingsPath);
363
- if (model) {
364
- settings.env = settings.env || {};
365
- settings.env.ANTHROPIC_MODEL = model;
366
- }
367
- else if (settings.env) {
368
- delete settings.env.ANTHROPIC_MODEL;
369
- }
370
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
371
- }
372
- }
373
- writeConfig(config);
374
- res.json({
375
- name,
376
- provider: variant.provider,
377
- account: variant.account || 'default',
378
- settings: variant.settings,
379
- updated: true,
380
- });
381
- });
382
- /**
383
- * DELETE /api/cliproxy/:name - Delete cliproxy variant
384
- */
385
- exports.apiRoutes.delete('/cliproxy/:name', (req, res) => {
386
- const { name } = req.params;
387
- const config = readConfigSafe();
388
- if (!config.cliproxy?.[name]) {
389
- res.status(404).json({ error: 'Variant not found' });
390
- return;
391
- }
392
- // Never delete settings files for reserved provider names (safety guard)
393
- if (!(0, reserved_names_1.isReservedName)(name)) {
394
- // Only delete settings file for non-reserved variant names
395
- const settingsPath = path.join((0, config_manager_1.getCcsDir)(), `${name}.settings.json`);
396
- if (fs.existsSync(settingsPath)) {
397
- fs.unlinkSync(settingsPath);
398
- }
399
- }
400
- delete config.cliproxy[name];
401
- writeConfig(config);
402
- res.json({ name, deleted: true });
403
- });
404
- /**
405
- * GET /api/cliproxy/auth - Get auth status for built-in CLIProxy profiles
406
- * Also fetches CLIProxyAPI stats to update lastUsedAt for active providers
407
- */
408
- exports.apiRoutes.get('/cliproxy/auth', async (_req, res) => {
409
- // Initialize accounts from existing tokens on first request
410
- (0, auth_handler_1.initializeAccounts)();
411
- // Fetch CLIProxyAPI usage stats to determine active providers
412
- const stats = await (0, stats_fetcher_1.fetchCliproxyStats)();
413
- // Map CLIProxyAPI provider names to our internal provider names
414
- const statsProviderMap = {
415
- gemini: 'gemini',
416
- antigravity: 'agy',
417
- codex: 'codex',
418
- qwen: 'qwen',
419
- iflow: 'iflow',
420
- };
421
- // Update lastUsedAt for providers with recent activity
422
- if (stats?.requestsByProvider) {
423
- for (const [statsProvider, requestCount] of Object.entries(stats.requestsByProvider)) {
424
- if (requestCount > 0) {
425
- const provider = statsProviderMap[statsProvider.toLowerCase()];
426
- if (provider) {
427
- // Touch the default account for this provider (or all accounts)
428
- const accounts = (0, account_manager_1.getProviderAccounts)(provider);
429
- for (const account of accounts) {
430
- // Only touch if this is the default account (most likely being used)
431
- if (account.isDefault) {
432
- (0, account_manager_1.touchAccount)(provider, account.id);
433
- }
434
- }
435
- }
436
- }
437
- }
438
- }
439
- const statuses = (0, auth_handler_1.getAllAuthStatus)();
440
- const authStatus = statuses.map((status) => {
441
- const oauthConfig = (0, auth_handler_1.getOAuthConfig)(status.provider);
442
- return {
443
- provider: status.provider,
444
- displayName: oauthConfig.displayName,
445
- authenticated: status.authenticated,
446
- lastAuth: status.lastAuth?.toISOString() || null,
447
- tokenFiles: status.tokenFiles.length,
448
- accounts: status.accounts,
449
- defaultAccount: status.defaultAccount,
450
- };
451
- });
452
- res.json({ authStatus });
453
- });
454
- // ==================== Account Management (Multi-Account Support) ====================
455
- /**
456
- * GET /api/cliproxy/accounts - Get all accounts across all providers
457
- */
458
- exports.apiRoutes.get('/cliproxy/accounts', (_req, res) => {
459
- // Initialize accounts from existing tokens
460
- (0, auth_handler_1.initializeAccounts)();
461
- const accounts = (0, account_manager_1.getAllAccountsSummary)();
462
- res.json({ accounts });
463
- });
464
- /**
465
- * GET /api/cliproxy/accounts/:provider - Get accounts for a specific provider
466
- */
467
- exports.apiRoutes.get('/cliproxy/accounts/:provider', (req, res) => {
468
- const { provider } = req.params;
469
- // Validate provider
470
- const validProviders = ['gemini', 'codex', 'agy', 'qwen', 'iflow'];
471
- if (!validProviders.includes(provider)) {
472
- res.status(400).json({ error: `Invalid provider: ${provider}` });
473
- return;
474
- }
475
- const accounts = (0, account_manager_1.getProviderAccounts)(provider);
476
- res.json({ provider, accounts });
477
- });
478
- /**
479
- * POST /api/cliproxy/accounts/:provider/default - Set default account for provider
480
- */
481
- exports.apiRoutes.post('/cliproxy/accounts/:provider/default', (req, res) => {
482
- const { provider } = req.params;
483
- const { accountId } = req.body;
484
- if (!accountId) {
485
- res.status(400).json({ error: 'Missing required field: accountId' });
486
- return;
487
- }
488
- // Validate provider
489
- const validProviders = ['gemini', 'codex', 'agy', 'qwen', 'iflow'];
490
- if (!validProviders.includes(provider)) {
491
- res.status(400).json({ error: `Invalid provider: ${provider}` });
492
- return;
493
- }
494
- const success = (0, account_manager_1.setDefaultAccount)(provider, accountId);
495
- if (success) {
496
- res.json({ provider, defaultAccount: accountId });
497
- }
498
- else {
499
- res.status(404).json({ error: 'Account not found' });
500
- }
501
- });
502
- /**
503
- * DELETE /api/cliproxy/accounts/:provider/:accountId - Remove an account
504
- */
505
- exports.apiRoutes.delete('/cliproxy/accounts/:provider/:accountId', (req, res) => {
506
- const { provider, accountId } = req.params;
507
- // Validate provider
508
- const validProviders = ['gemini', 'codex', 'agy', 'qwen', 'iflow'];
509
- if (!validProviders.includes(provider)) {
510
- res.status(400).json({ error: `Invalid provider: ${provider}` });
511
- return;
512
- }
513
- const success = (0, account_manager_1.removeAccount)(provider, accountId);
514
- if (success) {
515
- res.json({ provider, accountId, deleted: true });
516
- }
517
- else {
518
- res.status(404).json({ error: 'Account not found' });
519
- }
520
- });
521
- /**
522
- * POST /api/cliproxy/auth/:provider/start - Start OAuth flow for a provider
523
- * Opens browser for authentication and returns account info when complete
524
- */
525
- exports.apiRoutes.post('/cliproxy/auth/:provider/start', async (req, res) => {
526
- const { provider } = req.params;
527
- const { nickname } = req.body;
528
- // Validate provider
529
- const validProviders = ['gemini', 'codex', 'agy', 'qwen', 'iflow'];
530
- if (!validProviders.includes(provider)) {
531
- res.status(400).json({ error: `Invalid provider: ${provider}` });
532
- return;
533
- }
534
- try {
535
- // Trigger OAuth flow - this opens browser and waits for completion
536
- const account = await (0, auth_handler_1.triggerOAuth)(provider, {
537
- add: true, // Always add mode from UI
538
- headless: false, // Force interactive mode
539
- nickname: nickname || undefined,
540
- fromUI: true, // Enable project selection prompt in UI
541
- });
542
- if (account) {
543
- res.json({
544
- success: true,
545
- account: {
546
- id: account.id,
547
- email: account.email,
548
- nickname: account.nickname,
549
- provider: account.provider,
550
- isDefault: account.isDefault,
551
- },
552
- });
553
- }
554
- else {
555
- res.status(400).json({ error: 'Authentication failed or was cancelled' });
556
- }
557
- }
558
- catch (error) {
559
- res.status(500).json({ error: error.message });
560
- }
561
- });
562
- /**
563
- * GET /api/cliproxy/auth/project-selection/:sessionId - Get pending project selection prompt
564
- * Returns project list for user to select from during OAuth flow
565
- */
566
- exports.apiRoutes.get('/cliproxy/auth/project-selection/:sessionId', (req, res) => {
567
- const { sessionId } = req.params;
568
- const pending = (0, project_selection_handler_1.getPendingSelection)(sessionId);
569
- if (pending) {
570
- res.json(pending);
571
- }
572
- else {
573
- res.status(404).json({ error: 'No pending project selection for this session' });
574
- }
575
- });
576
- /**
577
- * POST /api/cliproxy/auth/project-selection/:sessionId - Submit project selection
578
- * Submits user's project choice during OAuth flow
579
- */
580
- exports.apiRoutes.post('/cliproxy/auth/project-selection/:sessionId', (req, res) => {
581
- const { sessionId } = req.params;
582
- const { selectedId } = req.body;
583
- if (!selectedId && selectedId !== '') {
584
- res.status(400).json({ error: 'selectedId is required (use empty string for default)' });
585
- return;
586
- }
587
- const success = (0, project_selection_handler_1.submitProjectSelection)({ sessionId, selectedId });
588
- if (success) {
589
- res.json({ success: true });
590
- }
591
- else {
592
- res.status(404).json({ error: 'No pending project selection for this session' });
593
- }
594
- });
595
- // ==================== Settings (Phase 05) ====================
596
- /**
597
- * Helper: Mask API keys in settings
598
- */
599
- function maskApiKeys(settings) {
600
- if (!settings.env)
601
- return settings;
602
- const masked = { ...settings, env: { ...settings.env } };
603
- for (const key of Object.keys(masked.env)) {
604
- if ((0, sensitive_keys_1.isSensitiveKey)(key)) {
605
- masked.env[key] = (0, sensitive_keys_1.maskSensitiveValue)(masked.env[key]);
606
- }
607
- }
608
- return masked;
609
- }
610
- /**
611
- * GET /api/settings/:profile - Get settings with masked API keys
612
- */
613
- exports.apiRoutes.get('/settings/:profile', (req, res) => {
614
- const { profile } = req.params;
615
- const ccsDir = (0, config_manager_1.getCcsDir)();
616
- const settingsPath = path.join(ccsDir, `${profile}.settings.json`);
617
- if (!fs.existsSync(settingsPath)) {
618
- res.status(404).json({ error: 'Settings not found' });
619
- return;
620
- }
621
- const stat = fs.statSync(settingsPath);
622
- const settings = (0, config_manager_1.loadSettings)(settingsPath);
623
- // Mask API keys in response
624
- const masked = maskApiKeys(settings);
625
- res.json({
626
- profile,
627
- settings: masked,
628
- mtime: stat.mtime.getTime(),
629
- path: settingsPath,
630
- });
631
- });
632
- /**
633
- * GET /api/settings/:profile/raw - Get full settings (for editing)
634
- */
635
- exports.apiRoutes.get('/settings/:profile/raw', (req, res) => {
636
- const { profile } = req.params;
637
- const ccsDir = (0, config_manager_1.getCcsDir)();
638
- const settingsPath = path.join(ccsDir, `${profile}.settings.json`);
639
- if (!fs.existsSync(settingsPath)) {
640
- res.status(404).json({ error: 'Settings not found' });
641
- return;
642
- }
643
- const stat = fs.statSync(settingsPath);
644
- const settings = (0, config_manager_1.loadSettings)(settingsPath);
645
- res.json({
646
- profile,
647
- settings,
648
- mtime: stat.mtime.getTime(),
649
- path: settingsPath,
650
- });
651
- });
652
- /**
653
- * PUT /api/settings/:profile - Update settings with conflict detection and backup
654
- * Creates the settings file if it doesn't exist (upsert behavior)
655
- */
656
- exports.apiRoutes.put('/settings/:profile', (req, res) => {
657
- const { profile } = req.params;
658
- const { settings, expectedMtime } = req.body;
659
- const ccsDir = (0, config_manager_1.getCcsDir)();
660
- const settingsPath = path.join(ccsDir, `${profile}.settings.json`);
661
- const fileExists = fs.existsSync(settingsPath);
662
- // Only check conflict if file exists and expectedMtime was provided
663
- if (fileExists && expectedMtime) {
664
- const stat = fs.statSync(settingsPath);
665
- if (stat.mtime.getTime() !== expectedMtime) {
666
- res.status(409).json({
667
- error: 'File modified externally',
668
- currentMtime: stat.mtime.getTime(),
669
- });
670
- return;
671
- }
672
- }
673
- // Create backup only if file exists
674
- let backupPath;
675
- if (fileExists) {
676
- const backupDir = path.join(ccsDir, 'backups');
677
- if (!fs.existsSync(backupDir)) {
678
- fs.mkdirSync(backupDir, { recursive: true });
679
- }
680
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
681
- backupPath = path.join(backupDir, `${profile}.${timestamp}.settings.json`);
682
- fs.copyFileSync(settingsPath, backupPath);
683
- }
684
- // Ensure directory exists for new files
685
- if (!fileExists) {
686
- fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
687
- }
688
- // Write new settings atomically
689
- const tempPath = settingsPath + '.tmp';
690
- fs.writeFileSync(tempPath, JSON.stringify(settings, null, 2) + '\n');
691
- fs.renameSync(tempPath, settingsPath);
692
- const newStat = fs.statSync(settingsPath);
693
- res.json({
694
- profile,
695
- mtime: newStat.mtime.getTime(),
696
- backupPath,
697
- created: !fileExists,
698
- });
699
- });
700
- // ==================== Presets ====================
701
- /**
702
- * GET /api/settings/:profile/presets - Get saved presets for a provider
703
- */
704
- exports.apiRoutes.get('/settings/:profile/presets', (req, res) => {
705
- const { profile } = req.params;
706
- const ccsDir = (0, config_manager_1.getCcsDir)();
707
- const settingsPath = path.join(ccsDir, `${profile}.settings.json`);
708
- if (!fs.existsSync(settingsPath)) {
709
- res.json({ presets: [] });
710
- return;
711
- }
712
- const settings = (0, config_manager_1.loadSettings)(settingsPath);
713
- res.json({ presets: settings.presets || [] });
714
- });
715
- /**
716
- * POST /api/settings/:profile/presets - Create a new preset
717
- */
718
- exports.apiRoutes.post('/settings/:profile/presets', (req, res) => {
719
- const { profile } = req.params;
720
- const { name, default: defaultModel, opus, sonnet, haiku } = req.body;
721
- if (!name || !defaultModel) {
722
- res.status(400).json({ error: 'Missing required fields: name, default' });
723
- return;
724
- }
725
- const ccsDir = (0, config_manager_1.getCcsDir)();
726
- const settingsPath = path.join(ccsDir, `${profile}.settings.json`);
727
- // Create settings file if it doesn't exist
728
- if (!fs.existsSync(settingsPath)) {
729
- fs.writeFileSync(settingsPath, JSON.stringify({ env: {}, presets: [] }, null, 2) + '\n');
730
- }
731
- const settings = (0, config_manager_1.loadSettings)(settingsPath);
732
- settings.presets = settings.presets || [];
733
- // Check for duplicate name
734
- if (settings.presets.some((p) => p.name === name)) {
735
- res.status(409).json({ error: 'Preset with this name already exists' });
736
- return;
737
- }
738
- const preset = {
739
- name,
740
- default: defaultModel,
741
- opus: opus || defaultModel,
742
- sonnet: sonnet || defaultModel,
743
- haiku: haiku || defaultModel,
744
- };
745
- settings.presets.push(preset);
746
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
747
- res.status(201).json({ preset });
748
- });
749
- /**
750
- * DELETE /api/settings/:profile/presets/:name - Delete a preset
751
- */
752
- exports.apiRoutes.delete('/settings/:profile/presets/:name', (req, res) => {
753
- const { profile, name } = req.params;
754
- const ccsDir = (0, config_manager_1.getCcsDir)();
755
- const settingsPath = path.join(ccsDir, `${profile}.settings.json`);
756
- if (!fs.existsSync(settingsPath)) {
757
- res.status(404).json({ error: 'Settings not found' });
758
- return;
759
- }
760
- const settings = (0, config_manager_1.loadSettings)(settingsPath);
761
- if (!settings.presets || !settings.presets.some((p) => p.name === name)) {
762
- res.status(404).json({ error: 'Preset not found' });
763
- return;
764
- }
765
- settings.presets = settings.presets.filter((p) => p.name !== name);
766
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
767
- res.json({ success: true });
768
- });
769
- // ==================== Accounts ====================
770
- /**
771
- * GET /api/accounts - List accounts from profiles.json
772
- */
773
- exports.apiRoutes.get('/accounts', (_req, res) => {
774
- const profilesPath = path.join((0, config_manager_1.getCcsDir)(), 'profiles.json');
775
- if (!fs.existsSync(profilesPath)) {
776
- res.json({ accounts: [], default: null });
777
- return;
778
- }
779
- const data = JSON.parse(fs.readFileSync(profilesPath, 'utf8'));
780
- const accounts = Object.entries(data.profiles || {}).map(([name, meta]) => {
781
- // Type-safe handling of metadata
782
- const metadata = meta;
783
- return {
784
- name,
785
- ...metadata,
786
- };
787
- });
788
- res.json({ accounts, default: data.default || null });
789
- });
790
- /**
791
- * POST /api/accounts/default - Set default account
792
- */
793
- exports.apiRoutes.post('/accounts/default', (req, res) => {
794
- const { name } = req.body;
795
- if (!name) {
796
- res.status(400).json({ error: 'Missing required field: name' });
797
- return;
798
- }
799
- const profilesPath = path.join((0, config_manager_1.getCcsDir)(), 'profiles.json');
800
- const data = fs.existsSync(profilesPath)
801
- ? JSON.parse(fs.readFileSync(profilesPath, 'utf8'))
802
- : { profiles: {} };
803
- data.default = name;
804
- fs.writeFileSync(profilesPath, JSON.stringify(data, null, 2) + '\n');
805
- res.json({ default: name });
806
- });
807
- // ==================== Health (Phase 06) ====================
808
- /**
809
- * GET /api/health - Run health checks
810
- */
811
- exports.apiRoutes.get('/health', async (_req, res) => {
812
- const report = await (0, health_service_1.runHealthChecks)();
813
- res.json(report);
814
- });
815
- /**
816
- * POST /api/health/fix/:checkId - Fix a health issue
817
- */
818
- exports.apiRoutes.post('/health/fix/:checkId', (req, res) => {
819
- const { checkId } = req.params;
820
- const result = (0, health_service_1.fixHealthIssue)(checkId);
821
- if (result.success) {
822
- res.json({ success: true, message: result.message });
823
- }
824
- else {
825
- res.status(400).json({ success: false, message: result.message });
826
- }
827
- });
828
- // ==================== Unified Config (Phase 5) ====================
829
- /**
830
- * GET /api/config/format - Return current config format and migration status
831
- */
832
- exports.apiRoutes.get('/config/format', (_req, res) => {
833
- res.json({
834
- format: (0, unified_config_loader_1.getConfigFormat)(),
835
- migrationNeeded: (0, migration_manager_1.needsMigration)(),
836
- backups: (0, migration_manager_1.getBackupDirectories)(),
837
- });
838
- });
839
- /**
840
- * GET /api/config - Return unified config (excludes secrets)
841
- */
842
- exports.apiRoutes.get('/config', (_req, res) => {
843
- if (!(0, unified_config_loader_1.hasUnifiedConfig)()) {
844
- res.status(400).json({ error: 'Unified config not enabled' });
845
- return;
846
- }
847
- const config = (0, unified_config_loader_1.loadUnifiedConfig)();
848
- if (!config) {
849
- res.status(500).json({ error: 'Failed to load config' });
850
- return;
851
- }
852
- res.json(config);
853
- });
854
- /**
855
- * GET /api/config/raw - Return raw YAML content for display
856
- */
857
- exports.apiRoutes.get('/config/raw', (_req, res) => {
858
- const yamlPath = (0, unified_config_loader_1.getConfigYamlPath)();
859
- if (!fs.existsSync(yamlPath)) {
860
- res.status(404).json({ error: 'Config file not found' });
861
- return;
862
- }
863
- try {
864
- const content = fs.readFileSync(yamlPath, 'utf8');
865
- res.type('text/plain').send(content);
866
- }
867
- catch (err) {
868
- res.status(500).json({ error: err.message });
869
- }
870
- });
871
- /**
872
- * PUT /api/config - Update unified config
873
- */
874
- exports.apiRoutes.put('/config', (req, res) => {
875
- const config = req.body;
876
- if (!(0, unified_config_types_1.isUnifiedConfig)(config)) {
877
- res.status(400).json({ error: 'Invalid config format' });
878
- return;
879
- }
880
- try {
881
- (0, unified_config_loader_1.saveUnifiedConfig)(config);
882
- res.json({ success: true });
883
- }
884
- catch (err) {
885
- res.status(500).json({ error: err.message });
886
- }
887
- });
888
- /**
889
- * POST /api/config/migrate - Trigger migration from JSON to YAML
890
- */
891
- exports.apiRoutes.post('/config/migrate', async (req, res) => {
892
- const dryRun = req.query.dryRun === 'true';
893
- const result = await (0, migration_manager_1.migrate)(dryRun);
894
- res.json(result);
895
- });
896
- /**
897
- * POST /api/config/rollback - Rollback migration to JSON format
898
- */
899
- exports.apiRoutes.post('/config/rollback', async (req, res) => {
900
- const { backupPath } = req.body;
901
- if (!backupPath || typeof backupPath !== 'string') {
902
- res.status(400).json({ error: 'Missing required field: backupPath' });
903
- return;
904
- }
905
- const success = await (0, migration_manager_1.rollback)(backupPath);
906
- res.json({ success });
907
- });
908
- /**
909
- * PUT /api/secrets/:profile - Update profile secrets (write-only)
910
- */
911
- exports.apiRoutes.put('/secrets/:profile', (req, res) => {
912
- const { profile } = req.params;
913
- const secrets = req.body;
914
- if (!secrets || typeof secrets !== 'object') {
915
- res.status(400).json({ error: 'Invalid secrets format' });
916
- return;
917
- }
918
- try {
919
- (0, secrets_manager_1.setProfileSecrets)(profile, secrets);
920
- res.json({ success: true });
921
- }
922
- catch (err) {
923
- res.status(500).json({ error: err.message });
924
- }
925
- });
926
- /**
927
- * GET /api/secrets/:profile/exists - Check if secrets exist (no values returned)
928
- */
929
- exports.apiRoutes.get('/secrets/:profile/exists', (req, res) => {
930
- const { profile } = req.params;
931
- const secrets = (0, secrets_manager_1.getProfileSecrets)(profile);
932
- res.json({
933
- exists: Object.keys(secrets).length > 0,
934
- keys: Object.keys(secrets), // Only key names, not values
935
- });
936
- });
937
- // ==================== Generic File API (Issue #73) ====================
938
- /**
939
- * Security: Validate file path is within allowed directories
940
- * - ~/.ccs/ directory: read/write allowed
941
- * - ~/.claude/settings.json: read-only
942
- */
943
- function validateFilePath(filePath) {
944
- const expandedPath = (0, helpers_1.expandPath)(filePath);
945
- const normalizedPath = path.normalize(expandedPath);
946
- const ccsDir = (0, config_manager_1.getCcsDir)();
947
- const claudeSettingsPath = (0, helpers_1.expandPath)('~/.claude/settings.json');
948
- // Check if path is within ~/.ccs/
949
- if (normalizedPath.startsWith(ccsDir)) {
950
- // Block access to sensitive subdirectories
951
- const relativePath = normalizedPath.slice(ccsDir.length);
952
- if (relativePath.includes('/.git/') || relativePath.includes('/node_modules/')) {
953
- return { valid: false, readonly: false, error: 'Access to this path is not allowed' };
954
- }
955
- return { valid: true, readonly: false };
956
- }
957
- // Allow read-only access to ~/.claude/settings.json
958
- if (normalizedPath === claudeSettingsPath) {
959
- return { valid: true, readonly: true };
960
- }
961
- return { valid: false, readonly: false, error: 'Access to this path is not allowed' };
962
- }
963
- /**
964
- * GET /api/file - Read a file with path validation
965
- * Query params: path (required)
966
- * Returns: { content: string, mtime: number, readonly: boolean, path: string }
967
- */
968
- exports.apiRoutes.get('/file', (req, res) => {
969
- const filePath = req.query.path;
970
- if (!filePath) {
971
- res.status(400).json({ error: 'Missing required query parameter: path' });
972
- return;
973
- }
974
- const validation = validateFilePath(filePath);
975
- if (!validation.valid) {
976
- res.status(403).json({ error: validation.error });
977
- return;
978
- }
979
- const expandedPath = (0, helpers_1.expandPath)(filePath);
980
- if (!fs.existsSync(expandedPath)) {
981
- res.status(404).json({ error: 'File not found' });
982
- return;
983
- }
984
- try {
985
- const stat = fs.statSync(expandedPath);
986
- const content = fs.readFileSync(expandedPath, 'utf8');
987
- res.json({
988
- content,
989
- mtime: stat.mtime.getTime(),
990
- readonly: validation.readonly,
991
- path: expandedPath,
992
- });
993
- }
994
- catch (error) {
995
- res.status(500).json({ error: error.message });
996
- }
997
- });
998
- /**
999
- * PUT /api/file - Write a file with conflict detection and backup
1000
- * Query params: path (required)
1001
- * Body: { content: string, expectedMtime?: number }
1002
- * Returns: { success: true, mtime: number, backupPath?: string }
1003
- */
1004
- exports.apiRoutes.put('/file', (req, res) => {
1005
- const filePath = req.query.path;
1006
- const { content, expectedMtime } = req.body;
1007
- if (!filePath) {
1008
- res.status(400).json({ error: 'Missing required query parameter: path' });
1009
- return;
1010
- }
1011
- if (typeof content !== 'string') {
1012
- res.status(400).json({ error: 'Missing required field: content' });
1013
- return;
1014
- }
1015
- const validation = validateFilePath(filePath);
1016
- if (!validation.valid) {
1017
- res.status(403).json({ error: validation.error });
1018
- return;
1019
- }
1020
- if (validation.readonly) {
1021
- res.status(403).json({ error: 'File is read-only' });
1022
- return;
1023
- }
1024
- const expandedPath = (0, helpers_1.expandPath)(filePath);
1025
- const ccsDir = (0, config_manager_1.getCcsDir)();
1026
- // Conflict detection (if file exists and expectedMtime provided)
1027
- if (fs.existsSync(expandedPath) && expectedMtime !== undefined) {
1028
- const stat = fs.statSync(expandedPath);
1029
- if (stat.mtime.getTime() !== expectedMtime) {
1030
- res.status(409).json({
1031
- error: 'File modified externally',
1032
- currentMtime: stat.mtime.getTime(),
1033
- });
1034
- return;
1035
- }
1036
- }
1037
- try {
1038
- // Create backup if file exists
1039
- let backupPath;
1040
- if (fs.existsSync(expandedPath)) {
1041
- const backupDir = path.join(ccsDir, 'backups');
1042
- if (!fs.existsSync(backupDir)) {
1043
- fs.mkdirSync(backupDir, { recursive: true });
1044
- }
1045
- const filename = path.basename(expandedPath);
1046
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
1047
- backupPath = path.join(backupDir, `${filename}.${timestamp}.bak`);
1048
- fs.copyFileSync(expandedPath, backupPath);
1049
- }
1050
- // Ensure parent directory exists
1051
- const parentDir = path.dirname(expandedPath);
1052
- if (!fs.existsSync(parentDir)) {
1053
- fs.mkdirSync(parentDir, { recursive: true });
1054
- }
1055
- // Write atomically
1056
- const tempPath = expandedPath + '.tmp';
1057
- fs.writeFileSync(tempPath, content);
1058
- fs.renameSync(tempPath, expandedPath);
1059
- const newStat = fs.statSync(expandedPath);
1060
- res.json({
1061
- success: true,
1062
- mtime: newStat.mtime.getTime(),
1063
- backupPath,
1064
- });
1065
- }
1066
- catch (error) {
1067
- res.status(500).json({ error: error.message });
1068
- }
1069
- });
1070
- /**
1071
- * GET /api/files - List editable files in ~/.ccs/
1072
- * Returns: { files: Array<{ name: string, path: string, mtime: number }> }
1073
- */
1074
- exports.apiRoutes.get('/files', (_req, res) => {
1075
- const ccsDir = (0, config_manager_1.getCcsDir)();
1076
- if (!fs.existsSync(ccsDir)) {
1077
- res.json({ files: [] });
1078
- return;
1079
- }
1080
- try {
1081
- const entries = fs.readdirSync(ccsDir, { withFileTypes: true });
1082
- const files = entries
1083
- .filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
1084
- .map((entry) => {
1085
- const filePath = path.join(ccsDir, entry.name);
1086
- const stat = fs.statSync(filePath);
1087
- return {
1088
- name: entry.name,
1089
- path: `~/.ccs/${entry.name}`,
1090
- mtime: stat.mtime.getTime(),
1091
- };
1092
- })
1093
- .sort((a, b) => a.name.localeCompare(b.name));
1094
- res.json({ files });
1095
- }
1096
- catch (error) {
1097
- res.status(500).json({ error: error.message });
1098
- }
1099
- });
1100
- /**
1101
- * GET /api/cliproxy/stats - Get CLIProxyAPI usage statistics
1102
- * Returns: CliproxyStats or error if proxy not running
1103
- */
1104
- exports.apiRoutes.get('/cliproxy/stats', async (_req, res) => {
1105
- try {
1106
- // Check if proxy is running first
1107
- const running = await (0, stats_fetcher_1.isCliproxyRunning)();
1108
- if (!running) {
1109
- res.status(503).json({
1110
- error: 'CLIProxyAPI not running',
1111
- message: 'Start a CLIProxy session (gemini, codex, agy) to collect stats',
1112
- });
1113
- return;
1114
- }
1115
- // Fetch stats from management API
1116
- const stats = await (0, stats_fetcher_1.fetchCliproxyStats)();
1117
- if (!stats) {
1118
- res.status(503).json({
1119
- error: 'Stats unavailable',
1120
- message: 'CLIProxyAPI is running but stats endpoint not responding',
1121
- });
1122
- return;
1123
- }
1124
- res.json(stats);
1125
- }
1126
- catch (error) {
1127
- res.status(500).json({ error: error.message });
1128
- }
1129
- });
1130
- /**
1131
- * GET /api/cliproxy/status - Check CLIProxyAPI running status
1132
- * Returns: { running: boolean }
1133
- */
1134
- exports.apiRoutes.get('/cliproxy/status', async (_req, res) => {
1135
- try {
1136
- const running = await (0, stats_fetcher_1.isCliproxyRunning)();
1137
- res.json({ running });
1138
- }
1139
- catch (error) {
1140
- res.status(500).json({ error: error.message });
1141
- }
1142
- });
1143
- /**
1144
- * GET /api/cliproxy/proxy-status - Get detailed proxy process status
1145
- * Returns: { running, port?, pid?, sessionCount?, startedAt? }
1146
- * Combines session tracker data with actual port check for accuracy
1147
- */
1148
- exports.apiRoutes.get('/cliproxy/proxy-status', async (_req, res) => {
1149
- try {
1150
- // First check session tracker for detailed info
1151
- const sessionStatus = (0, session_tracker_1.getProxyStatus)();
1152
- // If session tracker says running, trust it
1153
- if (sessionStatus.running) {
1154
- res.json(sessionStatus);
1155
- return;
1156
- }
1157
- // Session tracker says not running, but proxy might be running without session tracking
1158
- // (e.g., started before session persistence was implemented)
1159
- const actuallyRunning = await (0, stats_fetcher_1.isCliproxyRunning)();
1160
- if (actuallyRunning) {
1161
- // Proxy running but no session lock - legacy/untracked instance
1162
- res.json({
1163
- running: true,
1164
- port: 8317, // Default port
1165
- sessionCount: 0, // Unknown sessions
1166
- // No pid/startedAt since we don't have session lock
1167
- });
1168
- }
1169
- else {
1170
- res.json(sessionStatus);
1171
- }
1172
- }
1173
- catch (error) {
1174
- res.status(500).json({ error: error.message });
1175
- }
1176
- });
1177
- /**
1178
- * POST /api/cliproxy/proxy-start - Start the CLIProxy service
1179
- * Returns: { started, alreadyRunning, port, error? }
1180
- * Starts proxy in background if not already running
1181
- */
1182
- exports.apiRoutes.post('/cliproxy/proxy-start', async (_req, res) => {
1183
- try {
1184
- const result = await (0, service_manager_1.ensureCliproxyService)();
1185
- res.json(result);
1186
- }
1187
- catch (error) {
1188
- res.status(500).json({ error: error.message });
1189
- }
1190
- });
1191
- /**
1192
- * POST /api/cliproxy/proxy-stop - Stop the CLIProxy service
1193
- * Returns: { stopped, pid?, sessionCount?, error? }
1194
- */
1195
- exports.apiRoutes.post('/cliproxy/proxy-stop', async (_req, res) => {
1196
- try {
1197
- const result = await (0, session_tracker_1.stopProxy)();
1198
- res.json(result);
1199
- }
1200
- catch (error) {
1201
- res.status(500).json({ error: error.message });
1202
- }
1203
- });
1204
- /**
1205
- * GET /api/cliproxy/update-check - Check for CLIProxyAPI binary updates
1206
- * Returns: { hasUpdate, currentVersion, latestVersion, fromCache }
1207
- */
1208
- exports.apiRoutes.get('/cliproxy/update-check', async (_req, res) => {
1209
- try {
1210
- const result = await (0, binary_manager_1.checkCliproxyUpdate)();
1211
- res.json(result);
1212
- }
1213
- catch (error) {
1214
- res.status(500).json({ error: error.message });
1215
- }
1216
- });
1217
- /**
1218
- * GET /api/cliproxy/models - Get available models from CLIProxyAPI
1219
- * Returns: { models: CliproxyModel[], byCategory: Record<string, CliproxyModel[]>, totalCount: number }
1220
- */
1221
- exports.apiRoutes.get('/cliproxy/models', async (_req, res) => {
1222
- try {
1223
- // Check if proxy is running first
1224
- const running = await (0, stats_fetcher_1.isCliproxyRunning)();
1225
- if (!running) {
1226
- res.status(503).json({
1227
- error: 'CLIProxyAPI not running',
1228
- message: 'Start a CLIProxy session (gemini, codex, agy) to fetch available models',
1229
- });
1230
- return;
1231
- }
1232
- // Fetch models from /v1/models endpoint
1233
- const modelsResponse = await (0, stats_fetcher_1.fetchCliproxyModels)();
1234
- if (!modelsResponse) {
1235
- res.status(503).json({
1236
- error: 'Models unavailable',
1237
- message: 'CLIProxyAPI is running but /v1/models endpoint not responding',
1238
- });
1239
- return;
1240
- }
1241
- res.json(modelsResponse);
1242
- }
1243
- catch (error) {
1244
- res.status(500).json({ error: error.message });
1245
- }
1246
- });
1247
- // ==================== Error Logs ====================
1248
- /**
1249
- * GET /api/cliproxy/error-logs - Get list of error log files
1250
- * Returns: { files: CliproxyErrorLog[] } or error if proxy not running
1251
- */
1252
- exports.apiRoutes.get('/cliproxy/error-logs', async (_req, res) => {
1253
- try {
1254
- const running = await (0, stats_fetcher_1.isCliproxyRunning)();
1255
- if (!running) {
1256
- res.status(503).json({
1257
- error: 'CLIProxyAPI not running',
1258
- message: 'Start a CLIProxy session to view error logs',
1259
- });
1260
- return;
1261
- }
1262
- const files = await (0, stats_fetcher_1.fetchCliproxyErrorLogs)();
1263
- if (files === null) {
1264
- res.status(503).json({
1265
- error: 'Error logs unavailable',
1266
- message: 'CLIProxyAPI is running but error logs endpoint not responding',
1267
- });
1268
- return;
1269
- }
1270
- // Inject absolute paths into each file entry
1271
- const logsDir = path.join((0, config_generator_1.getCliproxyWritablePath)(), 'logs');
1272
- const filesWithPaths = files.map((file) => ({
1273
- ...file,
1274
- absolutePath: path.join(logsDir, file.name),
1275
- }));
1276
- res.json({ files: filesWithPaths });
1277
- }
1278
- catch (error) {
1279
- res.status(500).json({ error: error.message });
1280
- }
1281
- });
1282
- /**
1283
- * GET /api/cliproxy/error-logs/:name - Get content of a specific error log
1284
- * Returns: plain text log content
1285
- */
1286
- exports.apiRoutes.get('/cliproxy/error-logs/:name', async (req, res) => {
1287
- const { name } = req.params;
1288
- // Validate filename format and prevent path traversal
1289
- if (!name ||
1290
- !name.startsWith('error-') ||
1291
- !name.endsWith('.log') ||
1292
- name.includes('..') ||
1293
- name.includes('/') ||
1294
- name.includes('\\')) {
1295
- res.status(400).json({ error: 'Invalid error log filename' });
1296
- return;
1297
- }
1298
- try {
1299
- const running = await (0, stats_fetcher_1.isCliproxyRunning)();
1300
- if (!running) {
1301
- res.status(503).json({ error: 'CLIProxyAPI not running' });
1302
- return;
1303
- }
1304
- const content = await (0, stats_fetcher_1.fetchCliproxyErrorLogContent)(name);
1305
- if (content === null) {
1306
- res.status(404).json({ error: 'Error log not found' });
1307
- return;
1308
- }
1309
- res.type('text/plain').send(content);
1310
- }
1311
- catch (error) {
1312
- res.status(500).json({ error: error.message });
1313
- }
1314
- });
1315
- // ============================================
1316
- // OpenAI Compatibility Layer Routes
1317
- // ============================================
1318
- /**
1319
- * GET /api/cliproxy/openai-compat - List all OpenAI-compatible providers
1320
- */
1321
- exports.apiRoutes.get('/cliproxy/openai-compat', (_req, res) => {
1322
- try {
1323
- const providers = (0, openai_compat_manager_1.listOpenAICompatProviders)();
1324
- // Mask API keys for security
1325
- const masked = providers.map((p) => ({
1326
- ...p,
1327
- apiKey: p.apiKey ? `...${p.apiKey.slice(-4)}` : '',
1328
- }));
1329
- res.json({ providers: masked });
1330
- }
1331
- catch (error) {
1332
- res.status(500).json({ error: error.message });
1333
- }
1334
- });
1335
- /**
1336
- * GET /api/cliproxy/openai-compat/templates - Get pre-configured provider templates
1337
- */
1338
- exports.apiRoutes.get('/cliproxy/openai-compat/templates', (_req, res) => {
1339
- res.json({
1340
- templates: [
1341
- { ...openai_compat_manager_1.OPENROUTER_TEMPLATE, description: 'OpenRouter - Access multiple AI models' },
1342
- { ...openai_compat_manager_1.TOGETHER_TEMPLATE, description: 'Together AI - Open source models' },
1343
- ],
1344
- });
1345
- });
1346
- /**
1347
- * GET /api/cliproxy/openai-compat/:name - Get a specific provider
1348
- */
1349
- exports.apiRoutes.get('/cliproxy/openai-compat/:name', (req, res) => {
1350
- try {
1351
- const provider = (0, openai_compat_manager_1.getOpenAICompatProvider)(req.params.name);
1352
- if (!provider) {
1353
- res.status(404).json({ error: `Provider '${req.params.name}' not found` });
1354
- return;
1355
- }
1356
- // Mask API key
1357
- res.json({
1358
- ...provider,
1359
- apiKey: provider.apiKey ? `...${provider.apiKey.slice(-4)}` : '',
1360
- });
1361
- }
1362
- catch (error) {
1363
- res.status(500).json({ error: error.message });
1364
- }
1365
- });
1366
- /**
1367
- * POST /api/cliproxy/openai-compat - Add a new provider
1368
- * Body: { name, baseUrl, apiKey, models: [{ name, alias }] }
1369
- */
1370
- exports.apiRoutes.post('/cliproxy/openai-compat', (req, res) => {
1371
- try {
1372
- const { name, baseUrl, apiKey, models } = req.body;
1373
- // Validation
1374
- if (!name || typeof name !== 'string') {
1375
- res.status(400).json({ error: 'name is required' });
1376
- return;
1377
- }
1378
- // Validate reserved names
1379
- if ((0, reserved_names_1.isReservedName)(name)) {
1380
- res.status(400).json({
1381
- error: `Provider name '${name}' is reserved`,
1382
- reserved: reserved_names_1.RESERVED_PROFILE_NAMES,
1383
- });
1384
- return;
1385
- }
1386
- if (!baseUrl || typeof baseUrl !== 'string') {
1387
- res.status(400).json({ error: 'baseUrl is required' });
1388
- return;
1389
- }
1390
- if (!apiKey || typeof apiKey !== 'string') {
1391
- res.status(400).json({ error: 'apiKey is required' });
1392
- return;
1393
- }
1394
- (0, openai_compat_manager_1.addOpenAICompatProvider)({
1395
- name,
1396
- baseUrl,
1397
- apiKey,
1398
- models: models || [],
1399
- });
1400
- res.status(201).json({ success: true, name });
1401
- }
1402
- catch (error) {
1403
- const message = error.message;
1404
- if (message.includes('already exists')) {
1405
- res.status(409).json({ error: message });
1406
- }
1407
- else {
1408
- res.status(500).json({ error: message });
1409
- }
1410
- }
1411
- });
1412
- /**
1413
- * PUT /api/cliproxy/openai-compat/:name - Update a provider
1414
- * Body: { baseUrl?, apiKey?, models?, name? (for rename) }
1415
- */
1416
- exports.apiRoutes.put('/cliproxy/openai-compat/:name', (req, res) => {
1417
- try {
1418
- const { baseUrl, apiKey, models, name: newName } = req.body;
1419
- (0, openai_compat_manager_1.updateOpenAICompatProvider)(req.params.name, {
1420
- baseUrl,
1421
- apiKey,
1422
- models,
1423
- name: newName,
1424
- });
1425
- res.json({ success: true });
1426
- }
1427
- catch (error) {
1428
- const message = error.message;
1429
- if (message.includes('not found')) {
1430
- res.status(404).json({ error: message });
1431
- }
1432
- else {
1433
- res.status(500).json({ error: message });
1434
- }
1435
- }
1436
- });
1437
- /**
1438
- * DELETE /api/cliproxy/openai-compat/:name - Remove a provider
1439
- */
1440
- exports.apiRoutes.delete('/cliproxy/openai-compat/:name', (req, res) => {
1441
- try {
1442
- const removed = (0, openai_compat_manager_1.removeOpenAICompatProvider)(req.params.name);
1443
- if (!removed) {
1444
- res.status(404).json({ error: `Provider '${req.params.name}' not found` });
1445
- return;
1446
- }
1447
- res.json({ success: true });
1448
- }
1449
- catch (error) {
1450
- res.status(500).json({ error: error.message });
1451
- }
1452
- });
1453
- // ==================== WebSearch Configuration ====================
1454
- /**
1455
- * GET /api/websearch - Get WebSearch configuration
1456
- * Returns: WebSearchConfig with enabled, provider, fallback
1457
- */
1458
- exports.apiRoutes.get('/websearch', (_req, res) => {
1459
- try {
1460
- const config = (0, unified_config_loader_2.getWebSearchConfig)();
1461
- res.json(config);
1462
- }
1463
- catch (error) {
1464
- res.status(500).json({ error: error.message });
1465
- }
1466
- });
1467
- /**
1468
- * PUT /api/websearch - Update WebSearch configuration
1469
- * Body: WebSearchConfig fields (enabled, providers)
1470
- * Dashboard is the source of truth for provider selection.
1471
- */
1472
- exports.apiRoutes.put('/websearch', (req, res) => {
1473
- const { enabled, providers } = req.body;
1474
- // Validate enabled
1475
- if (enabled !== undefined && typeof enabled !== 'boolean') {
1476
- res.status(400).json({ error: 'Invalid value for enabled. Must be a boolean.' });
1477
- return;
1478
- }
1479
- // Validate providers if specified
1480
- if (providers !== undefined && typeof providers !== 'object') {
1481
- res.status(400).json({ error: 'Invalid value for providers. Must be an object.' });
1482
- return;
1483
- }
1484
- try {
1485
- // Load existing config and update websearch section
1486
- const existingConfig = (0, unified_config_loader_1.loadUnifiedConfig)();
1487
- if (!existingConfig) {
1488
- res.status(500).json({ error: 'Failed to load config' });
1489
- return;
1490
- }
1491
- // Merge updates - supports Gemini CLI and Grok CLI
1492
- existingConfig.websearch = {
1493
- enabled: enabled ?? existingConfig.websearch?.enabled ?? true,
1494
- providers: providers
1495
- ? {
1496
- gemini: {
1497
- enabled: providers.gemini?.enabled ??
1498
- existingConfig.websearch?.providers?.gemini?.enabled ??
1499
- true,
1500
- model: providers.gemini?.model ??
1501
- existingConfig.websearch?.providers?.gemini?.model ??
1502
- 'gemini-2.5-flash',
1503
- timeout: providers.gemini?.timeout ??
1504
- existingConfig.websearch?.providers?.gemini?.timeout ??
1505
- 55,
1506
- },
1507
- grok: {
1508
- enabled: providers.grok?.enabled ??
1509
- existingConfig.websearch?.providers?.grok?.enabled ??
1510
- false,
1511
- timeout: providers.grok?.timeout ?? existingConfig.websearch?.providers?.grok?.timeout ?? 55,
1512
- },
1513
- opencode: {
1514
- enabled: providers.opencode?.enabled ??
1515
- existingConfig.websearch?.providers?.opencode?.enabled ??
1516
- false,
1517
- model: providers.opencode?.model ??
1518
- existingConfig.websearch?.providers?.opencode?.model ??
1519
- 'opencode/grok-code',
1520
- timeout: providers.opencode?.timeout ??
1521
- existingConfig.websearch?.providers?.opencode?.timeout ??
1522
- 60,
1523
- },
1524
- }
1525
- : existingConfig.websearch?.providers,
1526
- };
1527
- (0, unified_config_loader_1.saveUnifiedConfig)(existingConfig);
1528
- res.json({
1529
- success: true,
1530
- websearch: existingConfig.websearch,
1531
- });
1532
- }
1533
- catch (error) {
1534
- res.status(500).json({ error: error.message });
1535
- }
1536
- });
1537
- /**
1538
- * GET /api/websearch/status - Get WebSearch status
1539
- * Returns: { geminiCli, grokCli, opencodeCli, readiness }
1540
- */
1541
- exports.apiRoutes.get('/websearch/status', (_req, res) => {
1542
- try {
1543
- const geminiCli = (0, websearch_manager_1.getGeminiCliStatus)();
1544
- const grokCli = (0, websearch_manager_1.getGrokCliStatus)();
1545
- const opencodeCli = (0, websearch_manager_1.getOpenCodeCliStatus)();
1546
- const readiness = (0, websearch_manager_1.getWebSearchReadiness)();
1547
- res.json({
1548
- geminiCli: {
1549
- installed: geminiCli.installed,
1550
- path: geminiCli.path,
1551
- version: geminiCli.version,
1552
- },
1553
- grokCli: {
1554
- installed: grokCli.installed,
1555
- path: grokCli.path,
1556
- version: grokCli.version,
1557
- },
1558
- opencodeCli: {
1559
- installed: opencodeCli.installed,
1560
- path: opencodeCli.path,
1561
- version: opencodeCli.version,
1562
- },
1563
- readiness: {
1564
- status: readiness.readiness,
1565
- message: readiness.message,
1566
- },
1567
- });
1568
- }
1569
- catch (error) {
1570
- res.status(500).json({ error: error.message });
1571
- }
1572
- });
1573
- // ============================================================================
1574
- // COPILOT API ROUTES
1575
- // GitHub Copilot integration via copilot-api proxy
1576
- // ============================================================================
1577
- const copilot_1 = require("../copilot");
1578
- const unified_config_types_2 = require("../config/unified-config-types");
1579
- const unified_config_loader_3 = require("../config/unified-config-loader");
1580
- /**
1581
- * GET /api/copilot/status - Get Copilot status (auth + daemon + install info)
1582
- */
1583
- exports.apiRoutes.get('/copilot/status', async (_req, res) => {
1584
- try {
1585
- const config = (0, unified_config_loader_3.loadOrCreateUnifiedConfig)();
1586
- const copilotConfig = config.copilot ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG;
1587
- const status = await (0, copilot_1.getCopilotStatus)(copilotConfig);
1588
- const installed = (0, copilot_1.isCopilotApiInstalled)();
1589
- const version = (0, copilot_1.getInstalledVersion)();
1590
- res.json({
1591
- enabled: copilotConfig.enabled,
1592
- installed,
1593
- version,
1594
- authenticated: status.auth.authenticated,
1595
- daemon_running: status.daemon.running,
1596
- port: copilotConfig.port,
1597
- model: copilotConfig.model,
1598
- account_type: copilotConfig.account_type,
1599
- auto_start: copilotConfig.auto_start,
1600
- rate_limit: copilotConfig.rate_limit,
1601
- wait_on_limit: copilotConfig.wait_on_limit,
1602
- });
1603
- }
1604
- catch (error) {
1605
- res.status(500).json({ error: error.message });
1606
- }
1607
- });
1608
- /**
1609
- * GET /api/copilot/config - Get Copilot configuration
1610
- */
1611
- exports.apiRoutes.get('/copilot/config', (_req, res) => {
1612
- try {
1613
- const config = (0, unified_config_loader_3.loadOrCreateUnifiedConfig)();
1614
- const copilotConfig = config.copilot ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG;
1615
- res.json(copilotConfig);
1616
- }
1617
- catch (error) {
1618
- res.status(500).json({ error: error.message });
1619
- }
1620
- });
1621
- /**
1622
- * PUT /api/copilot/config - Update Copilot configuration
1623
- */
1624
- exports.apiRoutes.put('/copilot/config', (req, res) => {
1625
- try {
1626
- const updates = req.body;
1627
- const config = (0, unified_config_loader_3.loadOrCreateUnifiedConfig)();
1628
- // Merge updates with existing config
1629
- config.copilot = {
1630
- enabled: updates.enabled ?? config.copilot?.enabled ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG.enabled,
1631
- auto_start: updates.auto_start ?? config.copilot?.auto_start ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG.auto_start,
1632
- port: updates.port ?? config.copilot?.port ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG.port,
1633
- account_type: updates.account_type ?? config.copilot?.account_type ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG.account_type,
1634
- rate_limit: updates.rate_limit !== undefined
1635
- ? updates.rate_limit
1636
- : (config.copilot?.rate_limit ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG.rate_limit),
1637
- wait_on_limit: updates.wait_on_limit ??
1638
- config.copilot?.wait_on_limit ??
1639
- unified_config_types_2.DEFAULT_COPILOT_CONFIG.wait_on_limit,
1640
- model: updates.model ?? config.copilot?.model ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG.model,
1641
- // Model mapping for opus/sonnet/haiku tiers
1642
- opus_model: updates.opus_model !== undefined ? updates.opus_model : config.copilot?.opus_model,
1643
- sonnet_model: updates.sonnet_model !== undefined ? updates.sonnet_model : config.copilot?.sonnet_model,
1644
- haiku_model: updates.haiku_model !== undefined ? updates.haiku_model : config.copilot?.haiku_model,
1645
- };
1646
- (0, unified_config_loader_1.saveUnifiedConfig)(config);
1647
- res.json({ success: true, copilot: config.copilot });
1648
- }
1649
- catch (error) {
1650
- res.status(500).json({ error: error.message });
1651
- }
1652
- });
1653
- /**
1654
- * POST /api/copilot/auth/start - Start GitHub OAuth flow
1655
- * Note: This is a long-running operation that opens browser
1656
- */
1657
- exports.apiRoutes.post('/copilot/auth/start', async (_req, res) => {
1658
- try {
1659
- const result = await (0, copilot_1.startAuthFlow)();
1660
- res.json(result);
1661
- }
1662
- catch (error) {
1663
- res.status(500).json({ error: error.message });
1664
- }
1665
- });
1666
- /**
1667
- * GET /api/copilot/auth/status - Get auth status only
1668
- */
1669
- exports.apiRoutes.get('/copilot/auth/status', async (_req, res) => {
1670
- try {
1671
- const status = await (0, copilot_1.checkAuthStatus)();
1672
- res.json(status);
1673
- }
1674
- catch (error) {
1675
- res.status(500).json({ error: error.message });
1676
- }
1677
- });
1678
- /**
1679
- * GET /api/copilot/models - Get available models
1680
- */
1681
- exports.apiRoutes.get('/copilot/models', async (_req, res) => {
1682
- try {
1683
- const config = (0, unified_config_loader_3.loadOrCreateUnifiedConfig)();
1684
- const port = config.copilot?.port ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG.port;
1685
- const currentModel = config.copilot?.model ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG.model;
1686
- const models = await (0, copilot_1.getAvailableModels)(port);
1687
- // Mark current model
1688
- const modelsWithCurrent = models.map((m) => ({
1689
- ...m,
1690
- isCurrent: m.id === currentModel,
1691
- }));
1692
- res.json({ models: modelsWithCurrent, current: currentModel });
1693
- }
1694
- catch (error) {
1695
- res.status(500).json({ error: error.message });
1696
- }
1697
- });
1698
- /**
1699
- * POST /api/copilot/daemon/start - Start copilot-api daemon
1700
- */
1701
- exports.apiRoutes.post('/copilot/daemon/start', async (_req, res) => {
1702
- try {
1703
- const config = (0, unified_config_loader_3.loadOrCreateUnifiedConfig)();
1704
- const copilotConfig = config.copilot ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG;
1705
- const result = await (0, copilot_1.startDaemon)(copilotConfig);
1706
- res.json(result);
1707
- }
1708
- catch (error) {
1709
- res.status(500).json({ error: error.message });
1710
- }
1711
- });
1712
- /**
1713
- * POST /api/copilot/daemon/stop - Stop copilot-api daemon
1714
- */
1715
- exports.apiRoutes.post('/copilot/daemon/stop', async (_req, res) => {
1716
- try {
1717
- const result = await (0, copilot_1.stopDaemon)();
1718
- res.json(result);
1719
- }
1720
- catch (error) {
1721
- res.status(500).json({ error: error.message });
1722
- }
1723
- });
1724
- /**
1725
- * POST /api/copilot/install - Install copilot-api
1726
- * Auto-installs latest version or specific version if provided
1727
- */
1728
- exports.apiRoutes.post('/copilot/install', async (req, res) => {
1729
- try {
1730
- const { version } = req.body || {};
1731
- if (version) {
1732
- // Install specific version
1733
- await (0, copilot_1.installCopilotApiVersion)(version);
1734
- }
1735
- else {
1736
- // Install latest version
1737
- await (0, copilot_1.ensureCopilotApi)();
1738
- }
1739
- const info = (0, copilot_1.getCopilotApiInfo)();
1740
- res.json({
1741
- success: true,
1742
- installed: info.installed,
1743
- version: info.version,
1744
- path: info.path,
1745
- });
1746
- }
1747
- catch (error) {
1748
- res.status(500).json({ error: error.message });
1749
- }
1750
- });
1751
- /**
1752
- * GET /api/copilot/info - Get copilot-api installation info
1753
- */
1754
- exports.apiRoutes.get('/copilot/info', (_req, res) => {
1755
- try {
1756
- const info = (0, copilot_1.getCopilotApiInfo)();
1757
- res.json(info);
1758
- }
1759
- catch (error) {
1760
- res.status(500).json({ error: error.message });
1761
- }
1762
- });
1763
- /**
1764
- * GET /api/copilot/settings/raw - Get raw copilot.settings.json
1765
- * Returns the raw JSON content for editing in the code editor
1766
- */
1767
- exports.apiRoutes.get('/copilot/settings/raw', (_req, res) => {
1768
- try {
1769
- const settingsPath = path.join((0, config_manager_1.getCcsDir)(), 'copilot.settings.json');
1770
- const config = (0, unified_config_loader_3.loadOrCreateUnifiedConfig)();
1771
- const copilotConfig = config.copilot ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG;
1772
- // Default model for all tiers
1773
- const defaultModel = copilotConfig.model;
1774
- // If file doesn't exist, return default structure with all model mappings
1775
- if (!fs.existsSync(settingsPath)) {
1776
- // Create settings structure matching CLIProxy pattern - always include all model mappings
1777
- // Use 127.0.0.1 instead of localhost for more reliable local connections
1778
- const defaultSettings = {
1779
- env: {
1780
- ANTHROPIC_BASE_URL: `http://127.0.0.1:${copilotConfig.port}`,
1781
- ANTHROPIC_AUTH_TOKEN: 'copilot-managed',
1782
- ANTHROPIC_MODEL: defaultModel,
1783
- ANTHROPIC_DEFAULT_OPUS_MODEL: copilotConfig.opus_model || defaultModel,
1784
- ANTHROPIC_DEFAULT_SONNET_MODEL: copilotConfig.sonnet_model || defaultModel,
1785
- ANTHROPIC_DEFAULT_HAIKU_MODEL: copilotConfig.haiku_model || defaultModel,
1786
- },
1787
- };
1788
- res.json({
1789
- settings: defaultSettings,
1790
- mtime: Date.now(),
1791
- path: `~/.ccs/copilot.settings.json`,
1792
- exists: false,
1793
- });
1794
- return;
1795
- }
1796
- const content = fs.readFileSync(settingsPath, 'utf-8');
1797
- const settings = JSON.parse(content);
1798
- const stat = fs.statSync(settingsPath);
1799
- res.json({
1800
- settings,
1801
- mtime: stat.mtimeMs,
1802
- path: `~/.ccs/copilot.settings.json`,
1803
- exists: true,
1804
- });
1805
- }
1806
- catch (error) {
1807
- res.status(500).json({ error: error.message });
1808
- }
1809
- });
1810
- /**
1811
- * PUT /api/copilot/settings/raw - Save raw copilot.settings.json
1812
- * Saves the raw JSON content from the code editor
1813
- */
1814
- exports.apiRoutes.put('/copilot/settings/raw', (req, res) => {
1815
- try {
1816
- const { settings, expectedMtime } = req.body;
1817
- const settingsPath = path.join((0, config_manager_1.getCcsDir)(), 'copilot.settings.json');
1818
- // Check for conflict if file exists and expectedMtime provided
1819
- if (fs.existsSync(settingsPath) && expectedMtime) {
1820
- const stat = fs.statSync(settingsPath);
1821
- if (Math.abs(stat.mtimeMs - expectedMtime) > 1000) {
1822
- res.status(409).json({ error: 'File modified externally', mtime: stat.mtimeMs });
1823
- return;
1824
- }
1825
- }
1826
- // Write settings file atomically
1827
- const tempPath = settingsPath + '.tmp';
1828
- fs.writeFileSync(tempPath, JSON.stringify(settings, null, 2) + '\n');
1829
- fs.renameSync(tempPath, settingsPath);
1830
- // Also sync model mappings back to unified config
1831
- const config = (0, unified_config_loader_3.loadOrCreateUnifiedConfig)();
1832
- const env = settings.env || {};
1833
- config.copilot = {
1834
- ...(config.copilot ?? unified_config_types_2.DEFAULT_COPILOT_CONFIG),
1835
- model: env.ANTHROPIC_MODEL || config.copilot?.model || unified_config_types_2.DEFAULT_COPILOT_CONFIG.model,
1836
- opus_model: env.ANTHROPIC_DEFAULT_OPUS_MODEL || undefined,
1837
- sonnet_model: env.ANTHROPIC_DEFAULT_SONNET_MODEL || undefined,
1838
- haiku_model: env.ANTHROPIC_DEFAULT_HAIKU_MODEL || undefined,
1839
- };
1840
- (0, unified_config_loader_1.saveUnifiedConfig)(config);
1841
- const stat = fs.statSync(settingsPath);
1842
- res.json({ success: true, mtime: stat.mtimeMs });
1843
- }
1844
- catch (error) {
1845
- res.status(500).json({ error: error.message });
1846
- }
1847
- });
1848
- // ==================== Global Environment Variables ====================
1849
- /**
1850
- * GET /api/global-env - Get global environment variables configuration
1851
- * Returns the global_env section from config.yaml
1852
- */
1853
- exports.apiRoutes.get('/global-env', (_req, res) => {
1854
- try {
1855
- const config = (0, unified_config_loader_3.getGlobalEnvConfig)();
1856
- res.json(config);
1857
- }
1858
- catch (error) {
1859
- res.status(500).json({ error: error.message });
1860
- }
1861
- });
1862
- /**
1863
- * PUT /api/global-env - Update global environment variables configuration
1864
- * Updates the global_env section in config.yaml
1865
- */
1866
- exports.apiRoutes.put('/global-env', (req, res) => {
1867
- try {
1868
- const { enabled, env } = req.body;
1869
- const config = (0, unified_config_loader_3.loadOrCreateUnifiedConfig)();
1870
- // Validate env is an object with string values
1871
- if (env !== undefined && typeof env === 'object' && env !== null) {
1872
- for (const [key, value] of Object.entries(env)) {
1873
- if (typeof value !== 'string') {
1874
- res.status(400).json({ error: `Invalid value for ${key}: must be a string` });
1875
- return;
1876
- }
1877
- }
1878
- }
1879
- // Update global_env section
1880
- config.global_env = {
1881
- enabled: enabled ?? config.global_env?.enabled ?? true,
1882
- env: env ?? config.global_env?.env ?? {},
1883
- };
1884
- (0, unified_config_loader_1.saveUnifiedConfig)(config);
1885
- res.json({ success: true, config: config.global_env });
1886
- }
1887
- catch (error) {
1888
- res.status(500).json({ error: error.message });
1889
- }
1890
- });
1891
- //# sourceMappingURL=routes.js.map