@kaitranntt/ccs 6.8.0-dev.1 → 7.0.0-dev.2

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 (103) hide show
  1. package/README.md +5 -0
  2. package/dist/api/services/index.d.ts +3 -0
  3. package/dist/api/services/index.d.ts.map +1 -1
  4. package/dist/api/services/index.js +14 -1
  5. package/dist/api/services/index.js.map +1 -1
  6. package/dist/api/services/openrouter-catalog.d.ts +30 -0
  7. package/dist/api/services/openrouter-catalog.d.ts.map +1 -0
  8. package/dist/api/services/openrouter-catalog.js +130 -0
  9. package/dist/api/services/openrouter-catalog.js.map +1 -0
  10. package/dist/api/services/openrouter-picker.d.ts +15 -0
  11. package/dist/api/services/openrouter-picker.d.ts.map +1 -0
  12. package/dist/api/services/openrouter-picker.js +113 -0
  13. package/dist/api/services/openrouter-picker.js.map +1 -0
  14. package/dist/api/services/profile-reader.d.ts +3 -0
  15. package/dist/api/services/profile-reader.d.ts.map +1 -1
  16. package/dist/api/services/profile-reader.js +14 -9
  17. package/dist/api/services/profile-reader.js.map +1 -1
  18. package/dist/api/services/profile-writer.d.ts.map +1 -1
  19. package/dist/api/services/profile-writer.js +8 -3
  20. package/dist/api/services/profile-writer.js.map +1 -1
  21. package/dist/api/services/provider-presets.d.ts +36 -0
  22. package/dist/api/services/provider-presets.d.ts.map +1 -0
  23. package/dist/api/services/provider-presets.js +89 -0
  24. package/dist/api/services/provider-presets.js.map +1 -0
  25. package/dist/auth/profile-detector.d.ts.map +1 -1
  26. package/dist/auth/profile-detector.js +1 -4
  27. package/dist/auth/profile-detector.js.map +1 -1
  28. package/dist/cliproxy/remote-proxy-client.d.ts +1 -1
  29. package/dist/cliproxy/remote-proxy-client.d.ts.map +1 -1
  30. package/dist/cliproxy/remote-proxy-client.js +55 -13
  31. package/dist/cliproxy/remote-proxy-client.js.map +1 -1
  32. package/dist/commands/api-command.d.ts.map +1 -1
  33. package/dist/commands/api-command.js +82 -30
  34. package/dist/commands/api-command.js.map +1 -1
  35. package/dist/config/index.d.ts +0 -1
  36. package/dist/config/index.d.ts.map +1 -1
  37. package/dist/config/index.js +0 -1
  38. package/dist/config/index.js.map +1 -1
  39. package/dist/config/migration-manager.d.ts.map +1 -1
  40. package/dist/config/migration-manager.js +0 -3
  41. package/dist/config/migration-manager.js.map +1 -1
  42. package/dist/config/unified-config-loader.d.ts.map +1 -1
  43. package/dist/config/unified-config-loader.js +15 -0
  44. package/dist/config/unified-config-loader.js.map +1 -1
  45. package/dist/config/unified-config-types.d.ts +1 -20
  46. package/dist/config/unified-config-types.d.ts.map +1 -1
  47. package/dist/config/unified-config-types.js +2 -22
  48. package/dist/config/unified-config-types.js.map +1 -1
  49. package/dist/management/recovery-manager.d.ts +3 -0
  50. package/dist/management/recovery-manager.d.ts.map +1 -1
  51. package/dist/management/recovery-manager.js +6 -11
  52. package/dist/management/recovery-manager.js.map +1 -1
  53. package/dist/ui/assets/{accounts--0JKEYYO.js → accounts-DQwh-CP_.js} +1 -1
  54. package/dist/ui/assets/{analytics-CX5PurM9.js → analytics-BR435WpH.js} +1 -1
  55. package/dist/ui/assets/api-ALobfD8T.js +1 -0
  56. package/dist/ui/assets/card-ByK9DlE_.js +1 -0
  57. package/dist/ui/assets/{cliproxy-D4AoDTZB.js → cliproxy-BlkTmqj-.js} +2 -2
  58. package/dist/ui/assets/{cliproxy-control-panel-CJ2CfYod.js → cliproxy-control-panel-CvVI3tSn.js} +1 -1
  59. package/dist/ui/assets/{code-editor-DIj6qdUG.js → code-editor-Bm8H1Oot.js} +2 -2
  60. package/dist/ui/assets/{confirm-dialog-DZkzZLrs.js → confirm-dialog-DQeTgOVm.js} +1 -1
  61. package/dist/ui/assets/copilot-_EhQN_rN.js +4 -0
  62. package/dist/ui/assets/health-CApaflOt.js +1 -0
  63. package/dist/ui/assets/icons-DvvDkxaF.js +1 -0
  64. package/dist/ui/assets/index-DDxKFig1.js +46 -0
  65. package/dist/ui/assets/index-Dfyx0piD.css +1 -0
  66. package/dist/ui/assets/settings-ByVm4JFM.js +1 -0
  67. package/dist/ui/assets/{shared--pI8rTbP.js → shared-C9bG-pqJ.js} +1 -1
  68. package/dist/ui/assets/{switch-BP6SzLyZ.js → switch-CasTIVeB.js} +1 -1
  69. package/dist/ui/icons/openrouter.svg +1 -0
  70. package/dist/ui/index.html +3 -3
  71. package/dist/utils/config-manager.d.ts +3 -1
  72. package/dist/utils/config-manager.d.ts.map +1 -1
  73. package/dist/utils/config-manager.js +41 -5
  74. package/dist/utils/config-manager.js.map +1 -1
  75. package/dist/utils/update-checker.d.ts +4 -0
  76. package/dist/utils/update-checker.d.ts.map +1 -1
  77. package/dist/utils/update-checker.js +7 -3
  78. package/dist/utils/update-checker.js.map +1 -1
  79. package/dist/web-server/routes/config-routes.d.ts.map +1 -1
  80. package/dist/web-server/routes/config-routes.js +0 -30
  81. package/dist/web-server/routes/config-routes.js.map +1 -1
  82. package/dist/web-server/routes/index.js +1 -2
  83. package/dist/web-server/routes/index.js.map +1 -1
  84. package/dist/web-server/routes/profile-routes.d.ts +2 -0
  85. package/dist/web-server/routes/profile-routes.d.ts.map +1 -1
  86. package/dist/web-server/routes/profile-routes.js +32 -32
  87. package/dist/web-server/routes/profile-routes.js.map +1 -1
  88. package/package.json +1 -1
  89. package/scripts/dev-release.sh +123 -0
  90. package/scripts/postinstall.js +11 -220
  91. package/scripts/send-discord-release.cjs +37 -2
  92. package/dist/config/secrets-manager.d.ts +0 -51
  93. package/dist/config/secrets-manager.d.ts.map +0 -1
  94. package/dist/config/secrets-manager.js +0 -194
  95. package/dist/config/secrets-manager.js.map +0 -1
  96. package/dist/ui/assets/api-D80tfZOx.js +0 -1
  97. package/dist/ui/assets/card-B-DPlvgo.js +0 -1
  98. package/dist/ui/assets/copilot-Q9tyCgYD.js +0 -4
  99. package/dist/ui/assets/health-Cw76uZRy.js +0 -1
  100. package/dist/ui/assets/icons-ZmwVoUeR.js +0 -1
  101. package/dist/ui/assets/index-C1W0iP_Z.js +0 -46
  102. package/dist/ui/assets/index-It66SkKf.css +0 -1
  103. package/dist/ui/assets/settings-CDdHW_gU.js +0 -1
