cc-viewer 1.7.5 → 1.7.6

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/dist/index.html CHANGED
@@ -21,7 +21,7 @@
21
21
  // 整体显示大小已弃用 CSS zoom:Electron 改用 webFrame.setZoomFactor(首屏抢占见
22
22
  // electron/tab-content-preload.js),纯浏览器交由用户用浏览器自带快捷键缩放,故此处不再设 zoom。
23
23
  </script>
24
- <script type="module" crossorigin src="./assets/index-DJ0AQwus.js"></script>
24
+ <script type="module" crossorigin src="./assets/index-BjNLFJ0z.js"></script>
25
25
  <link rel="modulepreload" crossorigin href="./assets/vendor-antd-DeqwrDxf.js">
26
26
  <link rel="modulepreload" crossorigin href="./assets/vendor-codemirror-_NbrtdQc.js">
27
27
  <link rel="modulepreload" crossorigin href="./assets/vendor-mdxeditor-DG0Nyxw5.js">
package/findcc.js CHANGED
@@ -351,6 +351,64 @@ export function pickSpawnableLookupResult(rawOut, platform = process.platform) {
351
351
  return lines[0] || null;
352
352
  }
353
353
 
354
+ /**
355
+ * Resolve the `claude` executable explicitly selected by the caller's PATH.
356
+ *
357
+ * Wrappers such as CodeFuse's `cfuse --ccv` prepend their managed Claude Code
358
+ * directory to PATH before launching ccv. This lookup must happen before any
359
+ * global npm fallback, otherwise an unrelated global @anthropic-ai install wins
360
+ * and the wrapper-provided (and potentially enterprise-approved) binary is lost.
361
+ *
362
+ * Returns both the path and whether Node is required for a legacy cli.js entry.
363
+ */
364
+ export function resolveClaudeFromPath() {
365
+ const lookupCmds = process.platform === 'win32'
366
+ ? [`where ${BINARY_NAME}`]
367
+ : [`which ${BINARY_NAME}`, `command -v ${BINARY_NAME}`];
368
+
369
+ for (const cmd of lookupCmds) {
370
+ try {
371
+ const rawOut = execSync(cmd, { encoding: 'utf-8', shell: true, env: process.env, windowsHide: true });
372
+ const result = pickSpawnableLookupResult(rawOut);
373
+ if (!result || !existsSync(result)) continue;
374
+
375
+ let real = result;
376
+ try { real = realpathSync(result); } catch { }
377
+ if (real.endsWith('.js')) return { path: real, isNpmVersion: true };
378
+ return { path: result, isNpmVersion: false };
379
+ } catch {
380
+ // Try the next lookup command.
381
+ }
382
+ }
383
+
384
+ return null;
385
+ }
386
+
387
+ // Starpoint currently approves the CodeFuse-managed Claude Code 2.1.199 build.
388
+ // Keep standalone `ccv` on the same binary as `cfuse --ccv`; callers can move
389
+ // the pin deliberately after a new enterprise build has been approved.
390
+ export const DEFAULT_CODEFUSE_CLAUDE_VERSION = '2.1.199';
391
+
392
+ export function resolveCodeFuseClaudePath(
393
+ codeFuseClaudeRoot = null,
394
+ version = process.env.CCV_CODEFUSE_CLAUDE_VERSION?.trim() || DEFAULT_CODEFUSE_CLAUDE_VERSION,
395
+ ) {
396
+ // Never let a test process escape its disposable fixtures into the real home.
397
+ // Tests exercise this resolver by passing an explicit temporary root.
398
+ if (!codeFuseClaudeRoot) {
399
+ if (isRealClaudeLookupBlocked()) return null;
400
+ codeFuseClaudeRoot = join(homedir(), '.codefuse', 'fuse', 'engine', 'bin', 'claude');
401
+ }
402
+ if (!version || !/^\d+\.\d+\.\d+$/.test(version)) return null;
403
+ const versionDir = join(codeFuseClaudeRoot, version);
404
+ const names = process.platform === 'win32' ? ['claude.exe'] : ['claude', 'claude.exe'];
405
+ for (const name of names) {
406
+ const candidate = join(versionDir, name);
407
+ if (existsSync(candidate)) return candidate;
408
+ }
409
+ return null;
410
+ }
411
+
354
412
  export function resolveNativePath() {
355
413
  // L7: steps 1 & 4 (platform/packaged binaries under `npm root -g`) ignore PATH isolation —
356
414
  // neutralized under test context via a null globalRoot (both helpers null-guard).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.7.5",
3
+ "version": "1.7.6",
4
4
  "description": "Claude Code logging, visualization, and management toolkit — launch a web viewer alongside Claude Code with full request/response tracing, proxy, and mobile support",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
@@ -400,34 +400,45 @@ export function replaceTopLevelModel(jsonStr, oldModel, newModel) {
400
400
  // 家族用**大小写不敏感子串**匹配(/opus/i 等),只认这几个已知家族单词——
401
401
  // 因此 claude-opus-4-8、未来的 claude-opus-5 等任何版本都命中同一家族,版本升级无需重配。
402
402
  // 字段直接沿用 Claude Code 的环境变量名以保持一致:
403
- // ANTHROPIC_MODEL —— 主模型;body.model 含 "fable" / "mythos" 时映射到它
403
+ // ANTHROPIC_MODEL —— 主模型(catch-all 兜底;body.model 含 "fable"/"mythos"
404
+ // 及所有未识别家族均回落到它)
404
405
  // ANTHROPIC_DEFAULT_OPUS_MODEL —— body.model 含 "opus"
405
406
  // ANTHROPIC_DEFAULT_SONNET_MODEL —— 含 "sonnet"
406
407
  // ANTHROPIC_DEFAULT_HAIKU_MODEL —— 含 "haiku"
407
- // 家族字段留空 = 该家族不改写(透传原始 model)。
408
- // 未识别家族(既不是 opus/sonnet/haiku 也不是 fable/mythos)不做兜底替换,原样透传。
408
+ // 家族字段优先:opus/sonnet/haiku 各自命中专属字段;字段留空则回落到 ANTHROPIC_MODEL。
409
+ // 未识别家族(既不是 opus/sonnet/haiku 也不是 fable/mythos)→ ANTHROPIC_MODEL 兜底。
409
410
  // 兼容旧数据:profile 未设任何新字段但有 activeModel(老结构)时,回退为旧的整体替换语义。
410
- // 返回目标模型字符串;无需改写(无目标 / 目标同旧值 / 入参非法 / 未识别家族)时返回 null。
411
+ // 返回目标模型字符串;无需改写(无目标 / 目标同旧值 / 入参非法)时返回 null。
412
+ // [1m] 后缀(Claude Code 1M context 标记)默认忽略:所有 profile 模型字段值在比较前先剥除。
411
413
  export function resolveProfileModel(oldModel, profile) {
412
414
  if (typeof oldModel !== 'string' || !oldModel || !profile || typeof profile !== 'object') return null;
413
- const opus = typeof profile.ANTHROPIC_DEFAULT_OPUS_MODEL === 'string' ? profile.ANTHROPIC_DEFAULT_OPUS_MODEL.trim() : '';
414
- const sonnet = typeof profile.ANTHROPIC_DEFAULT_SONNET_MODEL === 'string' ? profile.ANTHROPIC_DEFAULT_SONNET_MODEL.trim() : '';
415
- const haiku = typeof profile.ANTHROPIC_DEFAULT_HAIKU_MODEL === 'string' ? profile.ANTHROPIC_DEFAULT_HAIKU_MODEL.trim() : '';
416
- const primary = typeof profile.ANTHROPIC_MODEL === 'string' ? profile.ANTHROPIC_MODEL.trim() : '';
415
+
416
+ // Strip [1m] suffix (case-insensitive) from model names; Claude Code appends it
417
+ // for 1M-context variants and it should not affect model matching/replacement.
418
+ const strip1m = (s) => (typeof s === 'string' ? s.replace(/\[1m\]/gi, '').trim() : '');
419
+
420
+ const opus = strip1m(profile.ANTHROPIC_DEFAULT_OPUS_MODEL);
421
+ const sonnet = strip1m(profile.ANTHROPIC_DEFAULT_SONNET_MODEL);
422
+ const haiku = strip1m(profile.ANTHROPIC_DEFAULT_HAIKU_MODEL);
423
+ const primary = strip1m(profile.ANTHROPIC_MODEL);
417
424
  const hasNew = !!(primary || opus || sonnet || haiku);
418
425
 
419
426
  let target = '';
420
427
  if (hasNew) {
421
- if (/opus/i.test(oldModel)) target = opus;
422
- else if (/sonnet/i.test(oldModel)) target = sonnet;
423
- else if (/haiku/i.test(oldModel)) target = haiku;
424
- else if (/fable/i.test(oldModel) || /mythos/i.test(oldModel)) target = primary; // 显式家族 → 主模型
425
- // 未识别家族:target 保持 '',下方返回 null(不替换、原样透传)
428
+ // Family-specific fields take precedence; fall back to ANTHROPIC_MODEL when
429
+ // the family field is empty/unset. ANTHROPIC_MODEL itself acts as the
430
+ // catch-all default for any unrecognized family (including third-party models
431
+ // like gpt-4o, deepseek-v4, K3/kimi, etc.).
432
+ if (/opus/i.test(oldModel)) target = opus || primary;
433
+ else if (/sonnet/i.test(oldModel)) target = sonnet || primary;
434
+ else if (/haiku/i.test(oldModel)) target = haiku || primary;
435
+ else if (/fable/i.test(oldModel) || /mythos/i.test(oldModel)) target = primary;
436
+ else target = primary; // unrecognized family → ANTHROPIC_MODEL catch-all
426
437
  } else if (typeof profile.activeModel === 'string') {
427
- target = profile.activeModel.trim(); // 旧数据整体替换语义
438
+ target = strip1m(profile.activeModel); // 旧数据整体替换语义
428
439
  }
429
440
 
430
- if (!target || target === oldModel) return null;
441
+ if (!target || target === strip1m(oldModel)) return null;
431
442
  return target;
432
443
  }
433
444
 
@@ -231,6 +231,14 @@ async function _spawnClaudeImpl(proxyPort, cwd, extraArgs = [], claudePath = nul
231
231
  }
232
232
 
233
233
  const env = { ...process.env };
234
+ // CodeFuse owns and versions the Claude binary used by `cfuse --ccv`. Keep
235
+ // that managed 2.1.x binary from invoking Claude Code's independent updater;
236
+ // upgrades must come from CodeFuse so the enterprise-approved version cannot
237
+ // be silently replaced during a CCV session.
238
+ const normalizedClaudePath = String(claudePath || '').replace(/\\/g, '/');
239
+ if (normalizedClaudePath.includes('/.codefuse/fuse/engine/bin/claude/')) {
240
+ env.DISABLE_AUTOUPDATER = '1';
241
+ }
234
242
  env.ANTHROPIC_BASE_URL = `http://127.0.0.1:${proxyPort}`;
235
243
  env.CCV_PROXY_MODE = '1'; // 告诉 interceptor.js 不要再启动 server
236
244
  env.CCV_LOG_DIR = LOG_DIR; // 让 fork 出的 Claude Code 进程找到同一份 profile.json 等资源