@kaitranntt/ccs 7.79.1-dev.9 → 8.0.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 (193) hide show
  1. package/dist/bin/codex-runtime-router.d.ts.map +1 -1
  2. package/dist/bin/codex-runtime-router.js +38 -12
  3. package/dist/bin/codex-runtime-router.js.map +1 -1
  4. package/dist/ccs.js +2 -1
  5. package/dist/ccs.js.map +1 -1
  6. package/dist/cliproxy/ai-providers/codex-plan-compatibility.js +2 -2
  7. package/dist/cliproxy/ai-providers/codex-plan-compatibility.js.map +1 -1
  8. package/dist/cliproxy/ai-providers/codex-reasoning-proxy.d.ts +2 -2
  9. package/dist/cliproxy/ai-providers/codex-reasoning-proxy.d.ts.map +1 -1
  10. package/dist/cliproxy/ai-providers/codex-reasoning-proxy.js +16 -8
  11. package/dist/cliproxy/ai-providers/codex-reasoning-proxy.js.map +1 -1
  12. package/dist/cliproxy/ai-providers/model-id-normalizer.d.ts +1 -1
  13. package/dist/cliproxy/ai-providers/model-id-normalizer.d.ts.map +1 -1
  14. package/dist/cliproxy/ai-providers/model-id-normalizer.js +1 -1
  15. package/dist/cliproxy/ai-providers/model-id-normalizer.js.map +1 -1
  16. package/dist/cliproxy/auth/oauth-handler.d.ts +13 -1
  17. package/dist/cliproxy/auth/oauth-handler.d.ts.map +1 -1
  18. package/dist/cliproxy/auth/oauth-handler.js +111 -34
  19. package/dist/cliproxy/auth/oauth-handler.js.map +1 -1
  20. package/dist/cliproxy/auth/oauth-trace/redactor.d.ts.map +1 -1
  21. package/dist/cliproxy/auth/oauth-trace/redactor.js +9 -1
  22. package/dist/cliproxy/auth/oauth-trace/redactor.js.map +1 -1
  23. package/dist/cliproxy/config/generator.d.ts.map +1 -1
  24. package/dist/cliproxy/config/generator.js +5 -2
  25. package/dist/cliproxy/config/generator.js.map +1 -1
  26. package/dist/cliproxy/config/thinking-config.js +6 -6
  27. package/dist/cliproxy/config/thinking-config.js.map +1 -1
  28. package/dist/cliproxy/executor/env-resolver.js +1 -1
  29. package/dist/cliproxy/executor/env-resolver.js.map +1 -1
  30. package/dist/cliproxy/quota/quota-fetcher-claude.d.ts.map +1 -1
  31. package/dist/cliproxy/quota/quota-fetcher-claude.js +35 -4
  32. package/dist/cliproxy/quota/quota-fetcher-claude.js.map +1 -1
  33. package/dist/cliproxy/quota/quota-manager.d.ts +1 -0
  34. package/dist/cliproxy/quota/quota-manager.d.ts.map +1 -1
  35. package/dist/cliproxy/quota/quota-manager.js +53 -2
  36. package/dist/cliproxy/quota/quota-manager.js.map +1 -1
  37. package/dist/cliproxy/services/oauth-usage-log-transformer.d.ts.map +1 -1
  38. package/dist/cliproxy/services/oauth-usage-log-transformer.js +5 -0
  39. package/dist/cliproxy/services/oauth-usage-log-transformer.js.map +1 -1
  40. package/dist/codex-auth/codex-profile-resources.d.ts +6 -0
  41. package/dist/codex-auth/codex-profile-resources.d.ts.map +1 -0
  42. package/dist/codex-auth/codex-profile-resources.js +119 -0
  43. package/dist/codex-auth/codex-profile-resources.js.map +1 -0
  44. package/dist/codex-auth/commands/create-command.d.ts.map +1 -1
  45. package/dist/codex-auth/commands/create-command.js +20 -6
  46. package/dist/codex-auth/commands/create-command.js.map +1 -1
  47. package/dist/codex-auth/index.d.ts +1 -0
  48. package/dist/codex-auth/index.d.ts.map +1 -1
  49. package/dist/codex-auth/index.js +4 -1
  50. package/dist/codex-auth/index.js.map +1 -1
  51. package/dist/commands/cliproxy/variant-subcommand.js +1 -1
  52. package/dist/commands/cliproxy/variant-subcommand.js.map +1 -1
  53. package/dist/commands/command-catalog.d.ts +1 -1
  54. package/dist/commands/command-catalog.d.ts.map +1 -1
  55. package/dist/commands/command-catalog.js +10 -1
  56. package/dist/commands/command-catalog.js.map +1 -1
  57. package/dist/commands/completion-backend.d.ts.map +1 -1
  58. package/dist/commands/completion-backend.js +2 -0
  59. package/dist/commands/completion-backend.js.map +1 -1
  60. package/dist/commands/config-command.d.ts +2 -0
  61. package/dist/commands/config-command.d.ts.map +1 -1
  62. package/dist/commands/config-command.js +31 -20
  63. package/dist/commands/config-command.js.map +1 -1
  64. package/dist/commands/docker/finalize-key-rotation-subcommand.d.ts +2 -0
  65. package/dist/commands/docker/finalize-key-rotation-subcommand.d.ts.map +1 -0
  66. package/dist/commands/docker/finalize-key-rotation-subcommand.js +79 -0
  67. package/dist/commands/docker/finalize-key-rotation-subcommand.js.map +1 -0
  68. package/dist/commands/docker/help-subcommand.d.ts.map +1 -1
  69. package/dist/commands/docker/help-subcommand.js +6 -0
  70. package/dist/commands/docker/help-subcommand.js.map +1 -1
  71. package/dist/commands/docker/index.d.ts.map +1 -1
  72. package/dist/commands/docker/index.js +4 -0
  73. package/dist/commands/docker/index.js.map +1 -1
  74. package/dist/commands/docker/show-key-subcommand.d.ts +2 -0
  75. package/dist/commands/docker/show-key-subcommand.d.ts.map +1 -0
  76. package/dist/commands/docker/show-key-subcommand.js +96 -0
  77. package/dist/commands/docker/show-key-subcommand.js.map +1 -0
  78. package/dist/commands/docker/up-subcommand.d.ts.map +1 -1
  79. package/dist/commands/docker/up-subcommand.js +4 -0
  80. package/dist/commands/docker/up-subcommand.js.map +1 -1
  81. package/dist/commands/persist-command.d.ts.map +1 -1
  82. package/dist/commands/persist-command.js +77 -5
  83. package/dist/commands/persist-command.js.map +1 -1
  84. package/dist/docker/docker-bootstrap.d.ts +1 -1
  85. package/dist/docker/docker-bootstrap.d.ts.map +1 -1
  86. package/dist/docker/docker-bootstrap.js +99 -3
  87. package/dist/docker/docker-bootstrap.js.map +1 -1
  88. package/dist/docker/docker-executor.d.ts +3 -0
  89. package/dist/docker/docker-executor.d.ts.map +1 -1
  90. package/dist/docker/docker-executor.js +53 -0
  91. package/dist/docker/docker-executor.js.map +1 -1
  92. package/dist/docker/docker-key-rotation.d.ts +48 -0
  93. package/dist/docker/docker-key-rotation.d.ts.map +1 -0
  94. package/dist/docker/docker-key-rotation.js +179 -0
  95. package/dist/docker/docker-key-rotation.js.map +1 -0
  96. package/dist/management/checks/config-check.d.ts.map +1 -1
  97. package/dist/management/checks/config-check.js +10 -1
  98. package/dist/management/checks/config-check.js.map +1 -1
  99. package/dist/management/doctor.d.ts +1 -0
  100. package/dist/management/doctor.d.ts.map +1 -1
  101. package/dist/management/doctor.js +23 -0
  102. package/dist/management/doctor.js.map +1 -1
  103. package/dist/proxy/proxy-daemon.d.ts.map +1 -1
  104. package/dist/proxy/proxy-daemon.js +15 -3
  105. package/dist/proxy/proxy-daemon.js.map +1 -1
  106. package/dist/proxy/server/messages-route.d.ts.map +1 -1
  107. package/dist/proxy/server/messages-route.js +27 -1
  108. package/dist/proxy/server/messages-route.js.map +1 -1
  109. package/dist/shared/claude-extension-setup.d.ts.map +1 -1
  110. package/dist/shared/claude-extension-setup.js +21 -1
  111. package/dist/shared/claude-extension-setup.js.map +1 -1
  112. package/dist/shared/stale-codex-translator-settings.d.ts +4 -0
  113. package/dist/shared/stale-codex-translator-settings.d.ts.map +1 -0
  114. package/dist/shared/stale-codex-translator-settings.js +35 -0
  115. package/dist/shared/stale-codex-translator-settings.js.map +1 -0
  116. package/dist/targets/codex-adapter.d.ts.map +1 -1
  117. package/dist/targets/codex-adapter.js +64 -5
  118. package/dist/targets/codex-adapter.js.map +1 -1
  119. package/dist/ui/assets/{Trans-J6qQqjkU.js → Trans-dEd8UC39.js} +1 -1
  120. package/dist/ui/assets/{accounts-B9Y9uOFl.js → accounts-B2THsSu_.js} +1 -1
  121. package/dist/ui/assets/{alert-dialog-B7gsksZn.js → alert-dialog-Vurq1xzV.js} +1 -1
  122. package/dist/ui/assets/{api-Cz8U6P7R.js → api-CjLfG9mL.js} +1 -1
  123. package/dist/ui/assets/{auth-section-C8dzvYje.js → auth-section-iGkrMaT4.js} +1 -1
  124. package/dist/ui/assets/{backups-section-D0dn59k8.js → backups-section-DUGG_4KP.js} +1 -1
  125. package/dist/ui/assets/{channels-BEmwDrRW.js → channels-CimlCc2d.js} +1 -1
  126. package/dist/ui/assets/{checkbox-DD0s2D_O.js → checkbox-HiMHasEB.js} +1 -1
  127. package/dist/ui/assets/claude-extension-B2v9bsN9.js +1 -0
  128. package/dist/ui/assets/{cliproxy-ai-providers-B440rPlH.js → cliproxy-ai-providers-2lI5jSHb.js} +6 -6
  129. package/dist/ui/assets/{cliproxy-control-panel-Bo-Y41B_.js → cliproxy-control-panel-63gDI-e5.js} +1 -1
  130. package/dist/ui/assets/cliproxy-fai69Vyd.js +4 -0
  131. package/dist/ui/assets/{codex-DPe04ol4.js → codex-i_hfI4Tv.js} +1 -1
  132. package/dist/ui/assets/{confirm-dialog-MQNmqmha.js → confirm-dialog-B_6SrSxL.js} +1 -1
  133. package/dist/ui/assets/{copilot-COZj29YU.js → copilot-BgG5x_X6.js} +2 -2
  134. package/dist/ui/assets/{cursor-D0occhsH.js → cursor-DF63lFcy.js} +1 -1
  135. package/dist/ui/assets/droid-CqlQmFtN.js +2 -0
  136. package/dist/ui/assets/{globalenv-section-BwuNrdbO.js → globalenv-section-CYNmD12i.js} +1 -1
  137. package/dist/ui/assets/{health-DjNOO9K-.js → health-7ZO5PEm9.js} +1 -1
  138. package/dist/ui/assets/{index-Bnzie_zG.js → index-BC5ssPfF.js} +1 -1
  139. package/dist/ui/assets/index-BMoGhs28.css +1 -0
  140. package/dist/ui/assets/{index-i2MPNni3.js → index-Bgcd4FYK.js} +1 -1
  141. package/dist/ui/assets/{index-CmOBelFi.js → index-CFu0saCM.js} +1 -1
  142. package/dist/ui/assets/index-CqsmdQoA.js +1 -0
  143. package/dist/ui/assets/{index-CjM3Ehb8.js → index-D3kxCuJ7.js} +1 -1
  144. package/dist/ui/assets/{index-CSlgRg0s.js → index-DmV9wJET.js} +3 -3
  145. package/dist/ui/assets/{index-Bm_0r3qk.js → index-hHb9o079.js} +1 -1
  146. package/dist/ui/assets/{logs-7bWvuc1l.js → logs-Dy-yZhIK.js} +1 -1
  147. package/dist/ui/assets/{masked-input-D_amxi96.js → masked-input-BSLeznEs.js} +1 -1
  148. package/dist/ui/assets/{proxy-status-widget-BkC-wCtI.js → proxy-status-widget-hRH1agF9.js} +1 -1
  149. package/dist/ui/assets/{raw-json-settings-editor-panel-CVP5Bjfm.js → raw-json-settings-editor-panel-Bs83yILR.js} +1 -1
  150. package/dist/ui/assets/{searchable-select-e5sY7o6g.js → searchable-select-pXUC_0Ih.js} +1 -1
  151. package/dist/ui/assets/{separator-BR5bmtI7.js → separator-FZR_61I9.js} +1 -1
  152. package/dist/ui/assets/{shared-CsqW2cFN.js → shared-BiIwM7--.js} +1 -1
  153. package/dist/ui/assets/{table-DPheAd4c.js → table-_9IusQ8G.js} +1 -1
  154. package/dist/ui/assets/{updates-D07RjKLJ.js → updates-ButNfJqt.js} +1 -1
  155. package/dist/ui/assets/{use-accounts-CWVR2NXQ.js → use-accounts-BdtI0lBv.js} +1 -1
  156. package/dist/ui/index.html +2 -2
  157. package/dist/utils/claude-subcommand-detector.d.ts +7 -0
  158. package/dist/utils/claude-subcommand-detector.d.ts.map +1 -1
  159. package/dist/utils/claude-subcommand-detector.js +52 -9
  160. package/dist/utils/claude-subcommand-detector.js.map +1 -1
  161. package/dist/web-server/routes/cliproxy-stats-routes.d.ts +8 -0
  162. package/dist/web-server/routes/cliproxy-stats-routes.d.ts.map +1 -1
  163. package/dist/web-server/routes/cliproxy-stats-routes.js +13 -26
  164. package/dist/web-server/routes/cliproxy-stats-routes.js.map +1 -1
  165. package/dist/web-server/services/cliproxy-dashboard-restart-service.d.ts +21 -0
  166. package/dist/web-server/services/cliproxy-dashboard-restart-service.d.ts.map +1 -0
  167. package/dist/web-server/services/cliproxy-dashboard-restart-service.js +49 -0
  168. package/dist/web-server/services/cliproxy-dashboard-restart-service.js.map +1 -0
  169. package/dist/web-server/services/codex-dashboard-service.d.ts.map +1 -1
  170. package/dist/web-server/services/codex-dashboard-service.js +30 -3
  171. package/dist/web-server/services/codex-dashboard-service.js.map +1 -1
  172. package/dist/web-server/usage/aggregator.d.ts +13 -7
  173. package/dist/web-server/usage/aggregator.d.ts.map +1 -1
  174. package/dist/web-server/usage/aggregator.js +37 -22
  175. package/dist/web-server/usage/aggregator.js.map +1 -1
  176. package/dist/web-server/usage/handlers.d.ts +1 -0
  177. package/dist/web-server/usage/handlers.d.ts.map +1 -1
  178. package/dist/web-server/usage/handlers.js +16 -8
  179. package/dist/web-server/usage/handlers.js.map +1 -1
  180. package/dist/web-server/usage/profile-filter.d.ts +13 -0
  181. package/dist/web-server/usage/profile-filter.d.ts.map +1 -0
  182. package/dist/web-server/usage/profile-filter.js +30 -0
  183. package/dist/web-server/usage/profile-filter.js.map +1 -0
  184. package/dist/web-server/usage/types.d.ts +8 -0
  185. package/dist/web-server/usage/types.d.ts.map +1 -1
  186. package/lib/mcp/ccs-browser-server.cjs +217 -12
  187. package/package.json +1 -1
  188. package/scripts/docker-dashboard-sunset-guard.js +171 -0
  189. package/dist/ui/assets/claude-extension-DHVBsw6b.js +0 -1
  190. package/dist/ui/assets/cliproxy-BMlbpAIO.js +0 -4
  191. package/dist/ui/assets/droid-CSaYFApd.js +0 -2
  192. package/dist/ui/assets/index-D9HfyuQr.js +0 -1
  193. package/dist/ui/assets/index-G3mgTfgR.css +0 -1
