cc-viewer 1.7.3 → 1.7.4

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 (87) hide show
  1. package/cli.js +88 -17
  2. package/dist/assets/App-CKvCavmF.js +2 -0
  3. package/dist/assets/App-Dj37fluO.css +1 -0
  4. package/dist/assets/{MdxEditorPanel-D_dewIIc.js → MdxEditorPanel-DeSJp_ct.js} +1 -1
  5. package/dist/assets/{Mobile-BIzb7vpA.js → Mobile-CWou-jtF.js} +1 -1
  6. package/dist/assets/{_baseUniq-D9_E5ZDp.js → _baseUniq-BK7Nhp_x.js} +1 -1
  7. package/dist/assets/{arc-D4iFrlhb.js → arc-Cm4HMXz6.js} +1 -1
  8. package/dist/assets/{architectureDiagram-Q4EWVU46-f1neo7hg.js → architectureDiagram-Q4EWVU46-Co8QCowN.js} +1 -1
  9. package/dist/assets/{blockDiagram-DXYQGD6D-DWi9e9tG.js → blockDiagram-DXYQGD6D-Cudecf7x.js} +1 -1
  10. package/dist/assets/{c4Diagram-AHTNJAMY-DAdj49A2.js → c4Diagram-AHTNJAMY-4JeFoCHX.js} +1 -1
  11. package/dist/assets/{channel-B3VthT0m.js → channel-K7eT7Hjr.js} +1 -1
  12. package/dist/assets/{chunk-4BX2VUAB-BrPcAHV3.js → chunk-4BX2VUAB-BBjl6EVW.js} +1 -1
  13. package/dist/assets/{chunk-4TB4RGXK-Bvopc1V9.js → chunk-4TB4RGXK-DBP9YnwO.js} +1 -1
  14. package/dist/assets/{chunk-55IACEB6-Juw-7Z3i.js → chunk-55IACEB6--A_i9gwR.js} +1 -1
  15. package/dist/assets/{chunk-EDXVE4YY-DVYP9tpB.js → chunk-EDXVE4YY-UBsL-3qr.js} +1 -1
  16. package/dist/assets/{chunk-FMBD7UC4-VQ7VOXl3.js → chunk-FMBD7UC4-DBO0asmI.js} +1 -1
  17. package/dist/assets/{chunk-OYMX7WX6-F8Rj4ZUZ.js → chunk-OYMX7WX6-DOCcEaE6.js} +1 -1
  18. package/dist/assets/{chunk-QZHKN3VN-Dn7K3yi9.js → chunk-QZHKN3VN-B-j8QbQh.js} +1 -1
  19. package/dist/assets/{chunk-YZCP3GAM-UjO-KTsn.js → chunk-YZCP3GAM-B0WO1U-U.js} +1 -1
  20. package/dist/assets/classDiagram-6PBFFD2Q-Ba3tOmih.js +1 -0
  21. package/dist/assets/classDiagram-v2-HSJHXN6E-Ba3tOmih.js +1 -0
  22. package/dist/assets/clone-DjURvTeN.js +1 -0
  23. package/dist/assets/{cose-bilkent-S5V4N54A-BDhNj8rq.js → cose-bilkent-S5V4N54A-DQgpss_a.js} +1 -1
  24. package/dist/assets/{dagre-KV5264BT-BzdcDuyt.js → dagre-KV5264BT-CCJBKmKv.js} +1 -1
  25. package/dist/assets/{diagram-5BDNPKRD-DP_SX65N.js → diagram-5BDNPKRD-DgUcnfkU.js} +1 -1
  26. package/dist/assets/{diagram-G4DWMVQ6-B-pBFw8Q.js → diagram-G4DWMVQ6-CRpVvexw.js} +1 -1
  27. package/dist/assets/{diagram-MMDJMWI5-6XSfN1eN.js → diagram-MMDJMWI5-C3IhpsM-.js} +1 -1
  28. package/dist/assets/{diagram-TYMM5635-DJvGxvpE.js → diagram-TYMM5635-BaVbsrwt.js} +1 -1
  29. package/dist/assets/{erDiagram-SMLLAGMA-CpqdpGYj.js → erDiagram-SMLLAGMA-BxRoBUR8.js} +1 -1
  30. package/dist/assets/{flowDiagram-DWJPFMVM-C9m7wjwI.js → flowDiagram-DWJPFMVM-Du2lIzoM.js} +1 -1
  31. package/dist/assets/{ganttDiagram-T4ZO3ILL-CU0XOfY3.js → ganttDiagram-T4ZO3ILL-DR1pDqLK.js} +1 -1
  32. package/dist/assets/{gitGraphDiagram-UUTBAWPF-ipyGULnP.js → gitGraphDiagram-UUTBAWPF-DHAmE20O.js} +1 -1
  33. package/dist/assets/{graph-BR2KmES9.js → graph-D8Yv0IU1.js} +1 -1
  34. package/dist/assets/{index-C-be29ey.js → index-AOmFG_I_.js} +1 -1
  35. package/dist/assets/{index-D5tSeMU7.js → index-B6ZWIxY0.js} +1 -1
  36. package/dist/assets/{index-D5YruOKD.js → index-Bq95HjYM.js} +1 -1
  37. package/dist/assets/{index--wgRW_OB.js → index-D0pvxpcl.js} +1 -1
  38. package/dist/assets/{index-DuZQ6FqR.js → index-D2c77__8.js} +1 -1
  39. package/dist/assets/{index-C16fiVhv.js → index-DgtUyP57.js} +1 -1
  40. package/dist/assets/{index-DidP9FCD.js → index-Dpu92rw_.js} +2 -2
  41. package/dist/assets/{index-OWzSX2T8.js → index-cCB-lhEO.js} +1 -1
  42. package/dist/assets/{infoDiagram-42DDH7IO-BQWQjfFX.js → infoDiagram-42DDH7IO-BAqRIm2G.js} +1 -1
  43. package/dist/assets/{ishikawaDiagram-UXIWVN3A-Df-KH_Hz.js → ishikawaDiagram-UXIWVN3A-CzH4tezb.js} +1 -1
  44. package/dist/assets/{journeyDiagram-VCZTEJTY-CMbB_4G9.js → journeyDiagram-VCZTEJTY-Qs4eiyuI.js} +1 -1
  45. package/dist/assets/{jszip.min-B6k2t9Ch.js → jszip.min-C0OccDsk.js} +1 -1
  46. package/dist/assets/{kanban-definition-6JOO6SKY-CImdlKN3.js → kanban-definition-6JOO6SKY-BAjl0gpV.js} +1 -1
  47. package/dist/assets/{layout-DQl4aw09.js → layout-Cf6cd9xE.js} +1 -1
  48. package/dist/assets/{linear-CLD3aPlK.js → linear-PzdCkji6.js} +1 -1
  49. package/dist/assets/mermaid.core-DV9745Lq.js +7 -0
  50. package/dist/assets/{min-_D86henJ.js → min-CHgWxbzS.js} +1 -1
  51. package/dist/assets/{mindmap-definition-QFDTVHPH-2JVcu1nk.js → mindmap-definition-QFDTVHPH-CHUjMdpi.js} +1 -1
  52. package/dist/assets/{pieDiagram-DEJITSTG-Dik-m5u7.js → pieDiagram-DEJITSTG-D_r2QC0f.js} +1 -1
  53. package/dist/assets/{quadrantDiagram-34T5L4WZ-Bm5l40Jq.js → quadrantDiagram-34T5L4WZ-DPzGygdN.js} +1 -1
  54. package/dist/assets/{requirementDiagram-MS252O5E-Ck9w5rTj.js → requirementDiagram-MS252O5E-BA2ynnGb.js} +1 -1
  55. package/dist/assets/{sankeyDiagram-XADWPNL6-BbGVCFEf.js → sankeyDiagram-XADWPNL6-XHVvXuqU.js} +1 -1
  56. package/dist/assets/{seqResourceLoaders-DFrHmgnK.css → seqResourceLoaders-0RXZfUKp.css} +1 -1
  57. package/dist/assets/seqResourceLoaders-kBEzcKLh.js +2 -0
  58. package/dist/assets/{sequenceDiagram-FGHM5R23-vjvEdksk.js → sequenceDiagram-FGHM5R23-C47sA8YH.js} +1 -1
  59. package/dist/assets/{stateDiagram-FHFEXIEX-DTqMoiB2.js → stateDiagram-FHFEXIEX-BOmer839.js} +1 -1
  60. package/dist/assets/{stateDiagram-v2-QKLJ7IA2-CNbAwnhu.js → stateDiagram-v2-QKLJ7IA2-D-kVvTHA.js} +1 -1
  61. package/dist/assets/{timeline-definition-GMOUNBTQ-Cw8O-52d.js → timeline-definition-GMOUNBTQ-CTAKVZgs.js} +1 -1
  62. package/dist/assets/{vendor-antd-DI7JL-mE.js → vendor-antd-BuJ6oz45.js} +2 -2
  63. package/dist/assets/{vendor-codemirror-B9c49dtM.js → vendor-codemirror-D2KAW4ty.js} +1 -1
  64. package/dist/assets/{vendor-mdxeditor-B6cpBtIE.js → vendor-mdxeditor-BsFVVUcj.js} +2 -2
  65. package/dist/assets/{vendor-qrcode-Dn90-s-u.js → vendor-qrcode-BpxSq04x.js} +1 -1
  66. package/dist/assets/{vendor-virtuoso-CpdSKD_6.js → vendor-virtuoso-an4WvAvS.js} +1 -1
  67. package/dist/assets/{vennDiagram-DHZGUBPP-DCuqymb7.js → vennDiagram-DHZGUBPP-CuXzoroh.js} +1 -1
  68. package/dist/assets/{wardley-RL74JXVD-BJ20wJUU.js → wardley-RL74JXVD-CFCmxnck.js} +1 -1
  69. package/dist/assets/{wardleyDiagram-NUSXRM2D-Vz6US3qt.js → wardleyDiagram-NUSXRM2D-gfhDB9SR.js} +1 -1
  70. package/dist/assets/{xychartDiagram-5P7HB3ND-CzkLKrmi.js → xychartDiagram-5P7HB3ND-X-pj66qI.js} +1 -1
  71. package/dist/index.html +4 -4
  72. package/package.json +1 -1
  73. package/server/i18n.js +100 -55
  74. package/server/interceptor.js +27 -1
  75. package/server/lib/ccswitch-import.js +251 -0
  76. package/server/lib/proxy-retry.js +716 -0
  77. package/server/proxy.js +115 -11
  78. package/server/routes/preferences.js +184 -1
  79. package/server/server.js +12 -1
  80. package/src/utils/contentFilter.js +17 -1
  81. package/dist/assets/App-DRsuJXEw.js +0 -2
  82. package/dist/assets/App-dYPa5-df.css +0 -1
  83. package/dist/assets/classDiagram-6PBFFD2Q-DmSozxoO.js +0 -1
  84. package/dist/assets/classDiagram-v2-HSJHXN6E-DmSozxoO.js +0 -1
  85. package/dist/assets/clone-BQtvy9Ae.js +0 -1
  86. package/dist/assets/mermaid.core-CN35dvKJ.js +0 -7
  87. package/dist/assets/seqResourceLoaders-BtGpfGRW.js +0 -2
