@kaitranntt/ccs 6.7.0 → 6.7.1-dev.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.
- package/README.md +45 -23
- package/dist/api/index.d.ts +7 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +24 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/services/index.d.ts +10 -0
- package/dist/api/services/index.d.ts.map +1 -0
- package/dist/api/services/index.js +26 -0
- package/dist/api/services/index.js.map +1 -0
- package/dist/api/services/profile-reader.d.ts +28 -0
- package/dist/api/services/profile-reader.d.ts.map +1 -0
- package/dist/api/services/profile-reader.js +148 -0
- package/dist/api/services/profile-reader.js.map +1 -0
- package/dist/api/services/profile-types.d.ts +42 -0
- package/dist/api/services/profile-types.d.ts.map +1 -0
- package/dist/api/services/profile-types.js +8 -0
- package/dist/api/services/profile-types.js.map +1 -0
- package/dist/api/services/profile-writer.d.ts +6 -0
- package/dist/api/services/profile-writer.d.ts.map +1 -0
- package/dist/api/services/profile-writer.js +176 -0
- package/dist/api/services/profile-writer.js.map +1 -0
- package/dist/api/services/validation-service.d.ts +26 -0
- package/dist/api/services/validation-service.d.ts.map +1 -0
- package/dist/api/services/validation-service.js +77 -0
- package/dist/api/services/validation-service.js.map +1 -0
- package/dist/auth/auth-commands.d.ts +16 -22
- package/dist/auth/auth-commands.d.ts.map +1 -1
- package/dist/auth/auth-commands.js +29 -467
- package/dist/auth/auth-commands.js.map +1 -1
- package/dist/auth/commands/create-command.d.ts +11 -0
- package/dist/auth/commands/create-command.d.ts.map +1 -0
- package/dist/auth/commands/create-command.js +137 -0
- package/dist/auth/commands/create-command.js.map +1 -0
- package/dist/auth/commands/default-command.d.ts +15 -0
- package/dist/auth/commands/default-command.d.ts.map +1 -0
- package/dist/auth/commands/default-command.js +71 -0
- package/dist/auth/commands/default-command.js.map +1 -0
- package/dist/auth/commands/index.d.ts +12 -0
- package/dist/auth/commands/index.d.ts.map +1 -0
- package/dist/auth/commands/index.js +25 -0
- package/dist/auth/commands/index.js.map +1 -0
- package/dist/auth/commands/list-command.d.ts +11 -0
- package/dist/auth/commands/list-command.d.ts.map +1 -0
- package/dist/auth/commands/list-command.js +124 -0
- package/dist/auth/commands/list-command.js.map +1 -0
- package/dist/auth/commands/remove-command.d.ts +11 -0
- package/dist/auth/commands/remove-command.d.ts.map +1 -0
- package/dist/auth/commands/remove-command.js +102 -0
- package/dist/auth/commands/remove-command.js.map +1 -0
- package/dist/auth/commands/show-command.d.ts +11 -0
- package/dist/auth/commands/show-command.d.ts.map +1 -0
- package/dist/auth/commands/show-command.js +103 -0
- package/dist/auth/commands/show-command.js.map +1 -0
- package/dist/auth/commands/types.d.ts +50 -0
- package/dist/auth/commands/types.d.ts.map +1 -0
- package/dist/auth/commands/types.js +26 -0
- package/dist/auth/commands/types.js.map +1 -0
- package/dist/auth/index.d.ts +8 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +16 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/profile-detector.d.ts +0 -5
- package/dist/auth/profile-detector.d.ts.map +1 -1
- package/dist/auth/profile-detector.js +1 -9
- package/dist/auth/profile-detector.js.map +1 -1
- package/dist/auth/profile-registry.d.ts +0 -4
- package/dist/auth/profile-registry.d.ts.map +1 -1
- package/dist/auth/profile-registry.js +3 -10
- package/dist/auth/profile-registry.js.map +1 -1
- package/dist/ccs.js +21 -4
- package/dist/ccs.js.map +1 -1
- package/dist/cliproxy/auth/auth-types.d.ts +85 -0
- package/dist/cliproxy/auth/auth-types.d.ts.map +1 -0
- package/dist/cliproxy/auth/auth-types.js +100 -0
- package/dist/cliproxy/auth/auth-types.js.map +1 -0
- package/dist/cliproxy/auth/environment-detector.d.ts +30 -0
- package/dist/cliproxy/auth/environment-detector.d.ts.map +1 -0
- package/dist/cliproxy/auth/environment-detector.js +124 -0
- package/dist/cliproxy/auth/environment-detector.js.map +1 -0
- package/dist/cliproxy/auth/index.d.ts +11 -0
- package/dist/cliproxy/auth/index.d.ts.map +1 -0
- package/dist/cliproxy/auth/index.js +35 -0
- package/dist/cliproxy/auth/index.js.map +1 -0
- package/dist/cliproxy/auth/oauth-handler.d.ts +29 -0
- package/dist/cliproxy/auth/oauth-handler.d.ts.map +1 -0
- package/dist/cliproxy/auth/oauth-handler.js +205 -0
- package/dist/cliproxy/auth/oauth-handler.js.map +1 -0
- package/dist/cliproxy/auth/oauth-process.d.ts +27 -0
- package/dist/cliproxy/auth/oauth-process.d.ts.map +1 -0
- package/dist/cliproxy/auth/oauth-process.js +209 -0
- package/dist/cliproxy/auth/oauth-process.js.map +1 -0
- package/dist/cliproxy/auth/token-manager.d.ts +53 -0
- package/dist/cliproxy/auth/token-manager.d.ts.map +1 -0
- package/dist/cliproxy/auth/token-manager.js +250 -0
- package/dist/cliproxy/auth/token-manager.js.map +1 -0
- package/dist/cliproxy/auth-handler.d.ts +13 -103
- package/dist/cliproxy/auth-handler.d.ts.map +1 -1
- package/dist/cliproxy/auth-handler.js +35 -766
- package/dist/cliproxy/auth-handler.js.map +1 -1
- package/dist/cliproxy/binary/downloader.d.ts +29 -0
- package/dist/cliproxy/binary/downloader.d.ts.map +1 -0
- package/dist/cliproxy/binary/downloader.js +218 -0
- package/dist/cliproxy/binary/downloader.js.map +1 -0
- package/dist/cliproxy/binary/extractor.d.ts +12 -0
- package/dist/cliproxy/binary/extractor.d.ts.map +1 -0
- package/dist/cliproxy/binary/extractor.js +27 -0
- package/dist/cliproxy/binary/extractor.js.map +1 -0
- package/dist/cliproxy/binary/index.d.ts +13 -0
- package/dist/cliproxy/binary/index.d.ts.map +1 -0
- package/dist/cliproxy/binary/index.js +53 -0
- package/dist/cliproxy/binary/index.js.map +1 -0
- package/dist/cliproxy/binary/installer.d.ts +24 -0
- package/dist/cliproxy/binary/installer.d.ts.map +1 -0
- package/dist/cliproxy/binary/installer.js +117 -0
- package/dist/cliproxy/binary/installer.js.map +1 -0
- package/dist/cliproxy/binary/lifecycle.d.ts +11 -0
- package/dist/cliproxy/binary/lifecycle.d.ts.map +1 -0
- package/dist/cliproxy/binary/lifecycle.js +106 -0
- package/dist/cliproxy/binary/lifecycle.js.map +1 -0
- package/dist/cliproxy/binary/tar-extractor.d.ts +9 -0
- package/dist/cliproxy/binary/tar-extractor.d.ts.map +1 -0
- package/dist/cliproxy/binary/tar-extractor.js +118 -0
- package/dist/cliproxy/binary/tar-extractor.js.map +1 -0
- package/dist/cliproxy/binary/types.d.ts +24 -0
- package/dist/cliproxy/binary/types.d.ts.map +1 -0
- package/dist/cliproxy/binary/types.js +14 -0
- package/dist/cliproxy/binary/types.js.map +1 -0
- package/dist/cliproxy/binary/updater.d.ts +7 -0
- package/dist/cliproxy/binary/updater.d.ts.map +1 -0
- package/dist/cliproxy/binary/updater.js +18 -0
- package/dist/cliproxy/binary/updater.js.map +1 -0
- package/dist/cliproxy/binary/verifier.d.ts +18 -0
- package/dist/cliproxy/binary/verifier.d.ts.map +1 -0
- package/dist/cliproxy/binary/verifier.js +82 -0
- package/dist/cliproxy/binary/verifier.js.map +1 -0
- package/dist/cliproxy/binary/version-cache.d.ts +46 -0
- package/dist/cliproxy/binary/version-cache.d.ts.map +1 -0
- package/dist/cliproxy/binary/version-cache.js +171 -0
- package/dist/cliproxy/binary/version-cache.js.map +1 -0
- package/dist/cliproxy/binary/version-checker.d.ts +19 -0
- package/dist/cliproxy/binary/version-checker.d.ts.map +1 -0
- package/dist/cliproxy/binary/version-checker.js +77 -0
- package/dist/cliproxy/binary/version-checker.js.map +1 -0
- package/dist/cliproxy/binary/zip-extractor.d.ts +9 -0
- package/dist/cliproxy/binary/zip-extractor.d.ts.map +1 -0
- package/dist/cliproxy/binary/zip-extractor.js +110 -0
- package/dist/cliproxy/binary/zip-extractor.js.map +1 -0
- package/dist/cliproxy/binary-manager.d.ts +16 -170
- package/dist/cliproxy/binary-manager.d.ts.map +1 -1
- package/dist/cliproxy/binary-manager.js +38 -865
- package/dist/cliproxy/binary-manager.js.map +1 -1
- package/dist/cliproxy/services/binary-service.d.ts +65 -0
- package/dist/cliproxy/services/binary-service.d.ts.map +1 -0
- package/dist/cliproxy/services/binary-service.js +140 -0
- package/dist/cliproxy/services/binary-service.js.map +1 -0
- package/dist/cliproxy/services/index.d.ts +8 -0
- package/dist/cliproxy/services/index.d.ts.map +1 -0
- package/dist/cliproxy/services/index.js +31 -0
- package/dist/cliproxy/services/index.js.map +1 -0
- package/dist/cliproxy/services/proxy-lifecycle-service.d.ts +38 -0
- package/dist/cliproxy/services/proxy-lifecycle-service.d.ts.map +1 -0
- package/dist/cliproxy/services/proxy-lifecycle-service.js +41 -0
- package/dist/cliproxy/services/proxy-lifecycle-service.js.map +1 -0
- package/dist/cliproxy/services/variant-config-adapter.d.ts +38 -0
- package/dist/cliproxy/services/variant-config-adapter.d.ts.map +1 -0
- package/dist/cliproxy/services/variant-config-adapter.js +172 -0
- package/dist/cliproxy/services/variant-config-adapter.js.map +1 -0
- package/dist/cliproxy/services/variant-service.d.ts +37 -0
- package/dist/cliproxy/services/variant-service.d.ts.map +1 -0
- package/dist/cliproxy/services/variant-service.js +128 -0
- package/dist/cliproxy/services/variant-service.js.map +1 -0
- package/dist/cliproxy/services/variant-settings.d.ts +32 -0
- package/dist/cliproxy/services/variant-settings.d.ts.map +1 -0
- package/dist/cliproxy/services/variant-settings.js +128 -0
- package/dist/cliproxy/services/variant-settings.js.map +1 -0
- package/dist/commands/api-command.d.ts +3 -6
- package/dist/commands/api-command.d.ts.map +1 -1
- package/dist/commands/api-command.js +84 -445
- package/dist/commands/api-command.js.map +1 -1
- package/dist/commands/cliproxy-command.d.ts +0 -3
- package/dist/commands/cliproxy-command.d.ts.map +1 -1
- package/dist/commands/cliproxy-command.js +182 -621
- package/dist/commands/cliproxy-command.js.map +1 -1
- package/dist/commands/index.d.ts +17 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +33 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/config/unified-config-loader.d.ts +7 -0
- package/dist/config/unified-config-loader.d.ts.map +1 -1
- package/dist/config/unified-config-loader.js +11 -1
- package/dist/config/unified-config-loader.js.map +1 -1
- package/dist/delegation/executor/index.d.ts +7 -0
- package/dist/delegation/executor/index.d.ts.map +1 -0
- package/dist/delegation/executor/index.js +28 -0
- package/dist/delegation/executor/index.js.map +1 -0
- package/dist/delegation/executor/result-aggregator.d.ts +30 -0
- package/dist/delegation/executor/result-aggregator.d.ts.map +1 -0
- package/dist/delegation/executor/result-aggregator.js +66 -0
- package/dist/delegation/executor/result-aggregator.js.map +1 -0
- package/dist/delegation/executor/stream-parser.d.ts +28 -0
- package/dist/delegation/executor/stream-parser.d.ts.map +1 -0
- package/dist/delegation/executor/stream-parser.js +140 -0
- package/dist/delegation/executor/stream-parser.js.map +1 -0
- package/dist/delegation/executor/types.d.ts +121 -0
- package/dist/delegation/executor/types.d.ts.map +1 -0
- package/dist/delegation/executor/types.js +6 -0
- package/dist/delegation/executor/types.js.map +1 -0
- package/dist/delegation/headless-executor.d.ts +13 -91
- package/dist/delegation/headless-executor.d.ts.map +1 -1
- package/dist/delegation/headless-executor.js +84 -291
- package/dist/delegation/headless-executor.js.map +1 -1
- package/dist/delegation/index.d.ts +11 -0
- package/dist/delegation/index.d.ts.map +1 -0
- package/dist/delegation/index.js +33 -0
- package/dist/delegation/index.js.map +1 -0
- package/dist/delegation/result-formatter.d.ts +1 -30
- package/dist/delegation/result-formatter.d.ts.map +1 -1
- package/dist/delegation/result-formatter.js.map +1 -1
- package/dist/errors/cleanup-registry.d.ts +64 -0
- package/dist/errors/cleanup-registry.d.ts.map +1 -0
- package/dist/errors/cleanup-registry.js +141 -0
- package/dist/errors/cleanup-registry.js.map +1 -0
- package/dist/errors/error-handler.d.ts +45 -0
- package/dist/errors/error-handler.d.ts.map +1 -0
- package/dist/errors/error-handler.js +150 -0
- package/dist/errors/error-handler.js.map +1 -0
- package/dist/errors/error-types.d.ts +102 -0
- package/dist/errors/error-types.d.ts.map +1 -0
- package/dist/errors/error-types.js +158 -0
- package/dist/errors/error-types.js.map +1 -0
- package/dist/errors/exit-codes.d.ts +49 -0
- package/dist/errors/exit-codes.d.ts.map +1 -0
- package/dist/errors/exit-codes.js +72 -0
- package/dist/errors/exit-codes.js.map +1 -0
- package/dist/errors/index.d.ts +29 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +62 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/glmt/glmt-transformer.d.ts +29 -248
- package/dist/glmt/glmt-transformer.d.ts.map +1 -1
- package/dist/glmt/glmt-transformer.js +63 -718
- package/dist/glmt/glmt-transformer.js.map +1 -1
- package/dist/glmt/index.d.ts +14 -0
- package/dist/glmt/index.d.ts.map +1 -0
- package/dist/glmt/index.js +41 -0
- package/dist/glmt/index.js.map +1 -0
- package/dist/glmt/pipeline/content-transformer.d.ts +39 -0
- package/dist/glmt/pipeline/content-transformer.d.ts.map +1 -0
- package/dist/glmt/pipeline/content-transformer.js +162 -0
- package/dist/glmt/pipeline/content-transformer.js.map +1 -0
- package/dist/glmt/pipeline/index.d.ts +12 -0
- package/dist/glmt/pipeline/index.d.ts.map +1 -0
- package/dist/glmt/pipeline/index.js +20 -0
- package/dist/glmt/pipeline/index.js.map +1 -0
- package/dist/glmt/pipeline/request-transformer.d.ts +31 -0
- package/dist/glmt/pipeline/request-transformer.d.ts.map +1 -0
- package/dist/glmt/pipeline/request-transformer.js +116 -0
- package/dist/glmt/pipeline/request-transformer.js.map +1 -0
- package/dist/glmt/pipeline/response-builder.d.ts +52 -0
- package/dist/glmt/pipeline/response-builder.d.ts.map +1 -0
- package/dist/glmt/pipeline/response-builder.js +205 -0
- package/dist/glmt/pipeline/response-builder.js.map +1 -0
- package/dist/glmt/pipeline/stream-parser.d.ts +59 -0
- package/dist/glmt/pipeline/stream-parser.d.ts.map +1 -0
- package/dist/glmt/pipeline/stream-parser.js +241 -0
- package/dist/glmt/pipeline/stream-parser.js.map +1 -0
- package/dist/glmt/pipeline/tool-call-handler.d.ts +22 -0
- package/dist/glmt/pipeline/tool-call-handler.d.ts.map +1 -0
- package/dist/glmt/pipeline/tool-call-handler.js +87 -0
- package/dist/glmt/pipeline/tool-call-handler.js.map +1 -0
- package/dist/glmt/pipeline/types.d.ts +159 -0
- package/dist/glmt/pipeline/types.d.ts.map +1 -0
- package/dist/glmt/pipeline/types.js +6 -0
- package/dist/glmt/pipeline/types.js.map +1 -0
- package/dist/management/checks/cliproxy-check.d.ts +37 -0
- package/dist/management/checks/cliproxy-check.d.ts.map +1 -0
- package/dist/management/checks/cliproxy-check.js +187 -0
- package/dist/management/checks/cliproxy-check.js.map +1 -0
- package/dist/management/checks/config-check.d.ts +27 -0
- package/dist/management/checks/config-check.d.ts.map +1 -0
- package/dist/management/checks/config-check.js +158 -0
- package/dist/management/checks/config-check.js.map +1 -0
- package/dist/management/checks/env-check.d.ts +17 -0
- package/dist/management/checks/env-check.d.ts.map +1 -0
- package/dist/management/checks/env-check.js +71 -0
- package/dist/management/checks/env-check.js.map +1 -0
- package/dist/management/checks/index.d.ts +12 -0
- package/dist/management/checks/index.d.ts.map +1 -0
- package/dist/management/checks/index.js +48 -0
- package/dist/management/checks/index.js.map +1 -0
- package/dist/management/checks/oauth-check.d.ts +16 -0
- package/dist/management/checks/oauth-check.d.ts.map +1 -0
- package/dist/management/checks/oauth-check.js +68 -0
- package/dist/management/checks/oauth-check.js.map +1 -0
- package/dist/management/checks/profile-check.d.ts +36 -0
- package/dist/management/checks/profile-check.d.ts.map +1 -0
- package/dist/management/checks/profile-check.js +165 -0
- package/dist/management/checks/profile-check.js.map +1 -0
- package/dist/management/checks/symlink-check.d.ts +30 -0
- package/dist/management/checks/symlink-check.d.ts.map +1 -0
- package/dist/management/checks/symlink-check.js +204 -0
- package/dist/management/checks/symlink-check.js.map +1 -0
- package/dist/management/checks/system-check.d.ts +25 -0
- package/dist/management/checks/system-check.d.ts.map +1 -0
- package/dist/management/checks/system-check.js +136 -0
- package/dist/management/checks/system-check.js.map +1 -0
- package/dist/management/checks/types.d.ts +64 -0
- package/dist/management/checks/types.d.ts.map +1 -0
- package/dist/management/checks/types.js +63 -0
- package/dist/management/checks/types.js.map +1 -0
- package/dist/management/doctor.d.ts +3 -92
- package/dist/management/doctor.d.ts.map +1 -1
- package/dist/management/doctor.js +15 -831
- package/dist/management/doctor.js.map +1 -1
- package/dist/management/index.d.ts +15 -0
- package/dist/management/index.d.ts.map +1 -0
- package/dist/management/index.js +56 -0
- package/dist/management/index.js.map +1 -0
- package/dist/management/repair/auto-repair.d.ts +13 -0
- package/dist/management/repair/auto-repair.d.ts.map +1 -0
- package/dist/management/repair/auto-repair.js +170 -0
- package/dist/management/repair/auto-repair.js.map +1 -0
- package/dist/management/repair/index.d.ts +5 -0
- package/dist/management/repair/index.d.ts.map +1 -0
- package/dist/management/repair/index.js +9 -0
- package/dist/management/repair/index.js.map +1 -0
- package/dist/types/delegation.d.ts +1 -13
- package/dist/types/delegation.d.ts.map +1 -1
- package/dist/types/utils.d.ts +27 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/ui/assets/{accounts-p1_nf0Jy.js → accounts-BqDNXZu4.js} +1 -1
- package/dist/ui/assets/analytics-DQMyOsqI.js +1 -0
- package/dist/ui/assets/api-Bzyq4XP7.js +1 -0
- package/dist/ui/assets/{card-CCDc-Mx9.js → card-CIN0KDsX.js} +1 -1
- package/dist/ui/assets/cliproxy-5iKdPrEG.js +3 -0
- package/dist/ui/assets/cliproxy-control-panel-CJ2CfYod.js +1 -0
- package/dist/ui/assets/{code-editor-Br9x-r-E.js → code-editor-CDmNFLQ6.js} +1 -1
- package/dist/ui/assets/confirm-dialog-CaiDQikc.js +1 -0
- package/dist/ui/assets/copilot-nS6iAyk2.js +4 -0
- package/dist/ui/assets/form-utils-DP6ILe7Z.js +20 -0
- package/dist/ui/assets/health-DYOQfC7u.js +1 -0
- package/dist/ui/assets/{icons-BOsxPbiD.js → icons-ZmwVoUeR.js} +1 -1
- package/dist/ui/assets/index-C6Dah-xh.js +46 -0
- package/dist/ui/assets/index-It66SkKf.css +1 -0
- package/dist/ui/assets/{radix-ui-DFHQr9A5.js → radix-ui-CV3R9pD6.js} +3 -3
- package/dist/ui/assets/settings-DABC9b5G.js +1 -0
- package/dist/ui/assets/{shared-BfYhSN4-.js → shared-BwU4OKQc.js} +1 -1
- package/dist/ui/assets/{switch-CMk95lwf.js → switch-BQ6sBBUv.js} +1 -1
- package/dist/ui/assets/{tanstack-C4gT2P7V.js → tanstack-Df9bCj5R.js} +1 -1
- package/dist/ui/index.html +6 -6
- package/dist/utils/claude-spawner.d.ts +54 -0
- package/dist/utils/claude-spawner.d.ts.map +1 -0
- package/dist/utils/claude-spawner.js +118 -0
- package/dist/utils/claude-spawner.js.map +1 -0
- package/dist/utils/delegation-validator.d.ts +8 -4
- package/dist/utils/delegation-validator.d.ts.map +1 -1
- package/dist/utils/delegation-validator.js.map +1 -1
- package/dist/utils/helpers.d.ts +0 -11
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/helpers.js +1 -57
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +50 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/time.d.ts +10 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +27 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/ui/boxes.d.ts +24 -0
- package/dist/utils/ui/boxes.d.ts.map +1 -0
- package/dist/utils/ui/boxes.js +108 -0
- package/dist/utils/ui/boxes.js.map +1 -0
- package/dist/utils/ui/colors.d.ts +25 -0
- package/dist/utils/ui/colors.d.ts.map +1 -0
- package/dist/utils/ui/colors.js +70 -0
- package/dist/utils/ui/colors.js.map +1 -0
- package/dist/utils/ui/index.d.ts +51 -0
- package/dist/utils/ui/index.d.ts.map +1 -0
- package/dist/utils/ui/index.js +96 -0
- package/dist/utils/ui/index.js.map +1 -0
- package/dist/utils/ui/indicators.d.ts +23 -0
- package/dist/utils/ui/indicators.d.ts.map +1 -0
- package/dist/utils/ui/indicators.js +39 -0
- package/dist/utils/ui/indicators.js.map +1 -0
- package/dist/utils/ui/init.d.ts +30 -0
- package/dist/utils/ui/init.d.ts.map +1 -0
- package/dist/utils/ui/init.js +102 -0
- package/dist/utils/ui/init.js.map +1 -0
- package/dist/utils/ui/spinner.d.ts +13 -0
- package/dist/utils/ui/spinner.d.ts.map +1 -0
- package/dist/utils/ui/spinner.js +89 -0
- package/dist/utils/ui/spinner.js.map +1 -0
- package/dist/utils/ui/tables.d.ts +12 -0
- package/dist/utils/ui/tables.d.ts.map +1 -0
- package/dist/utils/ui/tables.js +69 -0
- package/dist/utils/ui/tables.js.map +1 -0
- package/dist/utils/ui/tasks.d.ts +26 -0
- package/dist/utils/ui/tasks.d.ts.map +1 -0
- package/dist/utils/ui/tasks.js +102 -0
- package/dist/utils/ui/tasks.js.map +1 -0
- package/dist/utils/ui/text.d.ts +24 -0
- package/dist/utils/ui/text.d.ts.map +1 -0
- package/dist/utils/ui/text.js +60 -0
- package/dist/utils/ui/text.js.map +1 -0
- package/dist/utils/ui/types.d.ts +36 -0
- package/dist/utils/ui/types.d.ts.map +1 -0
- package/dist/utils/ui/types.js +33 -0
- package/dist/utils/ui/types.js.map +1 -0
- package/dist/utils/ui.d.ts +3 -138
- package/dist/utils/ui.d.ts.map +1 -1
- package/dist/utils/ui.js +40 -567
- package/dist/utils/ui.js.map +1 -1
- package/dist/utils/websearch/gemini-cli.d.ts +36 -0
- package/dist/utils/websearch/gemini-cli.d.ts.map +1 -0
- package/dist/utils/websearch/gemini-cli.js +132 -0
- package/dist/utils/websearch/gemini-cli.js.map +1 -0
- package/dist/utils/websearch/grok-cli.d.ts +26 -0
- package/dist/utils/websearch/grok-cli.d.ts.map +1 -0
- package/dist/utils/websearch/grok-cli.js +81 -0
- package/dist/utils/websearch/grok-cli.js.map +1 -0
- package/dist/utils/websearch/hook-config.d.ts +21 -0
- package/dist/utils/websearch/hook-config.d.ts.map +1 -0
- package/dist/utils/websearch/hook-config.js +183 -0
- package/dist/utils/websearch/hook-config.js.map +1 -0
- package/dist/utils/websearch/hook-env.d.ts +16 -0
- package/dist/utils/websearch/hook-env.d.ts.map +1 -0
- package/dist/utils/websearch/hook-env.js +62 -0
- package/dist/utils/websearch/hook-env.js.map +1 -0
- package/dist/utils/websearch/hook-installer.d.ts +27 -0
- package/dist/utils/websearch/hook-installer.d.ts.map +1 -0
- package/dist/utils/websearch/hook-installer.js +141 -0
- package/dist/utils/websearch/hook-installer.js.map +1 -0
- package/dist/utils/websearch/index.d.ts +15 -0
- package/dist/utils/websearch/index.d.ts.map +1 -0
- package/dist/utils/websearch/index.js +44 -0
- package/dist/utils/websearch/index.js.map +1 -0
- package/dist/utils/websearch/opencode-cli.d.ts +26 -0
- package/dist/utils/websearch/opencode-cli.d.ts.map +1 -0
- package/dist/utils/websearch/opencode-cli.js +81 -0
- package/dist/utils/websearch/opencode-cli.js.map +1 -0
- package/dist/utils/websearch/status.d.ts +35 -0
- package/dist/utils/websearch/status.d.ts.map +1 -0
- package/dist/utils/websearch/status.js +185 -0
- package/dist/utils/websearch/status.js.map +1 -0
- package/dist/utils/websearch/types.d.ts +85 -0
- package/dist/utils/websearch/types.d.ts.map +1 -0
- package/dist/utils/websearch/types.js +10 -0
- package/dist/utils/websearch/types.js.map +1 -0
- package/dist/utils/websearch-manager.d.ts +7 -186
- package/dist/utils/websearch-manager.d.ts.map +1 -1
- package/dist/utils/websearch-manager.js +37 -710
- package/dist/utils/websearch-manager.js.map +1 -1
- package/dist/web-server/data-aggregator.d.ts +3 -51
- package/dist/web-server/data-aggregator.d.ts.map +1 -1
- package/dist/web-server/data-aggregator.js +18 -386
- package/dist/web-server/data-aggregator.js.map +1 -1
- package/dist/web-server/health/cliproxy-checks.d.ts +23 -0
- package/dist/web-server/health/cliproxy-checks.d.ts.map +1 -0
- package/dist/web-server/health/cliproxy-checks.js +145 -0
- package/dist/web-server/health/cliproxy-checks.js.map +1 -0
- package/dist/web-server/health/config-checks.d.ts +19 -0
- package/dist/web-server/health/config-checks.d.ts.map +1 -0
- package/dist/web-server/health/config-checks.js +174 -0
- package/dist/web-server/health/config-checks.js.map +1 -0
- package/dist/web-server/health/environment-checks.d.ts +11 -0
- package/dist/web-server/health/environment-checks.d.ts.map +1 -0
- package/dist/web-server/health/environment-checks.js +40 -0
- package/dist/web-server/health/environment-checks.js.map +1 -0
- package/dist/web-server/health/index.d.ts +13 -0
- package/dist/web-server/health/index.d.ts.map +1 -0
- package/dist/web-server/health/index.js +41 -0
- package/dist/web-server/health/index.js.map +1 -0
- package/dist/web-server/health/oauth-checks.d.ts +11 -0
- package/dist/web-server/health/oauth-checks.d.ts.map +1 -0
- package/dist/web-server/health/oauth-checks.js +34 -0
- package/dist/web-server/health/oauth-checks.js.map +1 -0
- package/dist/web-server/health/profile-checks.d.ts +19 -0
- package/dist/web-server/health/profile-checks.d.ts.map +1 -0
- package/dist/web-server/health/profile-checks.js +152 -0
- package/dist/web-server/health/profile-checks.js.map +1 -0
- package/dist/web-server/health/symlink-checks.d.ts +15 -0
- package/dist/web-server/health/symlink-checks.d.ts.map +1 -0
- package/dist/web-server/health/symlink-checks.js +173 -0
- package/dist/web-server/health/symlink-checks.js.map +1 -0
- package/dist/web-server/health/system-checks.d.ts +19 -0
- package/dist/web-server/health/system-checks.d.ts.map +1 -0
- package/dist/web-server/health/system-checks.js +127 -0
- package/dist/web-server/health/system-checks.js.map +1 -0
- package/dist/web-server/health/types.d.ts +34 -0
- package/dist/web-server/health/types.d.ts.map +1 -0
- package/dist/web-server/health/types.js +8 -0
- package/dist/web-server/health/types.js.map +1 -0
- package/dist/web-server/health/websearch-checks.d.ts +11 -0
- package/dist/web-server/health/websearch-checks.d.ts.map +1 -0
- package/dist/web-server/health/websearch-checks.js +53 -0
- package/dist/web-server/health/websearch-checks.js.map +1 -0
- package/dist/web-server/health-service.d.ts +4 -30
- package/dist/web-server/health-service.d.ts.map +1 -1
- package/dist/web-server/health-service.js +28 -695
- package/dist/web-server/health-service.js.map +1 -1
- package/dist/web-server/index.d.ts.map +1 -1
- package/dist/web-server/index.js +2 -5
- package/dist/web-server/index.js.map +1 -1
- package/dist/web-server/routes/cliproxy-auth-routes.d.ts +6 -0
- package/dist/web-server/routes/cliproxy-auth-routes.d.ts.map +1 -0
- package/dist/web-server/routes/cliproxy-auth-routes.js +202 -0
- package/dist/web-server/routes/cliproxy-auth-routes.js.map +1 -0
- package/dist/web-server/routes/cliproxy-stats-routes.d.ts +6 -0
- package/dist/web-server/routes/cliproxy-stats-routes.d.ts.map +1 -0
- package/dist/web-server/routes/cliproxy-stats-routes.js +253 -0
- package/dist/web-server/routes/cliproxy-stats-routes.js.map +1 -0
- package/dist/web-server/routes/config-routes.d.ts +6 -0
- package/dist/web-server/routes/config-routes.d.ts.map +1 -0
- package/dist/web-server/routes/config-routes.js +145 -0
- package/dist/web-server/routes/config-routes.js.map +1 -0
- package/dist/web-server/routes/copilot-routes.d.ts +6 -0
- package/dist/web-server/routes/copilot-routes.d.ts.map +1 -0
- package/dist/web-server/routes/copilot-routes.js +195 -0
- package/dist/web-server/routes/copilot-routes.js.map +1 -0
- package/dist/web-server/routes/copilot-settings-routes.d.ts +6 -0
- package/dist/web-server/routes/copilot-settings-routes.d.ts.map +1 -0
- package/dist/web-server/routes/copilot-settings-routes.js +122 -0
- package/dist/web-server/routes/copilot-settings-routes.js.map +1 -0
- package/dist/web-server/routes/health-routes.d.ts +6 -0
- package/dist/web-server/routes/health-routes.d.ts.map +1 -0
- package/dist/web-server/routes/health-routes.js +30 -0
- package/dist/web-server/routes/health-routes.js.map +1 -0
- package/dist/web-server/routes/index.d.ts +8 -0
- package/dist/web-server/routes/index.d.ts.map +1 -0
- package/dist/web-server/routes/index.js +54 -0
- package/dist/web-server/routes/index.js.map +1 -0
- package/dist/web-server/routes/misc-routes.d.ts +6 -0
- package/dist/web-server/routes/misc-routes.d.ts.map +1 -0
- package/dist/web-server/routes/misc-routes.js +219 -0
- package/dist/web-server/routes/misc-routes.js.map +1 -0
- package/dist/web-server/routes/profile-routes.d.ts +6 -0
- package/dist/web-server/routes/profile-routes.d.ts.map +1 -0
- package/dist/web-server/routes/profile-routes.js +164 -0
- package/dist/web-server/routes/profile-routes.js.map +1 -0
- package/dist/web-server/routes/provider-routes.d.ts +6 -0
- package/dist/web-server/routes/provider-routes.d.ts.map +1 -0
- package/dist/web-server/routes/provider-routes.js +146 -0
- package/dist/web-server/routes/provider-routes.js.map +1 -0
- package/dist/web-server/routes/route-helpers.d.ts +55 -0
- package/dist/web-server/routes/route-helpers.d.ts.map +1 -0
- package/dist/web-server/routes/route-helpers.js +201 -0
- package/dist/web-server/routes/route-helpers.js.map +1 -0
- package/dist/web-server/routes/settings-routes.d.ts +6 -0
- package/dist/web-server/routes/settings-routes.d.ts.map +1 -0
- package/dist/web-server/routes/settings-routes.js +207 -0
- package/dist/web-server/routes/settings-routes.js.map +1 -0
- package/dist/web-server/routes/variant-routes.d.ts +6 -0
- package/dist/web-server/routes/variant-routes.d.ts.map +1 -0
- package/dist/web-server/routes/variant-routes.js +158 -0
- package/dist/web-server/routes/variant-routes.js.map +1 -0
- package/dist/web-server/routes/websearch-routes.d.ts +6 -0
- package/dist/web-server/routes/websearch-routes.d.ts.map +1 -0
- package/dist/web-server/routes/websearch-routes.js +130 -0
- package/dist/web-server/routes/websearch-routes.js.map +1 -0
- package/dist/web-server/services/index.d.ts +7 -0
- package/dist/web-server/services/index.d.ts.map +1 -0
- package/dist/web-server/services/index.js +21 -0
- package/dist/web-server/services/index.js.map +1 -0
- package/dist/web-server/services/usage-aggregator.d.ts +7 -0
- package/dist/web-server/services/usage-aggregator.d.ts.map +1 -0
- package/dist/web-server/services/usage-aggregator.js +23 -0
- package/dist/web-server/services/usage-aggregator.js.map +1 -0
- package/dist/web-server/usage/aggregator.d.ts +54 -0
- package/dist/web-server/usage/aggregator.d.ts.map +1 -0
- package/dist/web-server/usage/aggregator.js +470 -0
- package/dist/web-server/usage/aggregator.js.map +1 -0
- package/dist/web-server/usage/data-aggregator.d.ts +55 -0
- package/dist/web-server/usage/data-aggregator.d.ts.map +1 -0
- package/dist/web-server/usage/data-aggregator.js +391 -0
- package/dist/web-server/usage/data-aggregator.js.map +1 -0
- package/dist/web-server/usage/disk-cache.d.ts +46 -0
- package/dist/web-server/usage/disk-cache.d.ts.map +1 -0
- package/dist/web-server/usage/disk-cache.js +162 -0
- package/dist/web-server/usage/disk-cache.js.map +1 -0
- package/dist/web-server/usage/handlers.d.ts +50 -0
- package/dist/web-server/usage/handlers.d.ts.map +1 -0
- package/dist/web-server/usage/handlers.js +524 -0
- package/dist/web-server/usage/handlers.js.map +1 -0
- package/dist/web-server/usage/index.d.ts +12 -0
- package/dist/web-server/usage/index.d.ts.map +1 -0
- package/dist/web-server/usage/index.js +54 -0
- package/dist/web-server/usage/index.js.map +1 -0
- package/dist/web-server/usage/routes.d.ts +11 -0
- package/dist/web-server/usage/routes.d.ts.map +1 -0
- package/dist/web-server/usage/routes.js +37 -0
- package/dist/web-server/usage/routes.js.map +1 -0
- package/dist/web-server/usage/types.d.ts +119 -0
- package/dist/web-server/usage/types.d.ts.map +1 -0
- package/dist/web-server/usage/types.js +9 -0
- package/dist/web-server/usage/types.js.map +1 -0
- package/dist/web-server/usage-disk-cache.d.ts +3 -42
- package/dist/web-server/usage-disk-cache.d.ts.map +1 -1
- package/dist/web-server/usage-disk-cache.js +5 -144
- package/dist/web-server/usage-disk-cache.js.map +1 -1
- package/dist/web-server/usage-routes.d.ts +3 -31
- package/dist/web-server/usage-routes.d.ts.map +1 -1
- package/dist/web-server/usage-routes.js +8 -1139
- package/dist/web-server/usage-routes.js.map +1 -1
- package/dist/web-server/usage-types.d.ts +3 -115
- package/dist/web-server/usage-types.d.ts.map +1 -1
- package/dist/web-server/usage-types.js +17 -3
- package/dist/web-server/usage-types.js.map +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/analytics-D1k2nYcF.js +0 -1
- package/dist/ui/assets/api-D9VVmZ1j.js +0 -1
- package/dist/ui/assets/cliproxy-CTt_Nsft.js +0 -1
- package/dist/ui/assets/cliproxy-control-panel-DKMAi4_4.js +0 -1
- package/dist/ui/assets/copilot-C6F8PT_L.js +0 -4
- package/dist/ui/assets/form-utils-BuXDJb0w.js +0 -20
- package/dist/ui/assets/health-DiAvJaSn.js +0 -1
- package/dist/ui/assets/index-Brq6EBKZ.css +0 -1
- package/dist/ui/assets/index-ByU8ZhED.js +0 -48
- package/dist/ui/assets/settings-CEomZLGl.js +0 -1
- package/dist/web-server/routes.d.ts +0 -7
- package/dist/web-server/routes.d.ts.map +0 -1
- package/dist/web-server/routes.js +0 -1891
- 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
|