@@ -83,6 +83,16 @@ const TOOL_HOVER = 'browser_hover';
83
83
  const TOOL_QUERY = 'browser_query';
84
84
  const TOOL_TAKE_ELEMENT_SCREENSHOT = 'browser_take_element_screenshot';
85
85
  const TOOL_WAIT_FOR_EVENT = 'browser_wait_for_event';
86
+ const SENSITIVE_INTERCEPT_HEADER_NAMES = new Set([
87
+ 'authorization',
88
+ 'cookie',
89
+ 'cookie2',
90
+ 'proxy-authorization',
91
+ 'x-api-key',
92
+ 'x-api-token',
93
+ 'x-auth-token',
94
+ ]);
95
+
86
96
  const TOOL_NAMES = [
87
97
  TOOL_SESSION_INFO,
88
98
  TOOL_URL_TITLE,
@@ -612,10 +622,15 @@ function getTools() {
612
622
  urlRegex: { type: 'string' },
613
623
  headerMatchers: {
614
624
  type: 'array',
625
+ description:
626
+ 'Match non-sensitive request headers. Cookie, Authorization, and token headers are not allowed.',
615
627
  items: {
616
628
  type: 'object',
617
629
  properties: {
618
- name: { type: 'string' },
630
+ name: {
631
+ type: 'string',
632
+ description: 'Non-sensitive request header name to match.',
633
+ },
619
634
  valueIncludes: { type: 'string' },
620
635
  valueRegex: { type: 'string' },
621
636
  },
@@ -624,7 +639,7 @@ function getTools() {
624
639
  },
625
640
  },
626
641
  priority: { type: 'integer' },
627
- action: { type: 'string', enum: ['continue', 'fail', 'fulfill'] },
642
+ action: { type: 'string', enum: getInterceptActionEnum() },
628
643
  statusCode: { type: 'integer', minimum: 100, maximum: 599 },
629
644
  responseHeaders: {
630
645
  type: 'array',
@@ -1194,7 +1209,18 @@ function getTools() {
1194
1209
  },
1195
1210
  event: {
1196
1211
  type: 'object',
1197
- description: 'Required event selector.',
1212
+ description:
1213
+ 'Required event selector. request events require urlIncludes; download events require urlIncludes or suggestedFilenameIncludes. URLs in returned details are redacted.',
1214
+ properties: {
1215
+ kind: { type: 'string', enum: ['dialog', 'navigation', 'request', 'download'] },
1216
+ dialogType: { type: 'string' },
1217
+ messageIncludes: { type: 'string' },
1218
+ urlIncludes: { type: 'string' },
1219
+ method: { type: 'string' },
1220
+ suggestedFilenameIncludes: { type: 'string' },
1221
+ },
1222
+ required: ['kind'],
1223
+ additionalProperties: false,
1198
1224
  },
1199
1225
  },
1200
1226
  required: ['event'],
@@ -1277,9 +1303,30 @@ function parseOptionalPageId(toolArgs) {
1277
1303
  : '';
1278
1304
  }
1279
1305
 
1306
+ function getBrowserInterceptFulfillMode() {
1307
+ return String(process.env.CCS_BROWSER_INTERCEPT_FULFILL_MODE || 'disabled').trim() === 'enabled'
1308
+ ? 'enabled'
1309
+ : 'disabled';
1310
+ }
1311
+
1312
+ function isBrowserInterceptFulfillEnabled() {
1313
+ return getBrowserInterceptFulfillMode() === 'enabled';
1314
+ }
1315
+
1316
+ function getInterceptActionEnum() {
1317
+ return isBrowserInterceptFulfillEnabled()
1318
+ ? ['continue', 'fail', 'fulfill']
1319
+ : ['continue', 'fail'];
1320
+ }
1321
+
1280
1322
  function parseInterceptAction(value) {
1323
+ if (value === 'fulfill' && !isBrowserInterceptFulfillEnabled()) {
1324
+ throw new Error(
1325
+ 'action fulfill is disabled by CCS_BROWSER_INTERCEPT_FULFILL_MODE=disabled; set it to enabled only for trusted local testing'
1326
+ );
1327
+ }
1281
1328
  if (value !== 'continue' && value !== 'fail' && value !== 'fulfill') {
1282
- throw new Error('action must be one of: continue, fail, fulfill');
1329
+ throw new Error(`action must be one of: ${getInterceptActionEnum().join(', ')}`);
1283
1330
  }
1284
1331
  return value;
1285
1332
  }
@@ -1379,6 +1426,10 @@ function parseOptionalPriority(value) {
1379
1426
  return value;
1380
1427
  }
1381
1428
 
1429
+ function isSensitiveInterceptHeaderName(name) {
1430
+ return SENSITIVE_INTERCEPT_HEADER_NAMES.has(name.toLowerCase());
1431
+ }
1432
+
1382
1433
  function parseOptionalHeaderMatchers(value) {
1383
1434
  if (value === undefined) {
1384
1435
  return [];
@@ -1391,6 +1442,9 @@ function parseOptionalHeaderMatchers(value) {
1391
1442
  throw new Error('headerMatchers entries must be objects');
1392
1443
  }
1393
1444
  const name = requireNonEmptyString(entry.name, 'headerMatchers.name');
1445
+ if (isSensitiveInterceptHeaderName(name)) {
1446
+ throw new Error(`headerMatchers.name cannot target sensitive request header: ${name}`);
1447
+ }
1394
1448
  const valueIncludes =
1395
1449
  entry.valueIncludes === undefined
1396
1450
  ? ''
@@ -3305,24 +3359,80 @@ function parseEventCondition(value) {
3305
3359
  };
3306
3360
  }
3307
3361
  if (value.kind === 'request') {
3362
+ const urlIncludes = value.urlIncludes ? String(value.urlIncludes) : undefined;
3363
+ if (!urlIncludes) {
3364
+ throw new Error('request events require urlIncludes to limit network metadata exposure');
3365
+ }
3308
3366
  return {
3309
3367
  kind: 'request',
3310
- urlIncludes: value.urlIncludes ? String(value.urlIncludes) : undefined,
3368
+ urlIncludes,
3311
3369
  method: value.method ? String(value.method) : undefined,
3312
3370
  };
3313
3371
  }
3314
3372
  if (value.kind === 'download') {
3373
+ const urlIncludes = value.urlIncludes ? String(value.urlIncludes) : undefined;
3374
+ const suggestedFilenameIncludes = value.suggestedFilenameIncludes
3375
+ ? String(value.suggestedFilenameIncludes)
3376
+ : undefined;
3377
+ if (!urlIncludes && !suggestedFilenameIncludes) {
3378
+ throw new Error(
3379
+ 'download events require urlIncludes or suggestedFilenameIncludes to limit metadata exposure'
3380
+ );
3381
+ }
3315
3382
  return {
3316
3383
  kind: 'download',
3317
- urlIncludes: value.urlIncludes ? String(value.urlIncludes) : undefined,
3318
- suggestedFilenameIncludes: value.suggestedFilenameIncludes
3319
- ? String(value.suggestedFilenameIncludes)
3320
- : undefined,
3384
+ urlIncludes,
3385
+ suggestedFilenameIncludes,
3321
3386
  };
3322
3387
  }
3323
3388
  throw new Error(`unknown event kind: ${String(value.kind || '')}`);
3324
3389
  }
3325
3390
 
3391
+ function redactUrlForModel(value, options = {}) {
3392
+ const rawUrl = String(value || '');
3393
+ if (!rawUrl) {
3394
+ return '';
3395
+ }
3396
+ try {
3397
+ const url = new URL(rawUrl);
3398
+ if (options.originOnly) {
3399
+ return url.origin;
3400
+ }
3401
+ url.username = '';
3402
+ url.password = '';
3403
+ url.search = '';
3404
+ url.hash = '';
3405
+ return url.toString();
3406
+ } catch {
3407
+ return '[redacted-url]';
3408
+ }
3409
+ }
3410
+
3411
+ function redactObservedEvent(event, observed) {
3412
+ if (!observed || typeof observed !== 'object') {
3413
+ return observed;
3414
+ }
3415
+ if (event.kind === 'request') {
3416
+ return {
3417
+ url: redactUrlForModel(observed.url, { originOnly: true }),
3418
+ method: observed.method || '',
3419
+ };
3420
+ }
3421
+ if (event.kind === 'download') {
3422
+ return {
3423
+ url: redactUrlForModel(observed.url, { originOnly: true }),
3424
+ suggestedFilename: observed.suggestedFilename || '',
3425
+ };
3426
+ }
3427
+ if (event.kind === 'navigation' && Object.prototype.hasOwnProperty.call(observed, 'url')) {
3428
+ return {
3429
+ ...observed,
3430
+ url: redactUrlForModel(observed.url, { originOnly: true }),
3431
+ };
3432
+ }
3433
+ return observed;
3434
+ }
3435
+
3326
3436
  function matchesObservedEvent(event, observed) {
3327
3437
  if (event.kind === 'dialog') {
3328
3438
  return (
@@ -4776,7 +4886,8 @@ async function handleWaitForEvent(toolArgs) {
4776
4886
  );
4777
4887
  const event = parseEventCondition(toolArgs.event);
4778
4888
  const observed = await waitForMatchingEvent({ page, pageIndex, timeoutMs, event });
4779
- return `pageIndex: ${pageIndex}\nevent: ${event.kind}\nstatus: observed\ndetail: ${JSON.stringify(observed)}`;
4889
+ const safeObserved = redactObservedEvent(event, observed);
4890
+ return `pageIndex: ${pageIndex}\nevent: ${event.kind}\nstatus: observed\ndetail: ${JSON.stringify(safeObserved)}`;
4780
4891
  }
4781
4892
 
4782
4893
  function findInterceptRuleIndex(ruleId) {
@@ -5381,7 +5492,10 @@ function buildRecordingInstallExpression(recordingPayload) {
5381
5492
  const recordingPayload = JSON.parse(${JSON.stringify(JSON.stringify(recordingPayload))});
5382
5493
  const existing = globalThis.__CCS_BROWSER_RECORDING_RECORDER__;
5383
5494
  if (existing && existing.installed === true) {
5384
- return { installed: true };
5495
+ if (typeof existing.teardown === 'function') {
5496
+ existing.teardown();
5497
+ }
5498
+ delete globalThis.__CCS_BROWSER_RECORDING_RECORDER__;
5385
5499
  }
5386
5500
 
5387
5501
  const events = Array.isArray(recordingPayload.events) ? [...recordingPayload.events] : [];
@@ -5413,8 +5527,42 @@ function buildRecordingInstallExpression(recordingPayload) {
5413
5527
  timestamp: Date.now(),
5414
5528
  });
5415
5529
  };
5530
+ const sensitiveInputTypes = new Set(['password', 'hidden']);
5531
+ const sensitiveAttributePattern = /(?:pass(?:word|code|phrase)?|pwd|secret|token|api[-_ ]?key|access[-_ ]?key|private[-_ ]?key|credential|otp|one[-_ ]?time[-_ ]?(?:code|password)|verif(?:ication)?[-_ ]?code|security[-_ ]?code|pin|auth(?:orization)?[-_ ]?(?:code|token)|mfa|2fa)/i;
5532
+ const sensitiveAutocompleteValues = new Set([
5533
+ 'current-password',
5534
+ 'new-password',
5535
+ 'one-time-code',
5536
+ 'cc-number',
5537
+ 'cc-csc',
5538
+ ]);
5539
+ const isSensitiveTextTarget = (target) => {
5540
+ if (!target || !(target instanceof Element)) {
5541
+ return false;
5542
+ }
5543
+ if (target instanceof HTMLInputElement) {
5544
+ const inputType = String(target.type || '').toLowerCase();
5545
+ if (sensitiveInputTypes.has(inputType)) {
5546
+ return true;
5547
+ }
5548
+ }
5549
+ const autocompleteTokens = String(target.getAttribute('autocomplete') || '')
5550
+ .toLowerCase()
5551
+ .split(/\s+/)
5552
+ .filter(Boolean);
5553
+ if (autocompleteTokens.some((token) => sensitiveAutocompleteValues.has(token))) {
5554
+ return true;
5555
+ }
5556
+ const attributesToInspect = ['id', 'name', 'placeholder', 'aria-label', 'data-testid'];
5557
+ return attributesToInspect.some((attributeName) =>
5558
+ sensitiveAttributePattern.test(String(target.getAttribute(attributeName) || ''))
5559
+ );
5560
+ };
5416
5561
  const onInput = (event) => {
5417
5562
  const target = event.target;
5563
+ if (isSensitiveTextTarget(target)) {
5564
+ return;
5565
+ }
5418
5566
  let text = '';
5419
5567
  if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
5420
5568
  text = target.value;
@@ -5425,7 +5573,19 @@ function buildRecordingInstallExpression(recordingPayload) {
5425
5573
  }
5426
5574
  pushEvent({ kind: 'type', selector: getSelector(target), text, timestamp: Date.now() });
5427
5575
  };
5576
+ const isRecordableKey = (event) => {
5577
+ if (isSensitiveTextTarget(event.target)) {
5578
+ return false;
5579
+ }
5580
+ if (typeof event.key !== 'string' || event.key.length !== 1) {
5581
+ return true;
5582
+ }
5583
+ return event.altKey === true || event.ctrlKey === true || event.metaKey === true;
5584
+ };
5428
5585
  const onKeyDown = (event) => {
5586
+ if (!isRecordableKey(event)) {
5587
+ return;
5588
+ }
5429
5589
  const modifiers = [];
5430
5590
  if (event.altKey) modifiers.push('Alt');
5431
5591
  if (event.ctrlKey) modifiers.push('Control');
@@ -5493,7 +5653,19 @@ async function finalizeRecordingCapture(session) {
5493
5653
  };
5494
5654
 
5495
5655
  const response = await sendCdpCommand(page, 'Runtime.evaluate', {
5496
- expression: `(() => globalThis.__CCS_BROWSER_RECORDING_RECORDER__ || { events: [], warnings: [] })()`,
5656
+ expression: `(() => {
5657
+ const recorder = globalThis.__CCS_BROWSER_RECORDING_RECORDER__;
5658
+ if (!recorder) {
5659
+ return { events: [], warnings: [] };
5660
+ }
5661
+ const events = Array.isArray(recorder.events) ? [...recorder.events] : [];
5662
+ const warnings = Array.isArray(recorder.warnings) ? [...recorder.warnings] : [];
5663
+ if (typeof recorder.teardown === 'function') {
5664
+ recorder.teardown();
5665
+ }
5666
+ delete globalThis.__CCS_BROWSER_RECORDING_RECORDER__;
5667
+ return { events, warnings };
5668
+ })()`,
5497
5669
  returnByValue: true,
5498
5670
  awaitPromise: true,
5499
5671
  });
@@ -5513,6 +5685,28 @@ async function finalizeRecordingCapture(session) {
5513
5685
  session.warnings = warnings.map((warning) => String(warning.message || warning));
5514
5686
  }
5515
5687
 
5688
+ async function teardownRecordingCapture(session) {
5689
+ if (!session || !session.pageWebSocketDebuggerUrl) {
5690
+ return;
5691
+ }
5692
+ const page = {
5693
+ id: session.pageId,
5694
+ webSocketDebuggerUrl: session.pageWebSocketDebuggerUrl,
5695
+ };
5696
+ await sendCdpCommand(page, 'Runtime.evaluate', {
5697
+ expression: `(() => {
5698
+ const recorder = globalThis.__CCS_BROWSER_RECORDING_RECORDER__;
5699
+ if (recorder && typeof recorder.teardown === 'function') {
5700
+ recorder.teardown();
5701
+ }
5702
+ delete globalThis.__CCS_BROWSER_RECORDING_RECORDER__;
5703
+ return { installed: false };
5704
+ })()`,
5705
+ returnByValue: true,
5706
+ awaitPromise: true,
5707
+ });
5708
+ }
5709
+
5516
5710
  async function handleStartRecording(toolArgs) {
5517
5711
  if (activeRecordingSession) {
5518
5712
  throw new Error('recording already active');
@@ -5563,6 +5757,11 @@ async function handleStopRecording() {
5563
5757
  } catch (error) {
5564
5758
  finalizeError = error instanceof Error ? error : new Error(String(error));
5565
5759
  session.warnings.push(`recording capture finalization failed: ${finalizeError.message}`);
5760
+ try {
5761
+ await teardownRecordingCapture(session);
5762
+ } catch {
5763
+ // Preserve the original finalization failure for the caller.
5764
+ }
5566
5765
  }
5567
5766
  session.status = 'stopped';
5568
5767
  session.stoppedAt = new Date().toISOString();
@@ -5583,6 +5782,12 @@ async function handleClearRecording() {
5583
5782
  if (!latestRecordingSession && !activeRecordingSession) {
5584
5783
  throw new Error('no recording available');
5585
5784
  }
5785
+ const session = activeRecordingSession || latestRecordingSession;
5786
+ try {
5787
+ await teardownRecordingCapture(session);
5788
+ } catch {
5789
+ // Clearing session-local recording state should still succeed if the page is already gone.
5790
+ }
5586
5791
  activeRecordingSession = null;
5587
5792
  latestRecordingSession = null;
5588
5793
  return 'status: cleared';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaitranntt/ccs",
3
- "version": "7.79.1-dev.9",
3
+ "version": "8.0.0",
4
4
  "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more",
5
5
  "keywords": [
6
6
  "cli",
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const { execFileSync } = require('node:child_process');
6
+
7
+ function parseStableVersion(value) {
8
+ const match = String(value || '').match(/^v?([0-9]+)\.([0-9]+)\.([0-9]+)$/);
9
+ if (!match) {
10
+ return null;
11
+ }
12
+ return {
13
+ major: Number(match[1]),
14
+ minor: Number(match[2]),
15
+ patch: Number(match[3]),
16
+ raw: `v${Number(match[1])}.${Number(match[2])}.${Number(match[3])}`,
17
+ };
18
+ }
19
+
20
+ function compareVersions(left, right) {
21
+ for (const key of ['major', 'minor', 'patch']) {
22
+ if (left[key] !== right[key]) {
23
+ return left[key] - right[key];
24
+ }
25
+ }
26
+ return 0;
27
+ }
28
+
29
+ function stableVersionKey(version) {
30
+ return `${version.major}.${version.minor}.${version.patch}`;
31
+ }
32
+
33
+ function parseArgs(argv) {
34
+ const args = {};
35
+ for (let i = 0; i < argv.length; i += 1) {
36
+ const arg = argv[i];
37
+ if (!arg.startsWith('--')) {
38
+ throw new Error(`Unexpected argument: ${arg}`);
39
+ }
40
+ const key = arg.slice(2);
41
+ if (key === 'tags-stdin') {
42
+ args.tagsStdin = true;
43
+ continue;
44
+ }
45
+ const value = argv[i + 1];
46
+ if (!value || value.startsWith('--')) {
47
+ throw new Error(`Missing value for --${key}`);
48
+ }
49
+ args[key] = value;
50
+ i += 1;
51
+ }
52
+ return args;
53
+ }
54
+
55
+ function readStableTagsFromGit() {
56
+ const output = execFileSync('git', ['tag', '--list', 'v*'], {
57
+ encoding: 'utf8',
58
+ stdio: ['ignore', 'pipe', 'pipe'],
59
+ });
60
+ return output.split(/\r?\n/).filter(Boolean);
61
+ }
62
+
63
+ function parseStableTags(tags) {
64
+ const byKey = new Map();
65
+ for (const tag of tags) {
66
+ const version = parseStableVersion(tag.trim());
67
+ if (version) {
68
+ byKey.set(stableVersionKey(version), version);
69
+ }
70
+ }
71
+ return Array.from(byKey.values()).sort(compareVersions);
72
+ }
73
+
74
+ function evaluateDashboardSunset({ targetTag, baselineVersion, releaseWindow, stableTags }) {
75
+ const target = parseStableVersion(targetTag);
76
+ if (!target) {
77
+ throw new Error(`Target tag must be stable semver like v1.2.3, got: ${targetTag}`);
78
+ }
79
+
80
+ const baseline = parseStableVersion(baselineVersion);
81
+ if (!baseline) {
82
+ throw new Error(`DEPRECATION_BASELINE_VERSION must be stable semver, got: ${baselineVersion}`);
83
+ }
84
+
85
+ if (!Number.isInteger(releaseWindow) || releaseWindow < 1) {
86
+ throw new Error(`STABLE_RELEASE_WINDOW must be a positive integer, got: ${releaseWindow}`);
87
+ }
88
+
89
+ if (compareVersions(target, baseline) < 0) {
90
+ return {
91
+ publish: true,
92
+ elapsed: 0,
93
+ reason: `${target.raw} is before dashboard deprecation baseline ${baseline.raw}`,
94
+ };
95
+ }
96
+
97
+ const versions = parseStableTags(stableTags);
98
+ const hasBaseline = versions.some((version) => compareVersions(version, baseline) === 0);
99
+ if (compareVersions(target, baseline) > 0 && !hasBaseline) {
100
+ throw new Error(
101
+ `Cannot count dashboard sunset releases: baseline tag ${baseline.raw} is missing from git tags`,
102
+ );
103
+ }
104
+
105
+ if (!versions.some((version) => compareVersions(version, target) === 0)) {
106
+ versions.push(target);
107
+ versions.sort(compareVersions);
108
+ }
109
+
110
+ const elapsed = versions.filter(
111
+ (version) => compareVersions(version, baseline) > 0 && compareVersions(version, target) <= 0,
112
+ ).length;
113
+ const publish = elapsed < releaseWindow;
114
+ const reason = publish
115
+ ? `legacy dashboard publish is still inside sunset window (${elapsed}/${releaseWindow} stable releases elapsed since ${baseline.raw})`
116
+ : `legacy dashboard sunset reached (${elapsed}/${releaseWindow} stable releases elapsed since ${baseline.raw})`;
117
+
118
+ return { publish, elapsed, reason };
119
+ }
120
+
121
+ function appendGithubOutput(values) {
122
+ if (!process.env.GITHUB_OUTPUT) {
123
+ return;
124
+ }
125
+ const lines = Object.entries(values).map(([key, value]) => `${key}=${String(value)}`);
126
+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `${lines.join('\n')}\n`);
127
+ }
128
+
129
+ function main() {
130
+ const args = parseArgs(process.argv.slice(2));
131
+ const targetTag = args.target || process.env.TARGET_TAG;
132
+ const baselineVersion = args.baseline || process.env.DEPRECATION_BASELINE_VERSION;
133
+ const windowValue = args.window || process.env.STABLE_RELEASE_WINDOW || '2';
134
+ const releaseWindow = Number(windowValue);
135
+ const stableTags = args.tagsStdin
136
+ ? fs.readFileSync(0, 'utf8').split(/\r?\n/).filter(Boolean)
137
+ : readStableTagsFromGit();
138
+
139
+ const result = evaluateDashboardSunset({
140
+ targetTag,
141
+ baselineVersion,
142
+ releaseWindow,
143
+ stableTags,
144
+ });
145
+
146
+ appendGithubOutput({
147
+ publish: result.publish ? 'true' : 'false',
148
+ elapsed: result.elapsed,
149
+ reason: result.reason,
150
+ });
151
+
152
+ const status = result.publish ? '[OK]' : '[i]';
153
+ console.log(`${status} ${result.reason}`);
154
+ if (!result.publish) {
155
+ console.log('[i] Skipping ghcr.io/kaitranntt/ccs-dashboard publish; use ghcr.io/kaitranntt/ccs.');
156
+ }
157
+ }
158
+
159
+ if (require.main === module) {
160
+ try {
161
+ main();
162
+ } catch (error) {
163
+ console.error(`[X] ${error.message}`);
164
+ process.exit(1);
165
+ }
166
+ }
167
+
168
+ module.exports = {
169
+ evaluateDashboardSunset,
170
+ parseStableVersion,
171
+ };
@@ -1 +0,0 @@
1
- import{j as e}from"./radix-ui-nu4wz29m.js";import{r as W}from"./react-vendor-Bjx91N7w.js";import{a as q,bf as Ce,B as f,d as P,n as te,C as x,j as p,k as v,_ as j,b as g,I as G,r as ne,s as ie,t as le,v as de,w as re,x as $,Q as we,R as Ve,U as oe,V as ce,c as fe,W as ke}from"./index-CSlgRg0s.js";import{u as J,a as K,b as M}from"./tanstack-bhJxV1h8.js";import{t as C}from"./notifications-B4_o8bcr.js";import{aa as De,a9 as Be,L as Q,au as Ie,at as Te,R as Re,T as ue,ai as Ae,a7 as qe}from"./icons-DzKUh8vG.js";import"./utils-CzKF5WmX.js";import"./form-utils-CuHzLhJZ.js";import"./charts-eIPy2oG6.js";const U=["claude-extension-bindings"];async function w(s,n){const t=await fetch(Ce(s),{headers:{"Content-Type":"application/json"},...n});if(!t.ok){const l=await t.json().catch(()=>null);throw new Error(l?.error||`Request failed (${t.status})`)}return t.status===204?void 0:await t.json()}function Ue(){return J({queryKey:["claude-extension-options"],queryFn:()=>w("/claude-extension/profiles")})}function Fe(s,n="vscode"){return J({queryKey:["claude-extension-setup",s,n],enabled:!!s,queryFn:()=>w(`/claude-extension/setup?profile=${encodeURIComponent(s||"")}&host=${encodeURIComponent(n)}`)})}function Le(){return J({queryKey:U,queryFn:()=>w("/claude-extension/bindings")})}function Oe(s){return J({queryKey:["claude-extension-binding-status",s],enabled:!!s,queryFn:()=>w(`/claude-extension/bindings/${encodeURIComponent(s||"")}/verify`)})}function Qe(){const s=K();return M({mutationFn:n=>w("/claude-extension/bindings",{method:"POST",body:JSON.stringify(n)}),onSuccess:()=>{s.invalidateQueries({queryKey:U}),C.success("Binding created")},onError:n=>C.error(n.message)})}function $e(){const s=K(),{t:n}=q();return M({mutationFn:({id:t,binding:l})=>w(`/claude-extension/bindings/${encodeURIComponent(t)}`,{method:"PUT",body:JSON.stringify(l)}),onSuccess:(t,l)=>{s.invalidateQueries({queryKey:U}),s.invalidateQueries({queryKey:["claude-extension-binding-status",l.id]}),C.success(n("claudeExtensionPage.bindingSaved"))},onError:t=>C.error(t.message)})}function Je(){const s=K(),{t:n}=q();return M({mutationFn:t=>w(`/claude-extension/bindings/${encodeURIComponent(t)}`,{method:"DELETE"}),onSuccess:()=>{s.invalidateQueries({queryKey:U}),C.success(n("claudeExtensionPage.bindingDeleted"))},onError:t=>C.error(t.message)})}function pe(s,n){const t=K();return M({mutationFn:({id:l,target:S})=>w(`/claude-extension/bindings/${encodeURIComponent(l)}/${s}`,{method:"POST",body:JSON.stringify({target:S})}),onSuccess:l=>{t.invalidateQueries({queryKey:U}),t.setQueryData(["claude-extension-binding-status",l.bindingId],l),C.success(n)},onError:l=>C.error(l.message)})}function Ke(){const{t:s}=q();return pe("apply",s("claudeExtensionPage.bindingApplied"))}function Me(){const{t:s}=q();return pe("reset",s("claudeExtensionPage.managedValuesRemoved"))}const He=[];function xe(s){return{name:"",profile:s,host:"vscode",ideSettingsPath:"",notes:""}}function T(s){return{name:s.name,profile:s.profile,host:s.host,ideSettingsPath:s.ideSettingsPath||"",notes:s.notes||""}}function _e(s){return{name:s.name.trim(),profile:s.profile.trim(),host:s.host,ideSettingsPath:s.ideSettingsPath.trim()||void 0,notes:s.notes.trim()||void 0}}function ge(s){return s?.state==="applied"}function ze({state:s}){const n=s==="applied"?"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300":s==="drifted"?"border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300":s==="missing"?"border-destructive/30 bg-destructive/10 text-destructive":"border-border bg-muted text-muted-foreground";return e.jsx(f,{variant:"outline",className:n,children:s})}function We(s){return s.replace(/[\\/]/g,"$&​")}function o({label:s,value:n,mono:t=!1,copyValue:l}){const S=typeof l=="string"&&l.trim().length>0;return e.jsxs("div",{className:"grid gap-2 text-sm sm:grid-cols-[112px_minmax(0,1fr)] sm:items-start",children:[e.jsx("span",{className:"text-muted-foreground",children:s}),S?e.jsxs("div",{className:"flex min-w-0 items-start gap-2",children:[e.jsx("div",{className:"min-w-0 flex-1 rounded-md border bg-muted/25 px-3 py-2",children:e.jsx("span",{className:"block text-left font-mono text-xs leading-5 [overflow-wrap:anywhere]",children:We(n)})}),e.jsx($,{value:l,label:`Copy ${s.toLowerCase()}`,className:"shrink-0"})]}):e.jsx("span",{className:fe("text-left sm:text-right",t&&"font-mono text-xs leading-5 [overflow-wrap:anywhere]"),children:n})]})}function me({title:s,description:n,value:t}){return e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsx(p,{className:"pb-3",children:e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx(v,{className:"text-base",children:s}),e.jsx(j,{className:"mt-1",children:n})]}),e.jsx($,{value:t,label:`Copy ${s}`})]})}),e.jsx(g,{children:e.jsx("pre",{className:"max-h-[360px] overflow-auto rounded-lg border bg-muted/30 p-4 text-xs leading-6",children:t})})]})}function he({title:s,description:n,status:t,applyLabel:l,resetLabel:S,onApply:F,onReset:m,disabled:b,busy:V}){return e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsx(p,{className:"pb-3",children:e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx(v,{className:"text-base",children:s}),e.jsx(j,{className:"mt-1",children:n})]}),t?e.jsx(ze,{state:t.state}):null]})}),e.jsxs(g,{className:"space-y-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{label:"Path",value:t?.path||"Save a binding first",mono:!0,copyValue:t?.path}),e.jsx(o,{label:"File",value:t?t.exists?"Present":"Not created yet":"Unavailable"})]}),e.jsx("div",{className:"rounded-lg border bg-muted/25 p-3 text-sm text-muted-foreground",children:t?.message||ke.t("claudeExtensionPage.verifyAfterSaving",{defaultValue:"Verify the binding after saving to inspect the current file state."})}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(P,{size:"sm",className:"flex-1",onClick:F,disabled:b||V,children:[V?e.jsx(Q,{className:"mr-1.5 h-3.5 w-3.5 animate-spin"}):null,l]}),e.jsx(P,{size:"sm",variant:"outline",className:"flex-1",onClick:m,disabled:b||V,children:S})]})]})]})}function Ge({binding:s,isSelected:n,onSelect:t}){return e.jsx("button",{onClick:t,className:fe("w-full rounded-lg border px-3 py-3 text-left transition-colors",n?"border-primary/40 bg-primary/10":"border-border/60 bg-card hover:bg-muted/40"),children:e.jsxs("div",{className:"flex items-start justify-between gap-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"truncate text-sm font-medium",children:s.name}),e.jsxs("div",{className:"mt-1 text-xs text-muted-foreground",children:[s.profile," · ",s.host]})]}),e.jsx(f,{variant:"outline",className:"shrink-0",children:s.usesDefaultIdeSettingsPath?"Default path":"Custom path"})]})})}function ls(){const{t:s}=q(),n=Ue(),t=Le(),l=Qe(),S=$e(),F=Je(),m=Ke(),b=Me(),V=n.data?.profiles??[],Y=n.data?.hosts??[],E=t.data?.bindings??He,ve=V[0]?.name??"default",[je,D]=W.useState(!1),[L,B]=W.useState(null),[X,k]=W.useState(()=>xe("default")),c=je||E.length===0,N=!c&&E.length>0?E.find(a=>a.id===L)??(L?null:E[0]):null,h=N?.id??null,d=c||!N||L?X:T(N),Z=Fe(d.profile,d.host),R=Oe(c?void 0:h||void 0),r=Y.find(a=>a.id===d.host),H=V.find(a=>a.name===d.profile),_=n.error||t.error||Z.error||R.error,be=`${E.length} saved`,ee=l.isPending||S.isPending,Ne=m.isPending&&m.variables?.target==="shared"||b.isPending&&b.variables?.target==="shared",ye=m.isPending&&m.variables?.target==="ide"||b.isPending&&b.variables?.target==="ide",se=d.name.trim().length>0&&d.profile.trim().length>0,i=Z.data,u=R.data,ae=Math.max((i?.env.length??0)-6,0),Pe=i?.env.slice(0,6)??[];function z(){D(!0),B(null),k(xe(ve))}async function Se(){if(!se)return;const a=_e(d);if(!c&&h){const O=await S.mutateAsync({id:h,binding:a});D(!1),B(O.binding.id),k(T(O.binding));return}const y=await l.mutateAsync(a);D(!1),B(y.binding.id),k(T(y.binding))}async function Ee(){if(!h||!N||!window.confirm(s("claudeExtensionPage.deleteBindingConfirm",{name:N.name,defaultValue:'Delete binding "{{name}}"?'})))return;await F.mutateAsync(h);const a=E.filter(y=>y.id!==h);a.length>0?(B(a[0].id),D(!1),k(T(a[0]))):z()}function A(a,y){if(!c&&N&&!L){B(N.id),k({...T(N),[a]:y}),D(!1);return}k(O=>({...O,[a]:y}))}function I(a,y){if(h){if(y==="apply"){m.mutate({id:h,target:a});return}b.mutate({id:h,target:a})}}return e.jsxs("div",{className:"flex h-full min-h-0 overflow-hidden",children:[e.jsxs("div",{className:"flex w-[348px] shrink-0 flex-col border-r bg-muted/30 xl:w-[372px]",children:[e.jsx("div",{className:"border-b bg-background p-4",children:e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"rounded-lg border bg-muted/40 p-2",children:e.jsx(De,{className:"h-5 w-5 text-primary"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"font-semibold",children:s("claudeExtensionPage.title")}),e.jsx("p",{className:"text-xs text-muted-foreground",children:s("claudeExtensionPage.savedBindingsSubtitle",{defaultValue:"Saved IDE bindings for CCS profiles"})})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(f,{variant:"secondary",children:be}),r?e.jsx(f,{variant:"outline",children:r.label}):null]})]}),e.jsxs(P,{size:"sm",onClick:z,className:"gap-1.5",children:[e.jsx(Be,{className:"h-3.5 w-3.5"}),s("claudeExtensionPage.new",{defaultValue:"New"})]})]})}),e.jsx(te,{className:"flex-1",children:e.jsxs("div",{className:"space-y-4 p-5",children:[e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsxs(p,{children:[e.jsx(v,{className:"text-base",children:c?s("claudeExtensionPage.createBinding",{defaultValue:"Create binding"}):s("claudeExtensionPage.bindingEditor",{defaultValue:"Binding editor"})}),e.jsx(j,{children:s("claudeExtensionPage.bindingEditorDescription",{defaultValue:"Save a profile + IDE path once, then apply or reset it from the dashboard."})})]}),e.jsxs(g,{className:"space-y-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"text-sm font-medium",children:s("settingsPage.thinkingSection.bindingName")}),e.jsx(G,{value:d.name,onChange:a=>A("name",a.target.value),placeholder:s("claudeExtensionPage.bindingNamePlaceholder",{defaultValue:"VS Code · work profile"})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"text-sm font-medium",children:s("claudeExtensionPage.ccsProfile",{defaultValue:"CCS profile"})}),e.jsxs(ne,{value:d.profile,onValueChange:a=>A("profile",a),children:[e.jsx(ie,{children:e.jsx(le,{placeholder:s("claudeExtensionPage.selectProfile",{defaultValue:"Select a profile"})})}),e.jsx(de,{children:V.map(a=>e.jsxs(re,{value:a.name,children:[a.label," (",a.profileType,")"]},a.name))})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:H?.description||s("claudeExtensionPage.chooseProfileHint",{defaultValue:"Choose which CCS profile the IDE should inherit."})})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"text-sm font-medium",children:s("settingsPage.thinkingSection.ideHost")}),e.jsxs(ne,{value:d.host,onValueChange:a=>A("host",a),children:[e.jsx(ie,{children:e.jsx(le,{placeholder:s("claudeExtensionPage.selectHost",{defaultValue:"Select a host"})})}),e.jsx(de,{children:Y.map(a=>e.jsx(re,{value:a.id,children:a.label},a.id))})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"text-sm font-medium",children:s("settingsPage.thinkingSection.ideSettingsPath")}),e.jsx(G,{value:d.ideSettingsPath,onChange:a=>A("ideSettingsPath",a.target.value),placeholder:r?.defaultSettingsPath||s("claudeExtensionPage.ideSettingsPathPlaceholder",{defaultValue:"Leave blank for the default user settings path"})}),e.jsxs("p",{className:"text-xs text-muted-foreground",children:[s("claudeExtensionPage.ideSettingsPathHint",{defaultValue:"Leave blank to use the default user settings path for"})," ",r?.label||"this IDE","."]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"text-sm font-medium",children:s("settingsPage.thinkingSection.notes")}),e.jsx(G,{value:d.notes,onChange:a=>A("notes",a.target.value),placeholder:s("claudeExtensionPage.notesPlaceholder",{defaultValue:"Optional reminder for this machine or workspace"})})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(P,{className:"flex-1 gap-1.5",onClick:()=>void Se(),disabled:!se||ee,children:[ee?e.jsx(Q,{className:"h-3.5 w-3.5 animate-spin"}):e.jsx(Ie,{className:"h-3.5 w-3.5"}),c?s("claudeExtensionPage.create",{defaultValue:"Create"}):s("claudeExtensionPage.save",{defaultValue:"Save"})]}),e.jsx(P,{variant:"outline",onClick:z,children:s("claudeExtensionPage.resetForm",{defaultValue:"Reset form"})})]}),c?null:e.jsxs(P,{variant:"outline",className:"w-full gap-1.5 text-destructive hover:text-destructive",onClick:()=>void Ee(),disabled:F.isPending,children:[e.jsx(Te,{className:"h-3.5 w-3.5"}),s("claudeExtensionPage.deleteBinding",{defaultValue:"Delete binding"})]})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground",children:s("claudeExtensionPage.savedBindings",{defaultValue:"Saved bindings"})}),e.jsx("div",{className:"space-y-2",children:E.length>0?E.map(a=>e.jsx(Ge,{binding:a,isSelected:a.id===h&&!c,onSelect:()=>{D(!1),B(a.id),k(T(a))}},a.id)):e.jsx(x,{className:"border-dashed border-border/60 bg-card/60",children:e.jsx(g,{className:"pt-6 text-sm text-muted-foreground",children:s("claudeExtensionPage.emptyBindings",{defaultValue:"No saved bindings yet. Create one to manage apply, reset, and drift checks from the dashboard."})})})})]})]})})]}),e.jsx("div",{className:"min-w-0 flex-1",children:e.jsx(te,{className:"h-full",children:e.jsxs("div",{className:"w-full space-y-6 p-6 xl:p-7 2xl:p-8",children:[e.jsxs("div",{className:"flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[H?e.jsx(f,{variant:"outline",children:H.label}):null,r?e.jsx(f,{variant:"outline",children:r.label}):null,c?e.jsx(f,{variant:"secondary",children:s("settingsPage.thinkingSection.draft")}):null,u?.sharedSettings&&ge(u.sharedSettings)&&ge(u.ideSettings)?e.jsx(f,{className:"bg-emerald-600 hover:bg-emerald-600",children:s("settingsPage.thinkingSection.inSync")}):null]}),e.jsxs("div",{className:"max-w-5xl",children:[e.jsx("h2",{className:"text-2xl font-semibold tracking-tight",children:N?.name||s("claudeExtensionPage.defaultBindingName",{defaultValue:"Claude extension binding"})}),e.jsx("p",{className:"mt-1 text-sm text-muted-foreground",children:s("claudeExtensionPage.bindingDescription",{defaultValue:"Manage the shared Claude settings file and the IDE-local settings file as two scoped targets."})})]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(P,{variant:"outline",onClick:()=>void R.refetch(),disabled:c||R.isFetching,children:[R.isFetching?e.jsx(Q,{className:"mr-1.5 h-3.5 w-3.5 animate-spin"}):e.jsx(Re,{className:"mr-1.5 h-3.5 w-3.5"}),s("claudeExtensionPage.verify",{defaultValue:"Verify"})]}),i?e.jsx($,{value:i.sharedSettings.command,label:"Copy persist command"}):null]})]}),_?e.jsx(x,{className:"border-destructive/40 bg-destructive/5",children:e.jsxs(g,{className:"flex items-start gap-3 pt-6 text-sm text-destructive",children:[e.jsx(ue,{className:"mt-0.5 h-4 w-4 shrink-0"}),e.jsx("div",{children:_.message})]})}):null,_?null:e.jsxs(we,{defaultValue:"overview",className:"flex flex-col gap-6",children:[e.jsxs(Ve,{className:"w-full justify-start",children:[e.jsx(oe,{value:"overview",children:s("claudeExtensionPage.overview",{defaultValue:"Overview"})})," ",e.jsx(oe,{value:"advanced",children:s("settingsPage.thinkingSection.advanced")})]}),e.jsxs(ce,{value:"overview",className:"mt-0 space-y-6",children:[e.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[e.jsx(he,{title:s("claudeExtensionPage.sharedClaudeSettings",{defaultValue:"Shared Claude settings"}),description:s("claudeExtensionPage.sharedClaudeSettingsDescription",{defaultValue:"Writes the managed env block inside ~/.claude/settings.json so CLI and IDE behavior stay aligned."}),status:u?.sharedSettings,applyLabel:s("claudeExtensionPage.applyShared",{defaultValue:"Apply shared"}),resetLabel:s("claudeExtensionPage.resetShared",{defaultValue:"Reset shared"}),onApply:()=>I("shared","apply"),onReset:()=>I("shared","reset"),disabled:c,busy:Ne}),e.jsx(he,{title:`${r?.label||"IDE"} settings.json`,description:"Writes only the Anthropic extension keys so unrelated editor preferences stay untouched.",status:u?.ideSettings,applyLabel:s("claudeExtensionPage.applyIde",{defaultValue:"Apply IDE"}),resetLabel:s("claudeExtensionPage.resetIde",{defaultValue:"Reset IDE"}),onApply:()=>I("ide","apply"),onReset:()=>I("ide","reset"),disabled:c,busy:ye})]}),e.jsxs("div",{className:"grid gap-6 xl:grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)]",children:[e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsxs(p,{children:[e.jsx(v,{className:"text-base",children:s("settingsPage.thinkingSection.resolvedBinding")}),e.jsx(j,{children:s("claudeExtensionPage.resolvedBindingDescription",{defaultValue:"The binding uses the same profile resolution as `ccs persist` and `ccs env`."})})]}),e.jsxs(g,{className:"space-y-3",children:[e.jsx(o,{label:s("claudeExtensionPage.profile",{defaultValue:"Profile"}),value:i?.profile.label||d.profile||"Not selected"}),e.jsx(o,{label:s("claudeExtensionPage.profileType",{defaultValue:"Profile type"}),value:i?.profile.profileType||"Unknown"}),e.jsx(o,{label:"IDE host",value:r?.label||"Not selected"}),e.jsx(o,{label:s("claudeExtensionPage.idePathMode",{defaultValue:"IDE path mode"}),value:d.ideSettingsPath.trim()?s("claudeExtensionPage.customPath",{defaultValue:"Custom path"}):s("claudeExtensionPage.defaultUserPath",{defaultValue:"Default user path"})}),e.jsx(o,{label:s("claudeExtensionPage.effectiveIdePath",{defaultValue:"Effective IDE path"}),value:u?.ideSettings.path||d.ideSettingsPath.trim()||r?.defaultSettingsPath||"Unavailable",mono:!0,copyValue:u?.ideSettings.path||d.ideSettingsPath.trim()||r?.defaultSettingsPath}),e.jsx(o,{label:s("claudeExtensionPage.persistCommand",{defaultValue:"Persist command"}),value:i?.sharedSettings.command||s("claudeExtensionPage.saveValidBindingFirst",{defaultValue:"Save a valid binding first"}),mono:!0}),d.notes.trim()?e.jsx(o,{label:s("settingsPage.thinkingSection.notes"),value:d.notes.trim()}):null]})]}),e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsxs(p,{children:[e.jsx(v,{className:"text-base",children:s("settingsPage.thinkingSection.managedPayload")}),e.jsx(j,{children:s("claudeExtensionPage.managedPayloadDescription",{defaultValue:"Keep the main view short. The full JSON stays in the Advanced tab."})})]}),e.jsxs(g,{className:"space-y-4",children:[e.jsxs("div",{className:"flex flex-wrap gap-2",children:[Pe.map(a=>e.jsx(f,{variant:"secondary",className:"font-mono text-[10px]",children:a.name},a.name)),ae>0?e.jsxs(f,{variant:"outline",children:["+",ae," more"]}):null]}),e.jsx("div",{className:"rounded-lg border bg-muted/25 p-4 text-sm",children:i?.env.length?e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"font-medium",children:s("claudeExtensionPage.envInjected",{count:i.env.length,defaultValue:"CCS will inject {{count}} environment values."})}),e.jsx("div",{className:"text-muted-foreground",children:s("claudeExtensionPage.envInjectedDescription",{defaultValue:"The IDE-local target receives the extension schema. The shared target receives the same env block through Claude settings."})})]}):e.jsx("div",{className:"text-muted-foreground",children:s("claudeExtensionPage.nativeDefaultsDescription",{defaultValue:"This profile resolves to native Claude defaults, so apply/reset mainly clears existing CCS-managed overrides."})})}),c?e.jsx("div",{className:"rounded-lg border border-dashed bg-muted/15 p-4 text-sm text-muted-foreground",children:s("claudeExtensionPage.saveDraftToUnlock",{defaultValue:"Save this draft to unlock apply, reset, and verify actions."})}):e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(P,{className:"flex-1",onClick:()=>I("all","apply"),disabled:m.isPending,children:[m.isPending&&m.variables?.target==="all"?e.jsx(Q,{className:"mr-1.5 h-3.5 w-3.5 animate-spin"}):null,s("claudeExtensionPage.applyBothTargets",{defaultValue:"Apply both targets"})]}),e.jsx(P,{variant:"outline",className:"flex-1",onClick:()=>I("all","reset"),disabled:b.isPending,children:s("claudeExtensionPage.resetBothTargets",{defaultValue:"Reset both targets"})})]})]})]})]}),i&&(i.warnings.length>0||i.notes.length>0)?e.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsxs(p,{children:[e.jsx(v,{className:"text-base",children:s("settingsPage.thinkingSection.warnings")}),e.jsx(j,{children:"Operational details that can break the binding even when JSON is correct."})]}),e.jsx(g,{className:"space-y-3",children:i.warnings.length>0?i.warnings.map(a=>e.jsxs("div",{className:"flex items-start gap-3 rounded-lg border border-amber-400/40 bg-amber-50/60 p-3 text-sm dark:bg-amber-950/10",children:[e.jsx(ue,{className:"mt-0.5 h-4 w-4 shrink-0 text-amber-600"}),e.jsx("span",{children:a})]},a)):e.jsx("div",{className:"rounded-lg border bg-muted/20 p-3 text-sm text-muted-foreground",children:"No runtime warnings for this binding."})})]}),e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsxs(p,{children:[e.jsx(v,{className:"text-base",children:s("settingsPage.thinkingSection.notes")}),e.jsx(j,{children:"Short context from CCS about account continuity and host-specific behavior."})]}),e.jsx(g,{className:"space-y-3",children:i.notes.length>0?i.notes.map(a=>e.jsxs("div",{className:"flex items-start gap-3 rounded-lg border bg-muted/30 p-3 text-sm",children:[e.jsx(Ae,{className:"mt-0.5 h-4 w-4 shrink-0 text-muted-foreground"}),e.jsx("span",{children:a})]},a)):e.jsx("div",{className:"rounded-lg border bg-muted/20 p-3 text-sm text-muted-foreground",children:"No extra notes for this binding."})})]})]}):null]}),e.jsx(ce,{value:"advanced",className:"mt-0 space-y-6",children:i?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[e.jsx(me,{title:s("claudeExtensionPage.sharedSettingsJson",{defaultValue:"Shared Claude settings JSON"}),description:s("claudeExtensionPage.sharedSettingsJsonDescription",{defaultValue:"Managed env block for ~/.claude/settings.json."}),value:i.sharedSettings.json}),e.jsx(me,{title:`${r?.label||"IDE"} settings JSON`,description:s("claudeExtensionPage.ideSettingsJsonDescription",{label:r?.settingsTargetLabel||"settings.json",defaultValue:"Anthropic extension snippet for {{label}}."}),value:i.ideSettings.json})]}),e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsx(p,{children:e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx(v,{className:"text-base",children:s("claudeExtensionPage.resolvedEnvironmentPayload",{defaultValue:"Resolved environment payload"})}),e.jsx(j,{children:s("claudeExtensionPage.resolvedEnvironmentPayloadDescription",{defaultValue:"Exact environment values that the extension receives after CCS expands this profile."})})]}),e.jsx($,{value:JSON.stringify(i.env,null,2),label:s("claudeExtensionPage.copyEnvironmentPayload",{defaultValue:"Copy environment payload"})})]})}),e.jsx(g,{children:i.env.length>0?e.jsx("pre",{className:"max-h-[420px] overflow-auto rounded-lg border bg-muted/30 p-4 text-xs leading-6",children:JSON.stringify(i.env,null,2)}):e.jsx("div",{className:"rounded-lg border bg-muted/20 p-4 text-sm text-muted-foreground",children:"No env payload. This binding resolves to native Claude defaults."})})]}),e.jsxs("div",{className:"grid gap-6 xl:grid-cols-2",children:[e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsxs(p,{children:[e.jsx(v,{className:"text-base",children:"Shared target metadata"}),e.jsx(j,{children:"Useful when debugging drift or comparing with manual edits."})]}),e.jsxs(g,{className:"space-y-3",children:[e.jsx(o,{label:"Target path",value:u?.sharedSettings.path||i.sharedSettings.path,mono:!0,copyValue:u?.sharedSettings.path||i.sharedSettings.path}),e.jsx(o,{label:"Command",value:i.sharedSettings.command,mono:!0}),e.jsx(o,{label:"Current state",value:u?.sharedSettings.state||"Not verified"})]})]}),e.jsxs(x,{className:"border-border/60 bg-card/80",children:[e.jsxs(p,{children:[e.jsx(v,{className:"text-base",children:"IDE target metadata"}),e.jsx(j,{children:"Current file path plus the extension setting key used for this host."})]}),e.jsxs(g,{className:"space-y-3",children:[e.jsx(o,{label:"Target path",value:u?.ideSettings.path||d.ideSettingsPath.trim()||r?.defaultSettingsPath||i.ideSettings.path,mono:!0,copyValue:u?.ideSettings.path||d.ideSettingsPath.trim()||r?.defaultSettingsPath||i.ideSettings.path}),e.jsx(o,{label:"Settings key",value:r?.settingsKey||"Unknown",mono:!0}),e.jsx(o,{label:"Current state",value:u?.ideSettings.state||"Not verified"})]})]})]})]}):e.jsx(x,{className:"border-border/60 bg-card/80",children:e.jsxs(g,{className:"flex min-h-[240px] items-center justify-center gap-3 text-sm text-muted-foreground",children:[e.jsx(qe,{className:"h-5 w-5"}),"Choose a profile and IDE host to preview the generated payload."]})})})]})]})})})]})}export{ls as ClaudeExtensionPage};