package/cli.js CHANGED
@@ -5,7 +5,7 @@ if (process.platform === 'win32' && !process.env.UV_THREADPOOL_SIZE) {
5
5
  process.env.UV_THREADPOOL_SIZE = '16';
6
6
  }
7
7
 
8
- import { readFileSync, writeFileSync, existsSync, realpathSync, unlinkSync, mkdirSync } from 'node:fs';
8
+ import { readFileSync, writeFileSync, existsSync, realpathSync, unlinkSync, mkdirSync, statSync } from 'node:fs';
9
9
  import { resolve, isAbsolute } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
11
  import { homedir } from 'node:os';
@@ -60,6 +60,14 @@ function reportClaudeNotFound(cliPathHint) {
60
60
  }
61
61
  }
62
62
 
63
+ // Tombstone written by --uninstall; blocks the shell hook's background self-heal
64
+ // (`ccv -logger --self-heal`) from silently re-installing the integration.
65
+ // Anchored to the config dir (not LOG_DIR, which honors --log-dir/CCV_LOG_DIR
66
+ // overrides) so --uninstall and the hook always resolve the same path.
67
+ function getUninstallFlagPath() {
68
+ return resolve(getClaudeConfigDir(), 'cc-viewer', 'uninstalled.flag');
69
+ }
70
+
63
71
  function getShellConfigPath() {
64
72
  const shell = process.env.SHELL || '';
65
73
  if (shell.includes('zsh')) return resolve(homedir(), '.zshrc');
@@ -94,6 +102,12 @@ function buildShellHook(isNative) {
94
102
  '--help', '-h',
95
103
  ];
96
104
 
105
+ // Guard shared by both variants: if ccv was uninstalled, the wrapper removes
106
+ // itself from the live shell and falls back to the real claude binary.
107
+ // `hash -r` first — otherwise a stale command-hash entry in a long-lived
108
+ // shell lets `command -v ccv` false-pass once after the binary is deleted.
109
+ const ccvGoneGuard = `hash -r 2>/dev/null; command -v ccv >/dev/null 2>&1 || { unset -f claude; command claude "$@"; return; }`;
110
+
97
111
  if (isNative) {
98
112
  return `${SHELL_HOOK_START}
99
113
  claude() {
@@ -103,6 +117,8 @@ claude() {
103
117
  command claude "$@"
104
118
  return
105
119
  fi
120
+ # If ccv is gone (uninstalled), drop this stale wrapper and use claude directly
121
+ ${ccvGoneGuard}
106
122
  # Pass through certain commands directly without ccv interception
107
123
  case "$1" in
108
124
  ${passthroughCommands.join('|')})
@@ -128,6 +144,8 @@ claude() {
128
144
  command claude "$@"
129
145
  return
130
146
  fi
147
+ # If ccv is gone (uninstalled), drop this stale wrapper and use claude directly
148
+ ${ccvGoneGuard}
131
149
  # Pass through certain commands directly without ccv interception
132
150
  case "$1" in
133
151
  ${passthroughCommands.join('|')})
@@ -149,12 +167,12 @@ claude() {
149
167
  if [ -z "$cli_js" ]; then
150
168
  # cli.js 消失 → Claude Code 已升级到 2.1.114+(native-only 分发)。
151
169
  # 后台重写 hook(下次 shell 就是 native hook),当前调用直接走 native proxy 路径。
152
- ( ccv -logger >/dev/null 2>&1 & )
170
+ ( ccv -logger --self-heal >/dev/null 2>&1 & )
153
171
  ccv run -- claude --ccv-internal "$@"
154
172
  return $?
155
173
  fi
156
174
  if ! grep -q "CC Viewer" "$cli_js" 2>/dev/null; then
157
- ccv -logger 2>/dev/null
175
+ ccv -logger --self-heal 2>/dev/null
158
176
  fi
159
177
  command claude "$@"
160
178
  }
@@ -196,7 +214,8 @@ function removeShellHook() {
196
214
  for (const f of ['.zshrc', '.zprofile', '.bashrc', '.bash_profile', '.profile']) {
197
215
  allPaths.add(resolve(home, f));
198
216
  }
199
- let lastResult = { path: configPath, status: 'clean' };
217
+ // Per-file results; empty array = no file contained the hook marker
218
+ const results = [];
200
219
  for (const p of allPaths) {
201
220
  try {
202
221
  if (!existsSync(p)) continue;
@@ -204,13 +223,19 @@ function removeShellHook() {
204
223
  if (!content.includes(SHELL_HOOK_START)) continue;
205
224
  const regex = new RegExp(`\\n?${SHELL_HOOK_START}[\\s\\S]*?${SHELL_HOOK_END}\\n?`, 'g');
206
225
  const newContent = content.replace(regex, '\n');
226
+ if (newContent === content) {
227
+ // START marker present but the block is malformed (e.g. END marker damaged):
228
+ // leave the file untouched instead of reporting a removal that didn't happen
229
+ results.push({ file: p, status: 'corrupt' });
230
+ continue;
231
+ }
207
232
  writeFileSync(p, newContent);
208
- lastResult = { path: p, status: 'removed' };
233
+ results.push({ file: p, status: 'removed' });
209
234
  } catch (err) {
210
- lastResult = { path: p, status: 'error', error: err.message };
235
+ results.push({ file: p, status: 'error', error: err.message });
211
236
  }
212
237
  }
213
- return lastResult;
238
+ return results;
214
239
  }
215
240
 
216
241
  function injectCliJs() {
@@ -463,12 +488,18 @@ async function runCliMode(extraClaudeArgs = [], cwd, noOpen = false) {
463
488
  // Win 上 `start` 是 cmd.exe 内置不是 .exe,必须 shell:true;用 spawn + 数组让 Node 自己 escape。
464
489
  // 第二个 arg '""' 是 `start` 的 window-title 占位(否则 start 会把 URL 当 title)。
465
490
  const { spawn } = await import('node:child_process');
491
+ // Headless/container envs may lack xdg-open/open/start → spawn asynchronously emits an 'error' event,
492
+ // which try/catch cannot catch (it only catches synchronous throws); without a handler the process
493
+ // would crash. Swallow it here as a best-effort fallback.
494
+ let child;
466
495
  if (process.platform === 'win32') {
467
- spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore', detached: true, windowsHide: true }).unref();
496
+ child = spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore', detached: true, windowsHide: true });
468
497
  } else {
469
498
  const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open';
470
- spawn(cmd, [url], { stdio: 'ignore', detached: true }).unref();
499
+ child = spawn(cmd, [url], { stdio: 'ignore', detached: true });
471
500
  }
501
+ child.on('error', () => {});
502
+ child.unref();
472
503
  } catch {}
473
504
  }
474
505
 
@@ -646,12 +677,18 @@ async function runSdkMode(extraClaudeArgs = [], cwd, noOpen = false) {
646
677
  // Win 上 `start` 是 cmd.exe 内置不是 .exe,必须 shell:true;用 spawn + 数组让 Node 自己 escape。
647
678
  // 第二个 arg '""' 是 `start` 的 window-title 占位(否则 start 会把 URL 当 title)。
648
679
  const { spawn } = await import('node:child_process');
680
+ // Headless/container envs may lack xdg-open/open/start → spawn asynchronously emits an 'error' event,
681
+ // which try/catch cannot catch (it only catches synchronous throws); without a handler the process
682
+ // would crash. Swallow it here as a best-effort fallback.
683
+ let child;
649
684
  if (process.platform === 'win32') {
650
- spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore', detached: true, windowsHide: true }).unref();
685
+ child = spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore', detached: true, windowsHide: true });
651
686
  } else {
652
687
  const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open';
653
- spawn(cmd, [url], { stdio: 'ignore', detached: true }).unref();
688
+ child = spawn(cmd, [url], { stdio: 'ignore', detached: true });
654
689
  }
690
+ child.on('error', () => {});
691
+ child.unref();
655
692
  } catch {}
656
693
  }
657
694
 
@@ -805,7 +842,7 @@ if (isVersion) {
805
842
 
806
843
  if (isUninstall) {
807
844
  const cliResult = removeCliJsInjection();
808
- const shellResult = removeShellHook();
845
+ const shellResults = removeShellHook();
809
846
 
810
847
  if (cliResult === 'removed' || cliResult === 'clean') {
811
848
  console.log(t('cli.uninstall.cliCleaned'));
@@ -815,12 +852,18 @@ if (isUninstall) {
815
852
  console.log(t('cli.uninstall.cliFail'));
816
853
  }
817
854
 
818
- if (shellResult.status === 'removed') {
819
- console.log(t('cli.uninstall.hookRemoved', { path: shellResult.path }));
820
- } else if (shellResult.status === 'clean' || shellResult.status === 'not_found') {
821
- console.log(t('cli.uninstall.hookClean', { path: shellResult.path }));
855
+ if (shellResults.length === 0) {
856
+ console.log(t('cli.uninstall.hookClean', { path: getShellConfigPath() }));
822
857
  } else {
823
- console.log(t('cli.uninstall.hookFail', { error: shellResult.error }));
858
+ for (const r of shellResults) {
859
+ if (r.status === 'removed') {
860
+ console.log(t('cli.uninstall.hookRemoved', { path: r.file }));
861
+ } else if (r.status === 'corrupt') {
862
+ console.log(t('cli.uninstall.corruptBlock', { file: r.file }));
863
+ } else {
864
+ console.log(t('cli.uninstall.hookFail', { file: r.file, error: r.error }));
865
+ }
866
+ }
824
867
  }
825
868
 
826
869
  // 清理 settings.json 里的 cc-viewer-managed hooks + 历史 statusLine 残留
@@ -863,12 +906,40 @@ if (isUninstall) {
863
906
  }
864
907
  } catch { }
865
908
 
909
+ // Tombstone: stale hooks in still-open shells background `ccv -logger --self-heal`,
910
+ // which would otherwise re-install everything right after this uninstall.
911
+ try {
912
+ mkdirSync(resolve(getClaudeConfigDir(), 'cc-viewer'), { recursive: true });
913
+ writeFileSync(getUninstallFlagPath(), new Date().toISOString() + '\n');
914
+ } catch { }
915
+
866
916
  console.log(t('cli.uninstall.reloadShell'));
867
917
  console.log(t('cli.uninstall.done'));
868
918
  process.exit(0);
869
919
  }
870
920
 
871
921
  if (isLogger) {
922
+ // Tombstone check must run before mode detection: a backgrounded self-heal with
923
+ // claude fully uninstalled must exit 0 silently, not emit reportClaudeNotFound noise.
924
+ try {
925
+ const flagPath = getUninstallFlagPath();
926
+ if (existsSync(flagPath)) {
927
+ if (args.includes('--self-heal')) {
928
+ // Explicitly uninstalled — never let a stale hook resurrect the integration
929
+ process.exit(0);
930
+ }
931
+ const ageMs = Date.now() - statSync(flagPath).mtimeMs;
932
+ const GRACE_MS = 10 * 60 * 1000;
933
+ if (ageMs < GRACE_MS && !args.includes('--force')) {
934
+ // Old deployed hooks self-heal via plain `ccv -logger`; a grace period keeps
935
+ // them from wiping a fresh tombstone during the uninstall race window.
936
+ console.log(t('cli.logger.justUninstalled'));
937
+ process.exit(0);
938
+ }
939
+ unlinkSync(flagPath);
940
+ }
941
+ } catch { }
942
+
872
943
  // 模式选择:有 cli.js 就走 npm 注入模式(pre-2.1.113),没有就走 native proxy
873
944
  // 模式(2.1.114+)。单一判据,不再靠 realpath 的启发式。
874
945
  const nativePath = resolveNativePath();