@@ -68,12 +68,9 @@ function validateConfiguration() {
68
68
  errors.push('~/.ccs/ directory not found');
69
69
  }
70
70
 
71
- // Check required files
71
+ // Check required files (GLM/GLMT/Kimi are now optional - created via presets)
72
72
  const requiredFiles = [
73
- { path: path.join(ccsDir, 'config.json'), name: 'config.json' },
74
- { path: path.join(ccsDir, 'glm.settings.json'), name: 'glm.settings.json' },
75
- { path: path.join(ccsDir, 'glmt.settings.json'), name: 'glmt.settings.json' },
76
- { path: path.join(ccsDir, 'kimi.settings.json'), name: 'kimi.settings.json' }
73
+ { path: path.join(ccsDir, 'config.json'), name: 'config.json' }
77
74
  ];
78
75
 
79
76
  for (const file of requiredFiles) {
@@ -156,17 +153,15 @@ function createConfigFiles() {
156
153
  // Create config.json if missing
157
154
  // NOTE: gemini/codex profiles NOT included - they are added on-demand when user
158
155
  // runs `ccs gemini` or `ccs codex` for first time (requires OAuth auth first)
156
+ // NOTE: GLM/GLMT/Kimi profiles are now created via UI/CLI presets, not auto-created
159
157
  const configPath = path.join(ccsDir, 'config.json');
160
158
  if (!fs.existsSync(configPath)) {
161
159
  // NOTE: No 'default' entry - when no profile specified, CCS passes through
162
160
  // to Claude's native auth without --settings flag. This prevents env var
163
161
  // pollution from affecting the default profile.
162
+ // Profiles are empty by default - users create via `ccs api create --preset` or UI
164
163
  const config = {
165
- profiles: {
166
- glm: '~/.ccs/glm.settings.json',
167
- glmt: '~/.ccs/glmt.settings.json',
168
- kimi: '~/.ccs/kimi.settings.json'
169
- }
164
+ profiles: {}
170
165
  };
171
166
 
172
167
  // Atomic write: temp file → rename
@@ -213,216 +208,12 @@ function createConfigFiles() {
213
208
  }
214
209
  }
215
210
 
216
- // Create glm.settings.json if missing
217
- const glmSettingsPath = path.join(ccsDir, 'glm.settings.json');
218
- if (!fs.existsSync(glmSettingsPath)) {
219
- const glmSettings = {
220
- env: {
221
- ANTHROPIC_BASE_URL: 'https://api.z.ai/api/anthropic',
222
- ANTHROPIC_AUTH_TOKEN: 'YOUR_GLM_API_KEY_HERE',
223
- ANTHROPIC_MODEL: 'glm-4.6',
224
- ANTHROPIC_DEFAULT_OPUS_MODEL: 'glm-4.6',
225
- ANTHROPIC_DEFAULT_SONNET_MODEL: 'glm-4.6',
226
- ANTHROPIC_DEFAULT_HAIKU_MODEL: 'glm-4.6'
227
- }
228
- };
229
-
230
- // Atomic write
231
- const tmpPath = `${glmSettingsPath}.tmp`;
232
- fs.writeFileSync(tmpPath, JSON.stringify(glmSettings, null, 2) + '\n', 'utf8');
233
- fs.renameSync(tmpPath, glmSettingsPath);
234
-
235
- console.log('[OK] Created GLM profile: ~/.ccs/glm.settings.json');
236
- console.log('');
237
- console.log(' [!] Configure GLM API key:');
238
- console.log(' 1. Get key from: https://api.z.ai');
239
- console.log(' 2. Edit: ~/.ccs/glm.settings.json');
240
- console.log(' 3. Replace: YOUR_GLM_API_KEY_HERE');
241
- } else {
242
- console.log('[OK] GLM profile exists: ~/.ccs/glm.settings.json (preserved)');
243
- }
244
-
245
- // Create glmt.settings.json if missing
246
- const glmtSettingsPath = path.join(ccsDir, 'glmt.settings.json');
247
- if (!fs.existsSync(glmtSettingsPath)) {
248
- const glmtSettings = {
249
- env: {
250
- ANTHROPIC_BASE_URL: 'https://api.z.ai/api/coding/paas/v4/chat/completions',
251
- ANTHROPIC_AUTH_TOKEN: 'YOUR_GLM_API_KEY_HERE',
252
- ANTHROPIC_MODEL: 'glm-4.6',
253
- ANTHROPIC_DEFAULT_OPUS_MODEL: 'glm-4.6',
254
- ANTHROPIC_DEFAULT_SONNET_MODEL: 'glm-4.6',
255
- ANTHROPIC_DEFAULT_HAIKU_MODEL: 'glm-4.6',
256
- ANTHROPIC_TEMPERATURE: '0.2',
257
- ANTHROPIC_MAX_TOKENS: '65536',
258
- MAX_THINKING_TOKENS: '32768',
259
- ENABLE_STREAMING: 'true',
260
- ANTHROPIC_SAFE_MODE: 'false',
261
- API_TIMEOUT_MS: '3000000'
262
- },
263
- alwaysThinkingEnabled: true
264
- };
265
-
266
- // Atomic write
267
- const tmpPath = `${glmtSettingsPath}.tmp`;
268
- fs.writeFileSync(tmpPath, JSON.stringify(glmtSettings, null, 2) + '\n', 'utf8');
269
- fs.renameSync(tmpPath, glmtSettingsPath);
270
-
271
- console.log('[OK] Created GLMT profile: ~/.ccs/glmt.settings.json');
272
- console.log('');
273
- console.log(' [!] Configure GLMT API key:');
274
- console.log(' 1. Get key from: https://api.z.ai');
275
- console.log(' 2. Edit: ~/.ccs/glmt.settings.json');
276
- console.log(' 3. Replace: YOUR_GLM_API_KEY_HERE');
277
- console.log(' Note: GLMT enables GLM thinking mode (reasoning)');
278
- console.log(' Defaults: Temperature 0.2, thinking enabled, 50min timeout');
279
- } else {
280
- console.log('[OK] GLMT profile exists: ~/.ccs/glmt.settings.json (preserved)');
281
- }
282
-
283
- // Migrate existing GLMT configs to include new defaults (v3.3.0)
284
- if (fs.existsSync(glmtSettingsPath)) {
285
- try {
286
- const existing = JSON.parse(fs.readFileSync(glmtSettingsPath, 'utf8'));
287
- let updated = false;
288
-
289
- // Ensure env object exists
290
- if (!existing.env) {
291
- existing.env = {};
292
- updated = true;
293
- }
294
-
295
- // Add missing env vars (preserve existing values)
296
- const envDefaults = {
297
- ANTHROPIC_TEMPERATURE: '0.2',
298
- ANTHROPIC_MAX_TOKENS: '65536',
299
- MAX_THINKING_TOKENS: '32768',
300
- ENABLE_STREAMING: 'true',
301
- ANTHROPIC_SAFE_MODE: 'false',
302
- API_TIMEOUT_MS: '3000000'
303
- };
304
-
305
- for (const [key, value] of Object.entries(envDefaults)) {
306
- if (existing.env[key] === undefined) {
307
- existing.env[key] = value;
308
- updated = true;
309
- }
310
- }
311
-
312
- // Add alwaysThinkingEnabled if missing
313
- if (existing.alwaysThinkingEnabled === undefined) {
314
- existing.alwaysThinkingEnabled = true;
315
- updated = true;
316
- }
317
-
318
- // Write back if updated
319
- if (updated) {
320
- const tmpPath = `${glmtSettingsPath}.tmp`;
321
- fs.writeFileSync(tmpPath, JSON.stringify(existing, null, 2) + '\n', 'utf8');
322
- fs.renameSync(tmpPath, glmtSettingsPath);
323
- console.log('[OK] Migrated GLMT config with new defaults (v3.3.0)');
324
- console.log(' Added: temperature, max_tokens, thinking settings, alwaysThinkingEnabled');
325
- }
326
- } catch (err) {
327
- console.warn('[!] GLMT config migration failed:', err.message);
328
- console.warn(' Existing config preserved, may be missing new defaults');
329
- console.warn(' You can manually add fields or delete file to regenerate');
330
- }
331
- }
332
-
333
- // Create kimi.settings.json if missing
334
- const kimiSettingsPath = path.join(ccsDir, 'kimi.settings.json');
335
- if (!fs.existsSync(kimiSettingsPath)) {
336
- const kimiSettings = {
337
- env: {
338
- ANTHROPIC_BASE_URL: 'https://api.kimi.com/coding/',
339
- ANTHROPIC_AUTH_TOKEN: 'YOUR_KIMI_API_KEY_HERE',
340
- ANTHROPIC_MODEL: 'kimi-k2-thinking-turbo',
341
- ANTHROPIC_DEFAULT_OPUS_MODEL: 'kimi-k2-thinking-turbo',
342
- ANTHROPIC_DEFAULT_SONNET_MODEL: 'kimi-k2-thinking-turbo',
343
- ANTHROPIC_DEFAULT_HAIKU_MODEL: 'kimi-k2-thinking-turbo'
344
- },
345
- alwaysThinkingEnabled: true
346
- };
347
-
348
- // Atomic write
349
- const tmpPath = `${kimiSettingsPath}.tmp`;
350
- fs.writeFileSync(tmpPath, JSON.stringify(kimiSettings, null, 2) + '\n', 'utf8');
351
- fs.renameSync(tmpPath, kimiSettingsPath);
352
-
353
- console.log('[OK] Created Kimi profile: ~/.ccs/kimi.settings.json');
354
- console.log('');
355
- console.log(' [!] Configure Kimi API key:');
356
- console.log(' 1. Get key from: https://www.kimi.com/coding (membership page)');
357
- console.log(' 2. Edit: ~/.ccs/kimi.settings.json');
358
- console.log(' 3. Replace: YOUR_KIMI_API_KEY_HERE');
359
- } else {
360
- console.log('[OK] Kimi profile exists: ~/.ccs/kimi.settings.json (preserved)');
361
- }
362
-
363
- // NOTE: gemini.settings.json and codex.settings.json are NOT created during install
364
- // They are created on-demand when user runs `ccs gemini` or `ccs codex` for the first time
365
- // This prevents confusion - users need to run `--auth` first anyway
366
-
367
- // Migrate existing Kimi configs to use kimi-k2-thinking-turbo model (v5.5.0)
368
- // Kimi API now supports model specification with thinking models
369
- if (fs.existsSync(kimiSettingsPath)) {
370
- try {
371
- const existing = JSON.parse(fs.readFileSync(kimiSettingsPath, 'utf8'));
372
- let updated = false;
373
- const defaultModel = 'kimi-k2-thinking-turbo';
374
-
375
- // Ensure env object exists
376
- if (!existing.env) {
377
- existing.env = {};
378
- updated = true;
379
- }
380
-
381
- // Add/update model fields to use kimi-k2-thinking-turbo
382
- const modelFields = {
383
- ANTHROPIC_MODEL: defaultModel,
384
- ANTHROPIC_DEFAULT_OPUS_MODEL: defaultModel,
385
- ANTHROPIC_DEFAULT_SONNET_MODEL: defaultModel,
386
- ANTHROPIC_DEFAULT_HAIKU_MODEL: defaultModel
387
- };
388
-
389
- for (const [field, value] of Object.entries(modelFields)) {
390
- if (existing.env[field] !== value) {
391
- existing.env[field] = value;
392
- updated = true;
393
- }
394
- }
395
-
396
- // Remove deprecated ANTHROPIC_SMALL_FAST_MODEL if present
397
- if (existing.env.ANTHROPIC_SMALL_FAST_MODEL !== undefined) {
398
- delete existing.env.ANTHROPIC_SMALL_FAST_MODEL;
399
- updated = true;
400
- }
401
-
402
- // Ensure required fields exist
403
- if (!existing.env.ANTHROPIC_BASE_URL) {
404
- existing.env.ANTHROPIC_BASE_URL = 'https://api.kimi.com/coding/';
405
- updated = true;
406
- }
407
-
408
- // Add alwaysThinkingEnabled if missing
409
- if (existing.alwaysThinkingEnabled === undefined) {
410
- existing.alwaysThinkingEnabled = true;
411
- updated = true;
412
- }
413
-
414
- // Write back if updated
415
- if (updated) {
416
- const tmpPath = `${kimiSettingsPath}.tmp`;
417
- fs.writeFileSync(tmpPath, JSON.stringify(existing, null, 2) + '\n', 'utf8');
418
- fs.renameSync(tmpPath, kimiSettingsPath);
419
- console.log('[OK] Migrated Kimi config (v5.5.0): updated to kimi-k2-thinking-turbo model');
420
- }
421
- } catch (err) {
422
- console.warn('[!] Kimi config migration failed:', err.message);
423
- console.warn(' Existing config preserved');
424
- }
425
- }
211
+ // NOTE: GLM, GLMT, and Kimi profiles are NO LONGER auto-created during install
212
+ // Users can create these via:
213
+ // - UI: Profile Create Dialog → Provider Presets
214
+ // - CLI: ccs api create --preset glm|glmt|kimi
215
+ // This gives users control over which providers they want to use
216
+ // Existing profiles are preserved for backward compatibility
426
217
 
427
218
  // Copy shell completion files to ~/.ccs/completions/
428
219
  const completionsDir = path.join(ccsDir, 'completions');
@@ -34,7 +34,7 @@ try {
34
34
  }
35
35
 
36
36
  /**
37
- * Extract latest release from CHANGELOG.md
37
+ * Extract latest release from CHANGELOG.md (for production releases)
38
38
  */
39
39
  function extractLatestRelease() {
40
40
  const changelogPath = 'CHANGELOG.md';
@@ -92,6 +92,40 @@ function extractLatestRelease() {
92
92
  return { version, date, sections };
93
93
  }
94
94
 
95
+ /**
96
+ * Extract dev release info from .dev-release-info.json (for dev releases)
97
+ * Falls back to CHANGELOG.md if file not found
98
+ */
99
+ function extractDevRelease() {
100
+ const devInfoPath = '.dev-release-info.json';
101
+
102
+ if (!fs.existsSync(devInfoPath)) {
103
+ console.log('[!] .dev-release-info.json not found, falling back to CHANGELOG.md');
104
+ return extractLatestRelease();
105
+ }
106
+
107
+ try {
108
+ const content = fs.readFileSync(devInfoPath, 'utf8');
109
+ const info = JSON.parse(content);
110
+
111
+ // Parse commit messages into a simple Changes section
112
+ const changes = info.notes
113
+ .split('\n')
114
+ .filter((line) => line.trim().startsWith('-'))
115
+ .map((line) => line.trim().substring(1).trim())
116
+ .filter((item) => item);
117
+
118
+ return {
119
+ version: info.version,
120
+ date: new Date().toISOString().split('T')[0],
121
+ sections: changes.length > 0 ? { Changes: changes } : {},
122
+ };
123
+ } catch (error) {
124
+ console.error('[!] Error reading .dev-release-info.json:', error.message);
125
+ return extractLatestRelease();
126
+ }
127
+ }
128
+
95
129
  /**
96
130
  * Create Discord embed
97
131
  */
@@ -203,7 +237,8 @@ function sendToDiscord(embed) {
203
237
 
204
238
  // Main
205
239
  try {
206
- const release = extractLatestRelease();
240
+ const isDev = releaseType === 'dev';
241
+ const release = isDev ? extractDevRelease() : extractLatestRelease();
207
242
  console.log(`[i] Preparing ${releaseType} notification for v${release.version}`);
208
243
 
209
244
  const embed = createEmbed(release);
@@ -1,51 +0,0 @@
1
- /**
2
- * Secrets Manager
3
- *
4
- * Handles loading and saving secrets (API keys, tokens) in a separate file
5
- * with restricted permissions (chmod 600).
6
- */
7
- import { SecretsConfig } from './unified-config-types';
8
- export { isSensitiveKey as isSecretKey } from '../utils/sensitive-keys';
9
- /**
10
- * Get path to secrets.yaml
11
- */
12
- export declare function getSecretsPath(): string;
13
- /**
14
- * Check if secrets.yaml exists
15
- */
16
- export declare function hasSecrets(): boolean;
17
- /**
18
- * Load secrets from YAML file.
19
- * Returns empty secrets config if file doesn't exist.
20
- */
21
- export declare function loadSecrets(): SecretsConfig;
22
- /**
23
- * Save secrets to YAML file with restricted permissions.
24
- * Uses atomic write (temp file + rename) to prevent corruption.
25
- */
26
- export declare function saveSecrets(secrets: SecretsConfig): void;
27
- /**
28
- * Get a secret value for a specific profile.
29
- */
30
- export declare function getProfileSecret(profileName: string, key: string): string | undefined;
31
- /**
32
- * Set a secret value for a specific profile.
33
- */
34
- export declare function setProfileSecret(profileName: string, key: string, value: string): void;
35
- /**
36
- * Delete a secret value for a specific profile.
37
- */
38
- export declare function deleteProfileSecret(profileName: string, key: string): boolean;
39
- /**
40
- * Get all secrets for a profile.
41
- */
42
- export declare function getProfileSecrets(profileName: string): Record<string, string>;
43
- /**
44
- * Set all secrets for a profile (replaces existing).
45
- */
46
- export declare function setProfileSecrets(profileName: string, profileSecrets: Record<string, string>): void;
47
- /**
48
- * Delete all secrets for a profile.
49
- */
50
- export declare function deleteAllProfileSecrets(profileName: string): boolean;
51
- //# sourceMappingURL=secrets-manager.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"secrets-manager.d.ts","sourceRoot":"","sources":["../../src/config/secrets-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAE,aAAa,EAA6C,MAAM,wBAAwB,CAAC;AAGlG,OAAO,EAAE,cAAc,IAAI,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAKxE;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,aAAa,CAsB3C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAqCxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGrF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAStF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAgB7E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAG7E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACrC,IAAI,CAUN;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAUpE"}
@@ -1,194 +0,0 @@
1
- "use strict";
2
- /**
3
- * Secrets Manager
4
- *
5
- * Handles loading and saving secrets (API keys, tokens) in a separate file
6
- * with restricted permissions (chmod 600).
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.deleteAllProfileSecrets = exports.setProfileSecrets = exports.getProfileSecrets = exports.deleteProfileSecret = exports.setProfileSecret = exports.getProfileSecret = exports.saveSecrets = exports.loadSecrets = exports.hasSecrets = exports.getSecretsPath = exports.isSecretKey = void 0;
33
- const fs = __importStar(require("fs"));
34
- const path = __importStar(require("path"));
35
- const yaml = __importStar(require("js-yaml"));
36
- const config_manager_1 = require("../utils/config-manager");
37
- const unified_config_types_1 = require("./unified-config-types");
38
- // Re-export from shared utility for backward compatibility
39
- var sensitive_keys_1 = require("../utils/sensitive-keys");
40
- Object.defineProperty(exports, "isSecretKey", { enumerable: true, get: function () { return sensitive_keys_1.isSensitiveKey; } });
41
- const SECRETS_FILE = 'secrets.yaml';
42
- const SECRETS_FILE_MODE = 0o600; // Owner read/write only
43
- /**
44
- * Get path to secrets.yaml
45
- */
46
- function getSecretsPath() {
47
- return path.join((0, config_manager_1.getCcsDir)(), SECRETS_FILE);
48
- }
49
- exports.getSecretsPath = getSecretsPath;
50
- /**
51
- * Check if secrets.yaml exists
52
- */
53
- function hasSecrets() {
54
- return fs.existsSync(getSecretsPath());
55
- }
56
- exports.hasSecrets = hasSecrets;
57
- /**
58
- * Load secrets from YAML file.
59
- * Returns empty secrets config if file doesn't exist.
60
- */
61
- function loadSecrets() {
62
- const secretsPath = getSecretsPath();
63
- if (!fs.existsSync(secretsPath)) {
64
- return (0, unified_config_types_1.createEmptySecretsConfig)();
65
- }
66
- try {
67
- const content = fs.readFileSync(secretsPath, 'utf8');
68
- const parsed = yaml.load(content);
69
- if (!(0, unified_config_types_1.isSecretsConfig)(parsed)) {
70
- console.error(`[!] Invalid secrets format in ${secretsPath}`);
71
- return (0, unified_config_types_1.createEmptySecretsConfig)();
72
- }
73
- return parsed;
74
- }
75
- catch (err) {
76
- const error = err instanceof Error ? err.message : 'Unknown error';
77
- console.error(`[X] Failed to load secrets: ${error}`);
78
- return (0, unified_config_types_1.createEmptySecretsConfig)();
79
- }
80
- }
81
- exports.loadSecrets = loadSecrets;
82
- /**
83
- * Save secrets to YAML file with restricted permissions.
84
- * Uses atomic write (temp file + rename) to prevent corruption.
85
- */
86
- function saveSecrets(secrets) {
87
- const secretsPath = getSecretsPath();
88
- const dir = path.dirname(secretsPath);
89
- // Ensure directory exists
90
- if (!fs.existsSync(dir)) {
91
- fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
92
- }
93
- // Convert to YAML
94
- const content = yaml.dump(secrets, {
95
- indent: 2,
96
- lineWidth: -1,
97
- quotingType: '"',
98
- noRefs: true,
99
- });
100
- // Atomic write: write to temp file, then rename
101
- const tempPath = `${secretsPath}.tmp.${process.pid}`;
102
- try {
103
- fs.writeFileSync(tempPath, content, { mode: SECRETS_FILE_MODE });
104
- fs.renameSync(tempPath, secretsPath);
105
- // Ensure correct permissions after rename (some systems may not preserve)
106
- fs.chmodSync(secretsPath, SECRETS_FILE_MODE);
107
- }
108
- catch (err) {
109
- // Clean up temp file on error
110
- if (fs.existsSync(tempPath)) {
111
- try {
112
- fs.unlinkSync(tempPath);
113
- }
114
- catch {
115
- // Ignore cleanup errors
116
- }
117
- }
118
- throw err;
119
- }
120
- }
121
- exports.saveSecrets = saveSecrets;
122
- /**
123
- * Get a secret value for a specific profile.
124
- */
125
- function getProfileSecret(profileName, key) {
126
- const secrets = loadSecrets();
127
- return secrets.profiles[profileName]?.[key];
128
- }
129
- exports.getProfileSecret = getProfileSecret;
130
- /**
131
- * Set a secret value for a specific profile.
132
- */
133
- function setProfileSecret(profileName, key, value) {
134
- const secrets = loadSecrets();
135
- if (!secrets.profiles[profileName]) {
136
- secrets.profiles[profileName] = {};
137
- }
138
- secrets.profiles[profileName][key] = value;
139
- saveSecrets(secrets);
140
- }
141
- exports.setProfileSecret = setProfileSecret;
142
- /**
143
- * Delete a secret value for a specific profile.
144
- */
145
- function deleteProfileSecret(profileName, key) {
146
- const secrets = loadSecrets();
147
- if (!secrets.profiles[profileName]?.[key]) {
148
- return false;
149
- }
150
- delete secrets.profiles[profileName][key];
151
- // Clean up empty profile object
152
- if (Object.keys(secrets.profiles[profileName]).length === 0) {
153
- delete secrets.profiles[profileName];
154
- }
155
- saveSecrets(secrets);
156
- return true;
157
- }
158
- exports.deleteProfileSecret = deleteProfileSecret;
159
- /**
160
- * Get all secrets for a profile.
161
- */
162
- function getProfileSecrets(profileName) {
163
- const secrets = loadSecrets();
164
- return secrets.profiles[profileName] || {};
165
- }
166
- exports.getProfileSecrets = getProfileSecrets;
167
- /**
168
- * Set all secrets for a profile (replaces existing).
169
- */
170
- function setProfileSecrets(profileName, profileSecrets) {
171
- const secrets = loadSecrets();
172
- if (Object.keys(profileSecrets).length === 0) {
173
- delete secrets.profiles[profileName];
174
- }
175
- else {
176
- secrets.profiles[profileName] = profileSecrets;
177
- }
178
- saveSecrets(secrets);
179
- }
180
- exports.setProfileSecrets = setProfileSecrets;
181
- /**
182
- * Delete all secrets for a profile.
183
- */
184
- function deleteAllProfileSecrets(profileName) {
185
- const secrets = loadSecrets();
186
- if (!secrets.profiles[profileName]) {
187
- return false;
188
- }
189
- delete secrets.profiles[profileName];
190
- saveSecrets(secrets);
191
- return true;
192
- }
193
- exports.deleteAllProfileSecrets = deleteAllProfileSecrets;
194
- //# sourceMappingURL=secrets-manager.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"secrets-manager.js","sourceRoot":"","sources":["../../src/config/secrets-manager.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- import{j as e}from"./radix-ui-CV3R9pD6.js";import{r as d}from"./react-vendor-FspHSO0w.js";import{e as C,B as f,I as E,i as L,L as P,k,T as B,l as Y,m as F,n as M,R as W,o as X,p as G,q as Z,c as ee}from"./index-C1W0iP_Z.js";import{C as _,S as se}from"./confirm-dialog-DZkzZLrs.js";import{u as te,a as ae,b as ne}from"./tanstack-Df9bCj5R.js";import{t as D}from"./notifications-B2Pz7lik.js";import{a2 as T,a5 as $,L as K,a6 as re,E as ie,d as le,J as I,I as ce,a7 as oe,$ as de,a8 as me,a9 as U,aa as xe,k as V,z as ue,D as he}from"./icons-ZmwVoUeR.js";import"./utils-CzKF5WmX.js";import"./form-utils-DP6ILe7Z.js";import"./code-highlight-BoHWVYut.js";function fe({profileName:s,data:t,isLoading:n,isSaving:c,hasChanges:u,isRawJsonValid:a,onRefresh:i,onDelete:o,onSave:g}){return e.jsxs("div",{className:"px-6 py-4 border-b bg-background flex items-center justify-between shrink-0",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-lg font-semibold",children:s}),t?.path&&e.jsx(C,{variant:"outline",className:"text-xs",children:t.path.replace(/^.*\//,"")})]}),t&&e.jsxs("p",{className:"text-xs text-muted-foreground mt-0.5",children:["Last modified: ",new Date(t.mtime).toLocaleString()]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(f,{variant:"ghost",size:"sm",onClick:i,disabled:n,children:e.jsx(T,{className:`w-4 h-4 ${n?"animate-spin":""}`})}),o&&e.jsx(f,{variant:"ghost",size:"sm",onClick:o,children:e.jsx($,{className:"w-4 h-4 text-destructive"})}),e.jsx(f,{size:"sm",onClick:g,disabled:c||!u||!a,children:c?e.jsxs(e.Fragment,{children:[e.jsx(K,{className:"w-4 h-4 mr-1 animate-spin"}),"Saving..."]}):e.jsxs(e.Fragment,{children:[e.jsx(re,{className:"w-4 h-4 mr-1"}),"Save"]})})]})]})}function pe({label:s,...t}){const[n,c]=d.useState(!1);return e.jsxs("div",{className:"space-y-1",children:[s&&e.jsx("label",{className:"text-sm font-medium",children:s}),e.jsxs("div",{className:"relative",children:[e.jsx(E,{type:n?"text":"password",className:"pr-10 font-mono",...t}),e.jsx(f,{type:"button",variant:"ghost",size:"sm",className:"absolute right-0 top-0 h-full px-3",onClick:()=>c(!n),tabIndex:-1,children:n?e.jsx(ie,{className:"w-4 h-4"}):e.jsx(le,{className:"w-4 h-4"})})]})]})}function z(s){return[/^ANTHROPIC_AUTH_TOKEN$/,/_API_KEY$/,/_AUTH_TOKEN$/,/^API_KEY$/,/^AUTH_TOKEN$/,/_SECRET$/,/^SECRET$/].some(n=>n.test(s))}function je({currentSettings:s,newEnvKey:t,onNewEnvKeyChange:n,onEnvValueChange:c,onAddEnvVar:u}){return e.jsxs(e.Fragment,{children:[e.jsx(L,{className:"flex-1",children:e.jsx("div",{className:"p-4 space-y-4",children:s?.env&&Object.keys(s.env).length>0?e.jsx(e.Fragment,{children:Object.entries(s.env).map(([a,i])=>e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs(P,{className:"text-xs font-medium flex items-center gap-2 text-muted-foreground",children:[a,z(a)&&e.jsx(C,{variant:"secondary",className:"text-[10px] px-1 py-0 h-4",children:"sensitive"})]}),z(a)?e.jsx(pe,{value:i,onChange:o=>c(a,o.target.value),className:"font-mono text-sm h-8"}):e.jsx(E,{value:i,onChange:o=>c(a,o.target.value),className:"font-mono text-sm h-8"})]},a))}):e.jsxs("div",{className:"py-8 text-center text-muted-foreground bg-muted/30 rounded-lg border border-dashed text-sm",children:[e.jsx("p",{children:"No environment variables configured."}),e.jsx("p",{className:"text-xs mt-1 opacity-70",children:"Add variables using the input below or edit the JSON directly."})]})})}),e.jsxs("div",{className:"p-4 border-t bg-background shrink-0",children:[e.jsx(P,{className:"text-xs font-medium text-muted-foreground",children:"Add Environment Variable"}),e.jsxs("div",{className:"flex gap-2 mt-2",children:[e.jsx(E,{placeholder:"VARIABLE_NAME",value:t,onChange:a=>n(a.target.value.toUpperCase()),className:"font-mono text-sm h-8",onKeyDown:a=>a.key==="Enter"&&u()}),e.jsx(f,{variant:"outline",size:"sm",className:"h-8",onClick:u,disabled:!t.trim(),children:e.jsx(I,{className:"w-4 h-4"})})]})]})]})}function ge({profileName:s,data:t}){return e.jsx(L,{className:"h-full",children:e.jsxs("div",{className:"p-4 space-y-6",children:[e.jsxs("div",{children:[e.jsxs("h3",{className:"text-sm font-medium flex items-center gap-2 mb-3",children:[e.jsx(ce,{className:"w-4 h-4"}),"Profile Information"]}),e.jsx("div",{className:"space-y-3 bg-card rounded-lg border p-4 shadow-sm",children:t&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-[100px_1fr] gap-2 text-sm items-center",children:[e.jsx("span",{className:"font-medium text-muted-foreground",children:"Profile Name"}),e.jsx("span",{className:"font-mono",children:t.profile})]}),e.jsxs("div",{className:"grid grid-cols-[100px_1fr] gap-2 text-sm items-center",children:[e.jsx("span",{className:"font-medium text-muted-foreground",children:"File Path"}),e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx("code",{className:"bg-muted px-1.5 py-0.5 rounded text-xs break-all",children:t.path}),e.jsx(k,{value:t.path,size:"icon",className:"h-5 w-5"})]})]}),e.jsxs("div",{className:"grid grid-cols-[100px_1fr] gap-2 text-sm items-center",children:[e.jsx("span",{className:"font-medium text-muted-foreground",children:"Last Modified"}),e.jsx("span",{className:"text-xs",children:new Date(t.mtime).toLocaleString()})]})]})})]}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-sm font-medium mb-3",children:"Quick Usage"}),e.jsxs("div",{className:"space-y-3 bg-card rounded-lg border p-4 shadow-sm",children:[e.jsxs("div",{children:[e.jsx(P,{className:"text-xs text-muted-foreground",children:"Run with profile"}),e.jsxs("div",{className:"mt-1 flex gap-2",children:[e.jsxs("code",{className:"flex-1 px-2 py-1.5 bg-muted rounded text-xs font-mono truncate",children:["ccs ",s,' "prompt"']}),e.jsx(k,{value:`ccs ${s} "prompt"`,size:"icon",className:"h-6 w-6"})]})]}),e.jsxs("div",{children:[e.jsx(P,{className:"text-xs text-muted-foreground",children:"Set as default"}),e.jsxs("div",{className:"mt-1 flex gap-2",children:[e.jsxs("code",{className:"flex-1 px-2 py-1.5 bg-muted rounded text-xs font-mono truncate",children:["ccs default ",s]}),e.jsx(k,{value:`ccs default ${s}`,size:"icon",className:"h-6 w-6"})]})]})]})]})]})})}function Ne({profileName:s,data:t,currentSettings:n,newEnvKey:c,onNewEnvKeyChange:u,onEnvValueChange:a,onAddEnvVar:i}){return e.jsx("div",{className:"h-full flex flex-col",children:e.jsxs(B,{defaultValue:"env",className:"h-full flex flex-col",children:[e.jsx("div",{className:"px-4 pt-4 shrink-0",children:e.jsxs(Y,{className:"w-full",children:[e.jsx(F,{value:"env",className:"flex-1",children:"Environment Variables"}),e.jsx(F,{value:"info",className:"flex-1",children:"Info & Usage"})]})}),e.jsxs("div",{className:"flex-1 overflow-hidden flex flex-col",children:[e.jsx(M,{value:"env",className:"flex-1 mt-0 border-0 p-0 data-[state=inactive]:hidden flex flex-col overflow-hidden",children:e.jsx(je,{currentSettings:n,newEnvKey:c,onNewEnvKeyChange:u,onEnvValueChange:a,onAddEnvVar:i})}),e.jsx(M,{value:"info",className:"h-full mt-0 border-0 p-0 data-[state=inactive]:hidden",children:e.jsx(ge,{profileName:s,data:t})})]})]})})}function ve({profileName:s,onDelete:t}){const[n,c]=d.useState({}),[u,a]=d.useState(!1),[i,o]=d.useState(null),[g,S]=d.useState(""),p=te(),{data:h,isLoading:N,isError:m,refetch:w}=ae({queryKey:["settings",s],queryFn:async()=>{const l=await fetch(`/api/settings/${s}/raw`);if(!l.ok)throw new Error(`Failed to load settings: ${l.status}`);return l.json()}}),x=h?.settings,v=d.useMemo(()=>{if(i!==null)try{return JSON.parse(i)}catch{}if(x)return{...x,env:{...x.env,...n}}},[x,n,i]),y=d.useMemo(()=>i!==null?i:x?JSON.stringify(x,null,2):"",[i,x]),b=d.useCallback(l=>{o(l)},[]),r=(l,j)=>{const A={...v?.env||{},[l]:j};c(q=>({...q,[l]:j})),o(JSON.stringify({...v,env:A},null,2))},H=()=>{if(!g.trim())return;const l=g.trim(),j={...v?.env||{},[l]:""};c(A=>({...A,[l]:""})),o(JSON.stringify({...v,env:j},null,2)),S("")},R=d.useMemo(()=>{try{return JSON.parse(y),!0}catch{return!1}},[y]),Q=d.useMemo(()=>i!==null?i!==JSON.stringify(x,null,2):Object.keys(n).length>0,[i,n,x]),O=ne({mutationFn:async()=>{let l;try{l=JSON.parse(y)}catch{l={...h?.settings,env:{...h?.settings?.env,...n}}}const j=await fetch(`/api/settings/${s}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({settings:l,expectedMtime:h?.mtime})});if(j.status===409)throw new Error("CONFLICT");if(!j.ok)throw new Error("Failed to save");return j.json()},onSuccess:()=>{p.invalidateQueries({queryKey:["settings",s]}),p.invalidateQueries({queryKey:["profiles"]}),c({}),o(null),D.success("Settings saved")},onError:l=>{l.message==="CONFLICT"?a(!0):D.error(l.message)}}),J=async l=>{a(!1),l?(await w(),O.mutate()):(c({}),o(null))};return e.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden",children:[e.jsx(fe,{profileName:s,data:h,isLoading:N,isSaving:O.isPending,hasChanges:Q,isRawJsonValid:R,onRefresh:()=>w(),onDelete:t,onSave:()=>O.mutate()}),N?e.jsxs("div",{className:"flex-1 flex items-center justify-center",children:[e.jsx(K,{className:"w-8 h-8 animate-spin text-muted-foreground"}),e.jsx("span",{className:"ml-3 text-muted-foreground",children:"Loading settings..."})]}):m?e.jsx("div",{className:"flex-1 flex items-center justify-center",children:e.jsxs("div",{className:"text-center space-y-3",children:[e.jsx("p",{className:"text-sm text-muted-foreground",children:"Failed to load settings."}),e.jsxs(f,{variant:"outline",size:"sm",onClick:()=>w(),children:[e.jsx(T,{className:"w-4 h-4 mr-1"}),"Retry"]})]})}):e.jsxs("div",{className:"flex-1 grid grid-cols-[40%_60%] divide-x overflow-hidden",children:[e.jsx("div",{className:"flex flex-col overflow-hidden bg-muted/5",children:e.jsx(Ne,{profileName:s,data:h,currentSettings:v,newEnvKey:g,onNewEnvKeyChange:S,onEnvValueChange:r,onAddEnvVar:H})}),e.jsxs("div",{className:"flex flex-col overflow-hidden",children:[e.jsxs("div",{className:"px-6 py-2 bg-muted/30 border-b flex items-center gap-2 shrink-0 h-[45px]",children:[e.jsx(oe,{className:"w-4 h-4 text-muted-foreground"}),e.jsx("span",{className:"text-sm font-medium text-muted-foreground",children:"Raw Configuration (JSON)"})]}),e.jsx(W,{rawJsonContent:y,isRawJsonValid:R,rawJsonEdits:i,settings:x,onChange:b})]})]}),e.jsx(_,{open:u,title:"File Modified Externally",description:"Overwrite with your changes or discard?",confirmText:"Overwrite",variant:"destructive",onConfirm:()=>J(!0),onCancel:()=>J(!1)})]},s)}function Te(){const{data:s,isLoading:t,isError:n,refetch:c}=X(),u=G(),[a,i]=d.useState(null),[o,g]=d.useState(""),[S,p]=d.useState(!1),[h,N]=d.useState(null),m=d.useMemo(()=>s?.profiles||[],[s?.profiles]),w=d.useMemo(()=>m.filter(r=>r.name.toLowerCase().includes(o.toLowerCase())),[m,o]),x=d.useMemo(()=>a&&m.some(r=>r.name===a)?a:m.length>0?m[0].name:null,[a,m]),v=r=>{u.mutate(r,{onSuccess:()=>{a===r&&i(null),N(null)}})},y=r=>{p(!1),i(r)},b=m.find(r=>r.name===x);return e.jsxs("div",{className:"h-[calc(100vh-100px)] flex",children:[e.jsxs("div",{className:"w-80 border-r flex flex-col bg-muted/30",children:[e.jsxs("div",{className:"p-4 border-b bg-background",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(de,{className:"w-5 h-5 text-primary"}),e.jsx("h1",{className:"font-semibold",children:"API Profiles"})]}),e.jsxs(f,{size:"sm",onClick:()=>{p(!0)},children:[e.jsx(I,{className:"w-4 h-4 mr-1"}),"New"]})]}),e.jsxs("div",{className:"relative",children:[e.jsx(me,{className:"absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground"}),e.jsx(E,{placeholder:"Search profiles...",className:"pl-8 h-9",value:o,onChange:r=>g(r.target.value)})]})]}),e.jsx(L,{className:"flex-1",children:t?e.jsx("div",{className:"p-4 text-sm text-muted-foreground",children:"Loading profiles..."}):n?e.jsx("div",{className:"p-4 text-center",children:e.jsxs("div",{className:"space-y-3 py-8",children:[e.jsx(U,{className:"w-12 h-12 mx-auto text-destructive/50"}),e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium",children:"Failed to load profiles"}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"Unable to fetch API profiles. Please try again."})]}),e.jsxs(f,{size:"sm",variant:"outline",onClick:()=>c(),children:[e.jsx(T,{className:"w-4 h-4 mr-1"}),"Retry"]})]})}):w.length===0?e.jsx("div",{className:"p-4 text-center",children:m.length===0?e.jsxs("div",{className:"space-y-3 py-8",children:[e.jsx(xe,{className:"w-12 h-12 mx-auto text-muted-foreground/50"}),e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium",children:"No API profiles yet"}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"Create your first profile to connect to custom API endpoints"})]}),e.jsxs(f,{size:"sm",variant:"outline",onClick:()=>{p(!0)},children:[e.jsx(I,{className:"w-4 h-4 mr-1"}),"Create Profile"]})]}):e.jsxs("p",{className:"text-sm text-muted-foreground py-4",children:['No profiles match "',o,'"']})}):e.jsx("div",{className:"p-2 space-y-1",children:w.map(r=>e.jsx(we,{profile:r,isSelected:x===r.name,onSelect:()=>{i(r.name)},onDelete:()=>N(r.name)},r.name))})}),m.length>0&&e.jsx("div",{className:"p-3 border-t bg-background text-xs text-muted-foreground",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{children:[m.length," profile",m.length!==1?"s":""]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(V,{className:"w-3 h-3 text-green-600"}),m.filter(r=>r.configured).length," configured"]})]})})]}),e.jsx("div",{className:"flex-1 flex flex-col min-w-0",children:b?e.jsx(ve,{profileName:b.name,onDelete:()=>N(b.name)}):e.jsx(ye,{onCreateClick:()=>{p(!0)}})}),e.jsx(Z,{open:S,onOpenChange:p,onSuccess:y}),e.jsx(_,{open:!!h,title:"Delete Profile",description:`Are you sure you want to delete "${h}"? This will remove the settings file and cannot be undone.`,confirmText:"Delete",variant:"destructive",onConfirm:()=>h&&v(h),onCancel:()=>N(null)})]})}function we({profile:s,isSelected:t,onSelect:n,onDelete:c}){return e.jsxs("div",{className:ee("group flex items-center gap-2 px-3 py-2.5 rounded-md cursor-pointer transition-colors",t?"bg-primary/10 border border-primary/20":"hover:bg-muted border border-transparent"),onClick:n,children:[s.configured?e.jsx(V,{className:"w-4 h-4 text-green-600 shrink-0"}):e.jsx(U,{className:"w-4 h-4 text-yellow-600 shrink-0"}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"font-medium text-sm truncate",children:s.name}),e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx("div",{className:"text-xs text-muted-foreground truncate flex-1",children:s.settingsPath}),e.jsx(k,{value:s.settingsPath,size:"icon",className:"h-5 w-5 opacity-0 group-hover:opacity-100 transition-opacity"})]})]}),e.jsx(f,{variant:"ghost",size:"icon",className:"h-7 w-7 opacity-0 group-hover:opacity-100 transition-opacity",onClick:u=>{u.stopPropagation(),c()},children:e.jsx($,{className:"w-3.5 h-3.5 text-destructive"})})]})}function ye({onCreateClick:s}){return e.jsx("div",{className:"flex-1 flex items-center justify-center bg-muted/20",children:e.jsxs("div",{className:"text-center max-w-md px-8",children:[e.jsx(ue,{className:"w-16 h-16 mx-auto text-muted-foreground/30 mb-6"}),e.jsx("h2",{className:"text-xl font-semibold mb-2",children:"API Profile Manager"}),e.jsx("p",{className:"text-muted-foreground mb-6",children:"Configure custom API endpoints for Claude CLI. Connect to proxy services like copilot-api, OpenRouter, or your own API backend."}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs(f,{onClick:s,className:"w-full",children:[e.jsx(I,{className:"w-4 h-4 mr-2"}),"Create Your First Profile"]}),e.jsx(se,{className:"my-4"}),e.jsxs("div",{className:"text-left space-y-2",children:[e.jsx("p",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wide",children:"What you can configure:"}),e.jsxs("ul",{className:"text-sm text-muted-foreground space-y-1.5",children:[e.jsxs("li",{className:"flex items-start gap-2",children:[e.jsx(C,{variant:"outline",className:"text-xs shrink-0 mt-0.5",children:"URL"}),e.jsx("span",{children:"Custom API base URL endpoint"})]}),e.jsxs("li",{className:"flex items-start gap-2",children:[e.jsx(C,{variant:"outline",className:"text-xs shrink-0 mt-0.5",children:"Auth"}),e.jsx("span",{children:"API key or authentication token"})]}),e.jsxs("li",{className:"flex items-start gap-2",children:[e.jsx(C,{variant:"outline",className:"text-xs shrink-0 mt-0.5",children:"Models"}),e.jsx("span",{children:"Model mapping for Opus/Sonnet/Haiku"})]})]})]}),e.jsx("div",{className:"pt-4",children:e.jsxs("a",{href:"https://github.com/kaitranntt/ccs#api-profiles",target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center text-xs text-primary hover:underline",children:["Learn more about API profiles",e.jsx(he,{className:"w-3 h-3 ml-1"})]})})]})]})})}export{Te as ApiPage};
@@ -1 +0,0 @@
1
- import{j as t}from"./radix-ui-CV3R9pD6.js";import"./react-vendor-FspHSO0w.js";import{c as o}from"./index-C1W0iP_Z.js";function n({className:a,...r}){return t.jsx("div",{"data-slot":"card",className:o("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",a),...r})}function c({className:a,...r}){return t.jsx("div",{"data-slot":"card-header",className:o("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",a),...r})}function i({className:a,...r}){return t.jsx("div",{"data-slot":"card-title",className:o("leading-none font-semibold",a),...r})}function l({className:a,...r}){return t.jsx("div",{"data-slot":"card-content",className:o("px-6",a),...r})}export{n as C,l as a,c as b,i as c};