cc-viewer 1.6.300 → 1.6.302

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.js CHANGED
@@ -14,6 +14,8 @@ import { t } from './server/i18n.js';
14
14
  import { INJECT_IMPORT, LEGACY_INJECT_IMPORTS, resolveCliPath, resolveNativePath, resolveNpmClaudePath, buildShellCandidates, setLogDir, LOG_DIR, hasClaude2xWrapper, getGlobalNodeModulesDir, PACKAGES, getClaudeConfigDir } from './findcc.js';
15
15
  import { ensureHooks, removeAllManagedHooks } from './server/lib/ensure-hooks.js';
16
16
  import { injectCliJsAt, removeCliJsInjectionAt, INJECT_START as _INJECT_START, INJECT_END as _INJECT_END, buildInjectBlock as _buildInjectBlock } from './server/lib/cli-inject.js';
17
+ import { normalizeBasePath } from './server/lib/base-path.js';
18
+ import { createHardenedCleanup, installWinKeypressFallback } from './server/lib/term-signals.js';
17
19
 
18
20
  const __dirname = fileURLToPath(new URL('.', import.meta.url));
19
21
 
@@ -389,8 +391,8 @@ async function runCliMode(extraClaudeArgs = [], cwd, noOpen = false) {
389
391
 
390
392
  // 4. 自动打开浏览器
391
393
  const protocol = serverMod.getProtocol();
392
- const basePath = process.env.CCV_BASE_PATH || '';
393
- const url = `${protocol}://127.0.0.1:${port}${basePath}`;
394
+ const basePath = normalizeBasePath(process.env.CCV_BASE_PATH);
395
+ const url = `${protocol}://127.0.0.1:${port}${basePath}`;
394
396
  if (!noOpen) {
395
397
  try {
396
398
  // URL 含 & 在 cmd.exe 下会被当命令分隔符切断 query;用 spawn 数组传参避免 shell interpolation。
@@ -421,13 +423,20 @@ const url = `${protocol}://127.0.0.1:${port}${basePath}`;
421
423
  else console.log(` ${t('server.passwordActive', { password: _auth.password })}`);
422
424
  }
423
425
 
424
- // 5. 注册退出处理
425
- const cleanup = () => {
426
- killPty();
427
- serverMod.stopViewer().finally(() => process.exit());
428
- };
426
+ // 5. 注册退出处理(hardened:watchdog 5s 强退 + 连按 Ctrl+C 立退,
427
+ // 防 Windows ConPTY kill / IM teardown 挂住导致"Ctrl+C 完全无反应")
428
+ const cleanup = createHardenedCleanup({
429
+ doCleanup: () => {
430
+ killPty();
431
+ return serverMod.stopViewer();
432
+ },
433
+ });
429
434
  process.on('SIGINT', cleanup);
430
435
  process.on('SIGTERM', cleanup);
436
+ // Windows 兜底:ConPTY 下控制台 Ctrl+C 事件偶发不送达(SIGINT 永不触发),
437
+ // raw mode keypress 直连 cleanup。silent 模式本地终端无人读 stdin,无副作用;
438
+ // darwin / 非 TTY 内部自动跳过。
439
+ installWinKeypressFallback({ onInterrupt: cleanup });
431
440
  }
432
441
 
433
442
  // 启动一个独立常驻 IM worker。本质是「在 IM_<id>/ 工作目录、绑 127.0.0.1、skip-permissions」的 runCliMode,
@@ -552,8 +561,8 @@ async function runSdkMode(extraClaudeArgs = [], cwd, noOpen = false) {
552
561
 
553
562
  // 自动打开浏览器
554
563
  const protocol = serverMod.getProtocol();
555
- const basePath = process.env.CCV_BASE_PATH || '';
556
- const url = `${protocol}://127.0.0.1:${port}${basePath}`;
564
+ const basePath = normalizeBasePath(process.env.CCV_BASE_PATH);
565
+ const url = `${protocol}://127.0.0.1:${port}${basePath}`;
557
566
  if (!noOpen) {
558
567
  try {
559
568
  // URL 含 & 在 cmd.exe 下会被当命令分隔符切断 query;用 spawn 数组传参避免 shell interpolation。
@@ -584,13 +593,16 @@ const url = `${protocol}://127.0.0.1:${port}${basePath}`;
584
593
  else console.log(` ${t('server.passwordActive', { password: _auth.password })}`);
585
594
  }
586
595
 
587
- // 注册退出处理
588
- const cleanup = () => {
589
- sdkManager.stopSession();
590
- serverMod.stopViewer().finally(() => process.exit());
591
- };
596
+ // 注册退出处理(hardened,与 PTY 模式同款三层防御)
597
+ const cleanup = createHardenedCleanup({
598
+ doCleanup: () => {
599
+ sdkManager.stopSession();
600
+ return serverMod.stopViewer();
601
+ },
602
+ });
592
603
  process.on('SIGINT', cleanup);
593
604
  process.on('SIGTERM', cleanup);
605
+ installWinKeypressFallback({ onInterrupt: cleanup });
594
606
  }
595
607
 
596
608
  // === 主逻辑 ===