@kaitranntt/ccs 5.15.0 → 5.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/VERSION +1 -1
  2. package/dist/cliproxy/account-manager.d.ts.map +1 -1
  3. package/dist/cliproxy/account-manager.js +35 -0
  4. package/dist/cliproxy/account-manager.js.map +1 -1
  5. package/dist/cliproxy/config-generator.d.ts +21 -0
  6. package/dist/cliproxy/config-generator.d.ts.map +1 -1
  7. package/dist/cliproxy/config-generator.js +122 -16
  8. package/dist/cliproxy/config-generator.js.map +1 -1
  9. package/dist/cliproxy/index.d.ts +7 -1
  10. package/dist/cliproxy/index.d.ts.map +1 -1
  11. package/dist/cliproxy/index.js +20 -2
  12. package/dist/cliproxy/index.js.map +1 -1
  13. package/dist/cliproxy/openai-compat-manager.d.ts +46 -0
  14. package/dist/cliproxy/openai-compat-manager.d.ts.map +1 -0
  15. package/dist/cliproxy/openai-compat-manager.js +191 -0
  16. package/dist/cliproxy/openai-compat-manager.js.map +1 -0
  17. package/dist/cliproxy/service-manager.d.ts +43 -0
  18. package/dist/cliproxy/service-manager.d.ts.map +1 -0
  19. package/dist/cliproxy/service-manager.js +220 -0
  20. package/dist/cliproxy/service-manager.js.map +1 -0
  21. package/dist/cliproxy/stats-fetcher.d.ts +59 -0
  22. package/dist/cliproxy/stats-fetcher.d.ts.map +1 -0
  23. package/dist/cliproxy/stats-fetcher.js +134 -0
  24. package/dist/cliproxy/stats-fetcher.js.map +1 -0
  25. package/dist/commands/config-command.d.ts +1 -0
  26. package/dist/commands/config-command.d.ts.map +1 -1
  27. package/dist/commands/config-command.js +24 -1
  28. package/dist/commands/config-command.js.map +1 -1
  29. package/dist/management/doctor.d.ts.map +1 -1
  30. package/dist/management/doctor.js +21 -7
  31. package/dist/management/doctor.js.map +1 -1
  32. package/dist/types/config.d.ts +13 -0
  33. package/dist/types/config.d.ts.map +1 -1
  34. package/dist/types/config.js.map +1 -1
  35. package/dist/ui/assets/accounts-Bl9qfu_d.js +1 -0
  36. package/dist/ui/assets/analytics-BHnr8pTE.js +64 -0
  37. package/dist/ui/assets/{api-LYILKXgM.js → api-DoPJHgqb.js} +1 -1
  38. package/dist/ui/assets/cliproxy-DRxRpTJv.js +1 -0
  39. package/dist/ui/assets/cliproxy-control-panel-CNpMpeWs.js +1 -0
  40. package/dist/ui/assets/{code-editor-HkboCFlD.js → code-editor-ek-uxeLN.js} +11 -11
  41. package/dist/ui/assets/health-DBc3-gE9.js +1 -0
  42. package/dist/ui/assets/icons-CqXn5eV0.js +1 -0
  43. package/dist/ui/assets/index-D-zCRwmw.js +12 -0
  44. package/dist/ui/assets/index-DMEwxqAV.css +1 -0
  45. package/dist/ui/assets/providers/agy.png +0 -0
  46. package/dist/ui/assets/providers/gemini-color.svg +1 -0
  47. package/dist/ui/assets/providers/openai.svg +1 -0
  48. package/dist/ui/assets/providers/qwen-color.svg +1 -0
  49. package/dist/ui/assets/{radix-ui-CgfZoNEt.js → radix-ui-OFtPgiRV.js} +1 -1
  50. package/dist/ui/assets/{settings-DUCWbKbP.js → settings-Dh8DSszQ.js} +1 -1
  51. package/dist/ui/assets/{shared-Da5f_jsc.js → shared-n31ZWpeq.js} +1 -1
  52. package/dist/ui/assets/{tanstack-BgSYOn90.js → tanstack-DMWkeNzM.js} +1 -1
  53. package/dist/ui/index.html +5 -5
  54. package/dist/web-server/routes.d.ts.map +1 -1
  55. package/dist/web-server/routes.js +329 -4
  56. package/dist/web-server/routes.js.map +1 -1
  57. package/package.json +1 -1
  58. package/scripts/verify-bundle.js +11 -4
  59. package/dist/ui/assets/accounts-CTh48XsV.js +0 -1
  60. package/dist/ui/assets/analytics-CeU8ZE3O.js +0 -64
  61. package/dist/ui/assets/cliproxy-Cy2Bjv-c.js +0 -1
  62. package/dist/ui/assets/health-DyAiAnv0.js +0 -1
  63. package/dist/ui/assets/icons-CRbZgamU.js +0 -1
  64. package/dist/ui/assets/index-BIVAVDH5.js +0 -10
  65. package/dist/ui/assets/index-vojI8oaQ.css +0 -1
  66. package/dist/ui/assets/table-BCRYixSo.js +0 -1
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAI Compatibility Layer Manager
4
+ *
5
+ * Manages OpenAI-compatible providers (OpenRouter, Together, etc.)
6
+ * in CLIProxyAPI's config.yaml.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.TOGETHER_TEMPLATE = exports.OPENROUTER_TEMPLATE = exports.removeOpenAICompatProvider = exports.updateOpenAICompatProvider = exports.addOpenAICompatProvider = exports.getOpenAICompatProvider = exports.listOpenAICompatProviders = void 0;
33
+ const fs = __importStar(require("fs"));
34
+ const yaml = __importStar(require("js-yaml"));
35
+ const config_generator_1 = require("./config-generator");
36
+ /**
37
+ * Load current config.yaml
38
+ */
39
+ function loadConfig() {
40
+ const configPath = (0, config_generator_1.getConfigPath)();
41
+ if (!fs.existsSync(configPath)) {
42
+ return {};
43
+ }
44
+ try {
45
+ const content = fs.readFileSync(configPath, 'utf-8');
46
+ return yaml.load(content) || {};
47
+ }
48
+ catch {
49
+ return {};
50
+ }
51
+ }
52
+ /**
53
+ * Save config.yaml with proper formatting
54
+ */
55
+ function saveConfig(config) {
56
+ const configPath = (0, config_generator_1.getConfigPath)();
57
+ const content = yaml.dump(config, {
58
+ lineWidth: -1, // Disable line wrapping
59
+ quotingType: '"',
60
+ forceQuotes: false,
61
+ });
62
+ fs.writeFileSync(configPath, content, { mode: 0o600 });
63
+ }
64
+ /**
65
+ * List all configured OpenAI-compatible providers
66
+ */
67
+ function listOpenAICompatProviders() {
68
+ const config = loadConfig();
69
+ const providers = config['openai-compatibility'] || [];
70
+ return providers.map((p) => ({
71
+ name: p.name,
72
+ baseUrl: p['base-url'],
73
+ apiKey: p['api-key-entries']?.[0]?.['api-key'] || '',
74
+ models: (p.models || []).map((m) => ({
75
+ name: m.name,
76
+ alias: m.alias,
77
+ })),
78
+ }));
79
+ }
80
+ exports.listOpenAICompatProviders = listOpenAICompatProviders;
81
+ /**
82
+ * Get a specific provider by name
83
+ */
84
+ function getOpenAICompatProvider(name) {
85
+ const providers = listOpenAICompatProviders();
86
+ return providers.find((p) => p.name === name) || null;
87
+ }
88
+ exports.getOpenAICompatProvider = getOpenAICompatProvider;
89
+ /**
90
+ * Add a new OpenAI-compatible provider
91
+ * @throws Error if provider with same name already exists
92
+ */
93
+ function addOpenAICompatProvider(provider) {
94
+ const config = loadConfig();
95
+ // Initialize array if not exists
96
+ if (!config['openai-compatibility']) {
97
+ config['openai-compatibility'] = [];
98
+ }
99
+ // Check for duplicate
100
+ const existing = config['openai-compatibility'].find((p) => p.name === provider.name);
101
+ if (existing) {
102
+ throw new Error(`Provider '${provider.name}' already exists`);
103
+ }
104
+ // Add new provider
105
+ config['openai-compatibility'].push({
106
+ name: provider.name,
107
+ 'base-url': provider.baseUrl,
108
+ 'api-key-entries': [{ 'api-key': provider.apiKey }],
109
+ models: provider.models.map((m) => ({
110
+ name: m.name,
111
+ alias: m.alias,
112
+ })),
113
+ });
114
+ saveConfig(config);
115
+ }
116
+ exports.addOpenAICompatProvider = addOpenAICompatProvider;
117
+ /**
118
+ * Update an existing provider
119
+ * @throws Error if provider doesn't exist
120
+ */
121
+ function updateOpenAICompatProvider(name, updates) {
122
+ const config = loadConfig();
123
+ if (!config['openai-compatibility']) {
124
+ throw new Error(`Provider '${name}' not found`);
125
+ }
126
+ const index = config['openai-compatibility'].findIndex((p) => p.name === name);
127
+ if (index === -1) {
128
+ throw new Error(`Provider '${name}' not found`);
129
+ }
130
+ const provider = config['openai-compatibility'][index];
131
+ // Apply updates
132
+ if (updates.baseUrl) {
133
+ provider['base-url'] = updates.baseUrl;
134
+ }
135
+ if (updates.apiKey) {
136
+ provider['api-key-entries'] = [{ 'api-key': updates.apiKey }];
137
+ }
138
+ if (updates.models) {
139
+ provider.models = updates.models.map((m) => ({
140
+ name: m.name,
141
+ alias: m.alias,
142
+ }));
143
+ }
144
+ if (updates.name && updates.name !== name) {
145
+ provider.name = updates.name;
146
+ }
147
+ saveConfig(config);
148
+ }
149
+ exports.updateOpenAICompatProvider = updateOpenAICompatProvider;
150
+ /**
151
+ * Remove a provider
152
+ * @returns true if removed, false if not found
153
+ */
154
+ function removeOpenAICompatProvider(name) {
155
+ const config = loadConfig();
156
+ if (!config['openai-compatibility']) {
157
+ return false;
158
+ }
159
+ const index = config['openai-compatibility'].findIndex((p) => p.name === name);
160
+ if (index === -1) {
161
+ return false;
162
+ }
163
+ config['openai-compatibility'].splice(index, 1);
164
+ // Remove empty array
165
+ if (config['openai-compatibility'].length === 0) {
166
+ delete config['openai-compatibility'];
167
+ }
168
+ saveConfig(config);
169
+ return true;
170
+ }
171
+ exports.removeOpenAICompatProvider = removeOpenAICompatProvider;
172
+ /** Pre-configured OpenRouter template */
173
+ exports.OPENROUTER_TEMPLATE = {
174
+ name: 'openrouter',
175
+ baseUrl: 'https://openrouter.ai/api/v1',
176
+ models: [
177
+ { name: 'anthropic/claude-3.5-sonnet', alias: 'claude-sonnet' },
178
+ { name: 'anthropic/claude-3-opus', alias: 'claude-opus' },
179
+ { name: 'google/gemini-pro-1.5', alias: 'gemini-pro' },
180
+ ],
181
+ };
182
+ /** Pre-configured Together template */
183
+ exports.TOGETHER_TEMPLATE = {
184
+ name: 'together',
185
+ baseUrl: 'https://api.together.xyz/v1',
186
+ models: [
187
+ { name: 'meta-llama/Llama-3-70b-chat-hf', alias: 'llama-70b' },
188
+ { name: 'mistralai/Mixtral-8x7B-Instruct-v0.1', alias: 'mixtral' },
189
+ ],
190
+ };
191
+ //# sourceMappingURL=openai-compat-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-compat-manager.js","sourceRoot":"","sources":["../../src/cliproxy/openai-compat-manager.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,uCAAyB;AACzB,8CAAgC;AAChC,yDAAmD;AAqCnD;;GAEG;AACH,SAAS,UAAU;IACjB,MAAM,UAAU,GAAG,IAAA,gCAAa,GAAE,CAAC;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACrD,OAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,CAAgB,IAAI,EAAE,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,MAAkB;IACpC,MAAM,UAAU,GAAG,IAAA,gCAAa,GAAE,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAChC,SAAS,EAAE,CAAC,CAAC,EAAE,wBAAwB;QACvC,WAAW,EAAE,GAAG;QAChB,WAAW,EAAE,KAAK;KACnB,CAAC,CAAC;IAEH,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,SAAgB,yBAAyB;IACvC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,MAAM,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC;IAEvD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC;QACtB,MAAM,EAAE,CAAC,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE;QACpD,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC,CAAC;KACJ,CAAC,CAAC,CAAC;AACN,CAAC;AAbD,8DAaC;AAED;;GAEG;AACH,SAAgB,uBAAuB,CAAC,IAAY;IAClD,MAAM,SAAS,GAAG,yBAAyB,EAAE,CAAC;IAC9C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACxD,CAAC;AAHD,0DAGC;AAED;;;GAGG;AACH,SAAgB,uBAAuB,CAAC,QAA8B;IACpE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,iCAAiC;IACjC,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,sBAAsB,CAAC,GAAG,EAAE,CAAC;IACtC,CAAC;IAED,sBAAsB;IACtB,MAAM,QAAQ,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,aAAa,QAAQ,CAAC,IAAI,kBAAkB,CAAC,CAAC;IAChE,CAAC;IAED,mBAAmB;IACnB,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC;QAClC,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,UAAU,EAAE,QAAQ,CAAC,OAAO;QAC5B,iBAAiB,EAAE,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;QACnD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAClC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC,CAAC;KACJ,CAAC,CAAC;IAEH,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AA1BD,0DA0BC;AAED;;;GAGG;AACH,SAAgB,0BAA0B,CACxC,IAAY,EACZ,OAAsC;IAEtC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,aAAa,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC/E,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,aAAa,IAAI,aAAa,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC,KAAK,CAAC,CAAC;IAEvD,gBAAgB;IAChB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IACzC,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,QAAQ,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,QAAQ,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3C,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC,CAAC,CAAC;IACN,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/B,CAAC;IAED,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAnCD,gEAmCC;AAED;;;GAGG;AACH,SAAgB,0BAA0B,CAAC,IAAY;IACrD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACpC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC/E,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,CAAC,sBAAsB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAEhD,qBAAqB;IACrB,IAAI,MAAM,CAAC,sBAAsB,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,sBAAsB,CAAC,CAAC;IACxC,CAAC;IAED,UAAU,CAAC,MAAM,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC;AACd,CAAC;AArBD,gEAqBC;AAED,yCAAyC;AAC5B,QAAA,mBAAmB,GAAyC;IACvE,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,8BAA8B;IACvC,MAAM,EAAE;QACN,EAAE,IAAI,EAAE,6BAA6B,EAAE,KAAK,EAAE,eAAe,EAAE;QAC/D,EAAE,IAAI,EAAE,yBAAyB,EAAE,KAAK,EAAE,aAAa,EAAE;QACzD,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,YAAY,EAAE;KACvD;CACF,CAAC;AAEF,uCAAuC;AAC1B,QAAA,iBAAiB,GAAyC;IACrE,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,6BAA6B;IACtC,MAAM,EAAE;QACN,EAAE,IAAI,EAAE,gCAAgC,EAAE,KAAK,EAAE,WAAW,EAAE;QAC9D,EAAE,IAAI,EAAE,sCAAsC,EAAE,KAAK,EAAE,SAAS,EAAE;KACnE;CACF,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * CLIProxy Service Manager
3
+ *
4
+ * Manages CLIProxyAPI as a background service for the CCS dashboard.
5
+ * Ensures the proxy is running when needed for:
6
+ * - Control Panel integration (management.html)
7
+ * - Stats fetching
8
+ * - OAuth flows
9
+ *
10
+ * Unlike cliproxy-executor.ts which runs proxy per-session,
11
+ * this module manages a persistent background instance.
12
+ */
13
+ export interface ServiceStartResult {
14
+ started: boolean;
15
+ alreadyRunning: boolean;
16
+ port: number;
17
+ configRegenerated?: boolean;
18
+ error?: string;
19
+ }
20
+ /**
21
+ * Ensure CLIProxy service is running
22
+ *
23
+ * If proxy is already running, returns immediately.
24
+ * If not, spawns a new background instance.
25
+ *
26
+ * @param port CLIProxy port (default: 8317)
27
+ * @param verbose Show debug output
28
+ * @returns Result indicating success and whether it was already running
29
+ */
30
+ export declare function ensureCliproxyService(port?: number, verbose?: boolean): Promise<ServiceStartResult>;
31
+ /**
32
+ * Stop the managed CLIProxy service
33
+ */
34
+ export declare function stopCliproxyService(): boolean;
35
+ /**
36
+ * Get service status
37
+ */
38
+ export declare function getServiceStatus(port?: number): Promise<{
39
+ running: boolean;
40
+ managedByUs: boolean;
41
+ port: number;
42
+ }>;
43
+ //# sourceMappingURL=service-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-manager.d.ts","sourceRoot":"","sources":["../../src/cliproxy/service-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA6EH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,GAAE,MAA8B,EACpC,OAAO,GAAE,OAAe,GACvB,OAAO,CAAC,kBAAkB,CAAC,CA4G7B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAO7C;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,MAA8B,GAAG,OAAO,CAAC;IACpF,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CAKD"}
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ /**
3
+ * CLIProxy Service Manager
4
+ *
5
+ * Manages CLIProxyAPI as a background service for the CCS dashboard.
6
+ * Ensures the proxy is running when needed for:
7
+ * - Control Panel integration (management.html)
8
+ * - Stats fetching
9
+ * - OAuth flows
10
+ *
11
+ * Unlike cliproxy-executor.ts which runs proxy per-session,
12
+ * this module manages a persistent background instance.
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.getServiceStatus = exports.stopCliproxyService = exports.ensureCliproxyService = void 0;
39
+ const child_process_1 = require("child_process");
40
+ const net = __importStar(require("net"));
41
+ const binary_manager_1 = require("./binary-manager");
42
+ const config_generator_1 = require("./config-generator");
43
+ const stats_fetcher_1 = require("./stats-fetcher");
44
+ /** Background proxy process reference */
45
+ let proxyProcess = null;
46
+ /** Cleanup registered flag */
47
+ let cleanupRegistered = false;
48
+ /**
49
+ * Wait for TCP port to become available
50
+ */
51
+ async function waitForPort(port, timeout = 5000, pollInterval = 100) {
52
+ const start = Date.now();
53
+ while (Date.now() - start < timeout) {
54
+ try {
55
+ await new Promise((resolve, reject) => {
56
+ const socket = net.createConnection({ port, host: '127.0.0.1' }, () => {
57
+ socket.destroy();
58
+ resolve();
59
+ });
60
+ socket.on('error', (err) => {
61
+ socket.destroy();
62
+ reject(err);
63
+ });
64
+ socket.setTimeout(500, () => {
65
+ socket.destroy();
66
+ reject(new Error('Connection timeout'));
67
+ });
68
+ });
69
+ return true; // Connection successful
70
+ }
71
+ catch {
72
+ await new Promise((r) => setTimeout(r, pollInterval));
73
+ }
74
+ }
75
+ return false;
76
+ }
77
+ /**
78
+ * Register cleanup handlers to stop proxy on process exit
79
+ */
80
+ function registerCleanup() {
81
+ if (cleanupRegistered)
82
+ return;
83
+ const cleanup = () => {
84
+ if (proxyProcess && !proxyProcess.killed) {
85
+ proxyProcess.kill('SIGTERM');
86
+ proxyProcess = null;
87
+ }
88
+ };
89
+ process.once('exit', cleanup);
90
+ process.once('SIGTERM', cleanup);
91
+ process.once('SIGINT', cleanup);
92
+ cleanupRegistered = true;
93
+ }
94
+ /**
95
+ * Ensure CLIProxy service is running
96
+ *
97
+ * If proxy is already running, returns immediately.
98
+ * If not, spawns a new background instance.
99
+ *
100
+ * @param port CLIProxy port (default: 8317)
101
+ * @param verbose Show debug output
102
+ * @returns Result indicating success and whether it was already running
103
+ */
104
+ async function ensureCliproxyService(port = config_generator_1.CLIPROXY_DEFAULT_PORT, verbose = false) {
105
+ const log = (msg) => {
106
+ if (verbose) {
107
+ console.error(`[cliproxy-service] ${msg}`);
108
+ }
109
+ };
110
+ // Check if already running (from another process or previous start)
111
+ log(`Checking if CLIProxy is running on port ${port}...`);
112
+ const running = await (0, stats_fetcher_1.isCliproxyRunning)(port);
113
+ // Check if config needs update (even if running)
114
+ let configRegenerated = false;
115
+ if ((0, config_generator_1.configNeedsRegeneration)()) {
116
+ log('Config outdated, regenerating...');
117
+ (0, config_generator_1.regenerateConfig)(port);
118
+ configRegenerated = true;
119
+ }
120
+ if (running) {
121
+ log('CLIProxy already running');
122
+ if (configRegenerated) {
123
+ log('Config was updated - running instance will use new config on next restart');
124
+ }
125
+ return { started: true, alreadyRunning: true, port, configRegenerated };
126
+ }
127
+ // Need to start new instance
128
+ log('CLIProxy not running, starting background instance...');
129
+ // 1. Ensure binary exists
130
+ let binaryPath;
131
+ try {
132
+ binaryPath = await (0, binary_manager_1.ensureCLIProxyBinary)(verbose);
133
+ log(`Binary ready: ${binaryPath}`);
134
+ }
135
+ catch (error) {
136
+ const err = error;
137
+ return {
138
+ started: false,
139
+ alreadyRunning: false,
140
+ port,
141
+ error: `Failed to prepare binary: ${err.message}`,
142
+ };
143
+ }
144
+ // 2. Ensure/regenerate config if needed
145
+ let configPath;
146
+ if ((0, config_generator_1.configNeedsRegeneration)()) {
147
+ log('Config needs regeneration, updating...');
148
+ configPath = (0, config_generator_1.regenerateConfig)(port);
149
+ }
150
+ else {
151
+ // generateConfig only creates if doesn't exist
152
+ configPath = (0, config_generator_1.generateConfig)('gemini', port); // Provider doesn't matter for unified config
153
+ }
154
+ log(`Config ready: ${configPath}`);
155
+ // 3. Spawn background process
156
+ const proxyArgs = ['--config', configPath];
157
+ log(`Spawning: ${binaryPath} ${proxyArgs.join(' ')}`);
158
+ proxyProcess = (0, child_process_1.spawn)(binaryPath, proxyArgs, {
159
+ stdio: ['ignore', verbose ? 'pipe' : 'ignore', verbose ? 'pipe' : 'ignore'],
160
+ detached: true, // Allow process to run independently
161
+ });
162
+ // Forward output in verbose mode
163
+ if (verbose) {
164
+ proxyProcess.stdout?.on('data', (data) => {
165
+ process.stderr.write(`[cliproxy] ${data.toString()}`);
166
+ });
167
+ proxyProcess.stderr?.on('data', (data) => {
168
+ process.stderr.write(`[cliproxy-err] ${data.toString()}`);
169
+ });
170
+ }
171
+ // Don't let this process prevent parent from exiting
172
+ proxyProcess.unref();
173
+ // Handle spawn errors
174
+ proxyProcess.on('error', (error) => {
175
+ log(`Spawn error: ${error.message}`);
176
+ });
177
+ // Register cleanup handlers
178
+ registerCleanup();
179
+ // 4. Wait for proxy to be ready
180
+ log(`Waiting for CLIProxy on port ${port}...`);
181
+ const ready = await waitForPort(port, 5000);
182
+ if (!ready) {
183
+ // Kill failed process
184
+ if (proxyProcess && !proxyProcess.killed) {
185
+ proxyProcess.kill('SIGTERM');
186
+ proxyProcess = null;
187
+ }
188
+ return {
189
+ started: false,
190
+ alreadyRunning: false,
191
+ port,
192
+ error: `CLIProxy failed to start within 5s on port ${port}`,
193
+ };
194
+ }
195
+ log(`CLIProxy service started on port ${port}`);
196
+ return { started: true, alreadyRunning: false, port };
197
+ }
198
+ exports.ensureCliproxyService = ensureCliproxyService;
199
+ /**
200
+ * Stop the managed CLIProxy service
201
+ */
202
+ function stopCliproxyService() {
203
+ if (proxyProcess && !proxyProcess.killed) {
204
+ proxyProcess.kill('SIGTERM');
205
+ proxyProcess = null;
206
+ return true;
207
+ }
208
+ return false;
209
+ }
210
+ exports.stopCliproxyService = stopCliproxyService;
211
+ /**
212
+ * Get service status
213
+ */
214
+ async function getServiceStatus(port = config_generator_1.CLIPROXY_DEFAULT_PORT) {
215
+ const running = await (0, stats_fetcher_1.isCliproxyRunning)(port);
216
+ const managedByUs = proxyProcess !== null && !proxyProcess.killed;
217
+ return { running, managedByUs, port };
218
+ }
219
+ exports.getServiceStatus = getServiceStatus;
220
+ //# sourceMappingURL=service-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-manager.js","sourceRoot":"","sources":["../../src/cliproxy/service-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * CLIProxyAPI Stats Fetcher
3
+ *
4
+ * Fetches usage statistics from CLIProxyAPI's management API.
5
+ * Requires usage-statistics-enabled: true in config.yaml.
6
+ */
7
+ /** Usage statistics from CLIProxyAPI */
8
+ export interface CliproxyStats {
9
+ /** Total number of requests processed */
10
+ totalRequests: number;
11
+ /** Token counts */
12
+ tokens: {
13
+ input: number;
14
+ output: number;
15
+ total: number;
16
+ };
17
+ /** Requests grouped by model */
18
+ requestsByModel: Record<string, number>;
19
+ /** Requests grouped by provider */
20
+ requestsByProvider: Record<string, number>;
21
+ /** Number of quota exceeded (429) events */
22
+ quotaExceededCount: number;
23
+ /** Number of request retries */
24
+ retryCount: number;
25
+ /** Timestamp of stats collection */
26
+ collectedAt: string;
27
+ }
28
+ /**
29
+ * Fetch usage statistics from CLIProxyAPI management API
30
+ * @param port CLIProxyAPI port (default: 8317)
31
+ * @returns Stats object or null if unavailable
32
+ */
33
+ export declare function fetchCliproxyStats(port?: number): Promise<CliproxyStats | null>;
34
+ /** OpenAI-compatible model object from /v1/models endpoint */
35
+ export interface CliproxyModel {
36
+ id: string;
37
+ object: string;
38
+ created: number;
39
+ owned_by: string;
40
+ }
41
+ /** Categorized models response for UI */
42
+ export interface CliproxyModelsResponse {
43
+ models: CliproxyModel[];
44
+ byCategory: Record<string, CliproxyModel[]>;
45
+ totalCount: number;
46
+ }
47
+ /**
48
+ * Fetch available models from CLIProxyAPI /v1/models endpoint
49
+ * @param port CLIProxyAPI port (default: 8317)
50
+ * @returns Categorized models or null if unavailable
51
+ */
52
+ export declare function fetchCliproxyModels(port?: number): Promise<CliproxyModelsResponse | null>;
53
+ /**
54
+ * Check if CLIProxyAPI is running and responsive
55
+ * @param port CLIProxyAPI port (default: 8317)
56
+ * @returns true if proxy is running
57
+ */
58
+ export declare function isCliproxyRunning(port?: number): Promise<boolean>;
59
+ //# sourceMappingURL=stats-fetcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats-fetcher.d.ts","sourceRoot":"","sources":["../../src/cliproxy/stats-fetcher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB;IACnB,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,gCAAgC;IAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,mCAAmC;IACnC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,4CAA4C;IAC5C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;CACrB;AA2BD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,GAAE,MAA8B,GACnC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAuD/B;AAED,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAQD,yCAAyC;AACzC,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,GAAE,MAA8B,GACnC,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CA6CxC;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,MAA8B,GAAG,OAAO,CAAC,OAAO,CAAC,CAe9F"}
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ /**
3
+ * CLIProxyAPI Stats Fetcher
4
+ *
5
+ * Fetches usage statistics from CLIProxyAPI's management API.
6
+ * Requires usage-statistics-enabled: true in config.yaml.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isCliproxyRunning = exports.fetchCliproxyModels = exports.fetchCliproxyStats = void 0;
10
+ const config_generator_1 = require("./config-generator");
11
+ /**
12
+ * Fetch usage statistics from CLIProxyAPI management API
13
+ * @param port CLIProxyAPI port (default: 8317)
14
+ * @returns Stats object or null if unavailable
15
+ */
16
+ async function fetchCliproxyStats(port = config_generator_1.CLIPROXY_DEFAULT_PORT) {
17
+ try {
18
+ const controller = new AbortController();
19
+ const timeoutId = setTimeout(() => controller.abort(), 3000); // 3s timeout
20
+ const response = await fetch(`http://127.0.0.1:${port}/v0/management/usage`, {
21
+ signal: controller.signal,
22
+ headers: {
23
+ Accept: 'application/json',
24
+ Authorization: `Bearer ${config_generator_1.CCS_CONTROL_PANEL_SECRET}`,
25
+ },
26
+ });
27
+ clearTimeout(timeoutId);
28
+ if (!response.ok) {
29
+ return null;
30
+ }
31
+ const data = (await response.json());
32
+ const usage = data.usage;
33
+ // Extract models and providers from the nested API structure
34
+ const requestsByModel = {};
35
+ const requestsByProvider = {};
36
+ if (usage?.apis) {
37
+ for (const [provider, providerData] of Object.entries(usage.apis)) {
38
+ requestsByProvider[provider] = providerData.total_requests ?? 0;
39
+ if (providerData.models) {
40
+ for (const [model, modelData] of Object.entries(providerData.models)) {
41
+ requestsByModel[model] = modelData.total_requests ?? 0;
42
+ }
43
+ }
44
+ }
45
+ }
46
+ // Normalize the response to our interface
47
+ return {
48
+ totalRequests: usage?.total_requests ?? 0,
49
+ tokens: {
50
+ input: 0, // API doesn't provide input/output breakdown
51
+ output: 0,
52
+ total: usage?.total_tokens ?? 0,
53
+ },
54
+ requestsByModel,
55
+ requestsByProvider,
56
+ quotaExceededCount: usage?.failure_count ?? data.failed_requests ?? 0,
57
+ retryCount: 0, // API doesn't track retries separately
58
+ collectedAt: new Date().toISOString(),
59
+ };
60
+ }
61
+ catch {
62
+ // CLIProxyAPI not running or stats endpoint not available
63
+ return null;
64
+ }
65
+ }
66
+ exports.fetchCliproxyStats = fetchCliproxyStats;
67
+ /**
68
+ * Fetch available models from CLIProxyAPI /v1/models endpoint
69
+ * @param port CLIProxyAPI port (default: 8317)
70
+ * @returns Categorized models or null if unavailable
71
+ */
72
+ async function fetchCliproxyModels(port = config_generator_1.CLIPROXY_DEFAULT_PORT) {
73
+ try {
74
+ const controller = new AbortController();
75
+ const timeoutId = setTimeout(() => controller.abort(), 3000);
76
+ const response = await fetch(`http://127.0.0.1:${port}/v1/models`, {
77
+ signal: controller.signal,
78
+ headers: {
79
+ Accept: 'application/json',
80
+ // Use the internal API key for /v1 endpoints
81
+ Authorization: 'Bearer ccs-internal-managed',
82
+ },
83
+ });
84
+ clearTimeout(timeoutId);
85
+ if (!response.ok) {
86
+ return null;
87
+ }
88
+ const data = (await response.json());
89
+ // Group models by owned_by field
90
+ const byCategory = {};
91
+ for (const model of data.data) {
92
+ const category = model.owned_by || 'other';
93
+ if (!byCategory[category]) {
94
+ byCategory[category] = [];
95
+ }
96
+ byCategory[category].push(model);
97
+ }
98
+ // Sort models within each category alphabetically
99
+ for (const category of Object.keys(byCategory)) {
100
+ byCategory[category].sort((a, b) => a.id.localeCompare(b.id));
101
+ }
102
+ return {
103
+ models: data.data,
104
+ byCategory,
105
+ totalCount: data.data.length,
106
+ };
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ exports.fetchCliproxyModels = fetchCliproxyModels;
113
+ /**
114
+ * Check if CLIProxyAPI is running and responsive
115
+ * @param port CLIProxyAPI port (default: 8317)
116
+ * @returns true if proxy is running
117
+ */
118
+ async function isCliproxyRunning(port = config_generator_1.CLIPROXY_DEFAULT_PORT) {
119
+ try {
120
+ const controller = new AbortController();
121
+ const timeoutId = setTimeout(() => controller.abort(), 1000); // 1s timeout
122
+ // Use root endpoint - CLIProxyAPI returns server info at /
123
+ const response = await fetch(`http://127.0.0.1:${port}/`, {
124
+ signal: controller.signal,
125
+ });
126
+ clearTimeout(timeoutId);
127
+ return response.ok;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ }
133
+ exports.isCliproxyRunning = isCliproxyRunning;
134
+ //# sourceMappingURL=stats-fetcher.js.map