cc-viewer 1.6.281 → 1.6.282

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
@@ -19,11 +19,11 @@
19
19
  if (pick) document.documentElement.setAttribute('data-theme', pick);
20
20
  } catch {}
21
21
  </script>
22
- <script type="module" crossorigin src="/assets/index-CWPrqkgG.js"></script>
22
+ <script type="module" crossorigin src="/assets/index-Ce6a1edB.js"></script>
23
23
  <link rel="modulepreload" crossorigin href="/assets/vendor-antd-Cb6u7XOI.js">
24
24
  <link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-StjUr1Z2.js">
25
25
  <link rel="modulepreload" crossorigin href="/assets/vendor-mdxeditor-CN_kFxJv.js">
26
- <link rel="stylesheet" crossorigin href="/assets/index-wU9AHa7Y.css">
26
+ <link rel="stylesheet" crossorigin href="/assets/index-Dh0xUI2m.css">
27
27
  </head>
28
28
  <body>
29
29
  <div id="root"><div style="display:flex;align-items:center;justify-content:center;height:100vh;color:#888;font-family:system-ui">Loading...</div></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.6.281",
3
+ "version": "1.6.282",
4
4
  "description": "Claude Code Logger visualization management tool",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
package/server/i18n.js CHANGED
@@ -223,8 +223,8 @@ const i18nData = {
223
223
  "uk": "\nДля видалення виконайте: ccv --uninstall"
224
224
  },
225
225
  "cli.help": {
226
- "zh": "CC Viewer CLI — Claude Code 的 Web Viewer 前端\n\n用法: ccv [选项] [命令] [提示词]\n\n默认启动交互式会话 + Web Viewer,所有参数透传给 claude。\n\n参数:\n prompt 你的提示词\n\nCCV 专属选项:\n -SDK, --sdk 使用 Agent SDK 模式(无终端,结构化交互)\n -logger 安装/修复 Claude Code Hook\n --uninstall 移除 CC Viewer 集成\n --d --dangerously-skip-permissions 的快捷方式\n --ad --allow-dangerously-skip-permissions 的快捷方式\n --log-dir <path> 自定义 JSONL 日志存储目录\n --no-open 启动时不自动打开浏览器\n --user-name <name> 自定义显示名称\n --user-avatar <path|url> 自定义头像图片(文件路径或 URL)\n --usePassword[=<pwd>] 启用密码登录(远程访问需密码或 token;不带值则随机生成 8 位)\n\nClaude 选项(透传):\n -c, --continue 继续当前目录最近的对话\n -r, --resume [value] 通过会话 ID 恢复对话,或打开交互式选择器\n -p, --print 打印响应后退出(适用于管道)\n -d, --debug [filter] 启用调试模式\n -w, --worktree [name] 为此会话创建新的 git worktree\n --model <model> 当前会话使用的模型\n --effort <level> 努力级别 (low, medium, high, max)\n --dangerously-skip-permissions 跳过所有权限检查\n --allowedTools <tools...> 允许的工具列表\n --disallowedTools <tools...> 禁止的工具列表\n --system-prompt <prompt> 会话系统提示词\n --append-system-prompt <prompt> 追加系统提示词\n --mcp-config <configs...> 加载 MCP 服务器配置\n --permission-mode <mode> 权限模式\n --add-dir <directories...> 额外允许访问的目录\n --max-budget-usd <amount> API 调用最大预算(仅 --print)\n --output-format <format> 输出格式(仅 --print): text, json, stream-json\n --json-schema <schema> 结构化输出的 JSON Schema\n --verbose 详细模式\n -h, --help 显示帮助\n -v, --version 显示版本\n\n 更多选项请参考: claude --help\n\n示例:\n ccv 启动交互模式\n ccv -c 继续上次对话\n ccv -r 恢复对话\n ccv -p \"hello\" 打印模式\n ccv --model opus 指定模型\n ccv --d 跳过权限确认(快捷方式)\n ccv --ad 允许跳过权限(不立即启用,可通过 Shift+Tab 切换)\n ccv -c --model sonnet --effort max 组合使用\n ccv -SDK Agent SDK 模式(无终端)\n ccv -SDK --d SDK 模式 + 跳过权限\n ccv -logger 安装/修复 Hook\n ccv --log-dir ~/my-logs 使用自定义日志目录\n ccv --no-open 启动时不打开浏览器\n ccv --user-name \"Alice\" --user-avatar ./avatar.png 自定义用户身份\n ccv --log-dir /tmp/test --no-open 组合 CCV 选项",
227
- "en": "CC Viewer CLI — Web Viewer frontend for Claude Code\n\nUsage: ccv [options] [command] [prompt]\n\nStarts an interactive session with Web Viewer by default. All args are passed\nthrough to claude.\n\nArguments:\n prompt Your prompt\n\nCCV Options:\n -SDK, --sdk Use Agent SDK mode (no terminal, structured interaction)\n -logger Install/repair Claude Code hook\n --uninstall Remove CC Viewer integration\n --d Shortcut for --dangerously-skip-permissions\n --ad Shortcut for --allow-dangerously-skip-permissions\n --log-dir <path> Custom JSONL log storage directory\n --no-open Don't auto-open browser on startup\n --user-name <name> Custom display name for this session\n --user-avatar <path|url> Custom avatar image (file path or URL)\n --usePassword[=<pwd>] Enable password login (remote needs password or token; bare flag → random 8-char)\n\nClaude Options (passed through):\n -c, --continue Continue the most recent conversation\n -r, --resume [value] Resume a conversation by session ID, or open picker\n -p, --print Print response and exit (useful for pipes)\n -d, --debug [filter] Enable debug mode\n -w, --worktree [name] Create a new git worktree for this session\n --model <model> Model for the current session\n --effort <level> Effort level (low, medium, high, max)\n --dangerously-skip-permissions Bypass all permission checks\n --allowedTools <tools...> Allowed tool names\n --disallowedTools <tools...> Disallowed tool names\n --system-prompt <prompt> System prompt for the session\n --append-system-prompt <prompt> Append to default system prompt\n --mcp-config <configs...> Load MCP servers from JSON files or strings\n --permission-mode <mode> Permission mode for the session\n --add-dir <directories...> Additional directories to allow access to\n --max-budget-usd <amount> Max dollar amount for API calls (--print only)\n --output-format <format> Output format (--print only): text, json, stream-json\n --json-schema <schema> JSON Schema for structured output\n --verbose Enable verbose mode\n -h, --help Show help\n -v, --version Show version\n\n For all options, see: claude --help\n\nExamples:\n ccv Start interactive mode\n ccv -c Continue last conversation\n ccv -r Resume a conversation\n ccv -p \"hello\" Print mode\n ccv --model opus Specify model\n ccv --d Skip permission prompts (shortcut)\n ccv --ad Allow bypass mode (not active, toggle via Shift+Tab)\n ccv -c --model sonnet --effort max Combine options\n ccv -SDK Agent SDK mode (no terminal)\n ccv -SDK --d SDK mode + bypass permissions\n ccv -logger Install/repair hook\n ccv --log-dir ~/my-logs Use custom log directory\n ccv --no-open Start without opening browser\n ccv --user-name \"Alice\" --user-avatar ./avatar.png Custom user profile\n ccv --log-dir /tmp/test --no-open Combine CCV options",
226
+ "zh": "CC Viewer CLI — Claude Code 的 Web Viewer 前端\n\n用法: ccv [选项] [命令] [提示词]\n\n默认启动交互式会话 + Web Viewer,所有参数透传给 claude。\n\n参数:\n prompt 你的提示词\n\nCCV 专属选项:\n -SDK, --sdk 使用 Agent SDK 模式(无终端,结构化交互)\n -logger 安装/修复 Claude Code Hook\n --uninstall 移除 CC Viewer 集成\n --d --dangerously-skip-permissions 的快捷方式\n --ad --allow-dangerously-skip-permissions 的快捷方式\n --log-dir <path> 自定义 JSONL 日志存储目录\n --no-open 启动时不自动打开浏览器\n --user-name <name> 自定义显示名称\n --user-avatar <path|url> 自定义头像图片(文件路径或 URL)\n --usePassword[=<pwd>] 启用密码登录(远程访问需密码或 token;不带值则随机生成 6 位(2 字母 + 4 数字))\n\nClaude 选项(透传):\n -c, --continue 继续当前目录最近的对话\n -r, --resume [value] 通过会话 ID 恢复对话,或打开交互式选择器\n -p, --print 打印响应后退出(适用于管道)\n -d, --debug [filter] 启用调试模式\n -w, --worktree [name] 为此会话创建新的 git worktree\n --model <model> 当前会话使用的模型\n --effort <level> 努力级别 (low, medium, high, max)\n --dangerously-skip-permissions 跳过所有权限检查\n --allowedTools <tools...> 允许的工具列表\n --disallowedTools <tools...> 禁止的工具列表\n --system-prompt <prompt> 会话系统提示词\n --append-system-prompt <prompt> 追加系统提示词\n --mcp-config <configs...> 加载 MCP 服务器配置\n --permission-mode <mode> 权限模式\n --add-dir <directories...> 额外允许访问的目录\n --max-budget-usd <amount> API 调用最大预算(仅 --print)\n --output-format <format> 输出格式(仅 --print): text, json, stream-json\n --json-schema <schema> 结构化输出的 JSON Schema\n --verbose 详细模式\n -h, --help 显示帮助\n -v, --version 显示版本\n\n 更多选项请参考: claude --help\n\n示例:\n ccv 启动交互模式\n ccv -c 继续上次对话\n ccv -r 恢复对话\n ccv -p \"hello\" 打印模式\n ccv --model opus 指定模型\n ccv --d 跳过权限确认(快捷方式)\n ccv --ad 允许跳过权限(不立即启用,可通过 Shift+Tab 切换)\n ccv -c --model sonnet --effort max 组合使用\n ccv -SDK Agent SDK 模式(无终端)\n ccv -SDK --d SDK 模式 + 跳过权限\n ccv -logger 安装/修复 Hook\n ccv --log-dir ~/my-logs 使用自定义日志目录\n ccv --no-open 启动时不打开浏览器\n ccv --user-name \"Alice\" --user-avatar ./avatar.png 自定义用户身份\n ccv --log-dir /tmp/test --no-open 组合 CCV 选项",
227
+ "en": "CC Viewer CLI — Web Viewer frontend for Claude Code\n\nUsage: ccv [options] [command] [prompt]\n\nStarts an interactive session with Web Viewer by default. All args are passed\nthrough to claude.\n\nArguments:\n prompt Your prompt\n\nCCV Options:\n -SDK, --sdk Use Agent SDK mode (no terminal, structured interaction)\n -logger Install/repair Claude Code hook\n --uninstall Remove CC Viewer integration\n --d Shortcut for --dangerously-skip-permissions\n --ad Shortcut for --allow-dangerously-skip-permissions\n --log-dir <path> Custom JSONL log storage directory\n --no-open Don't auto-open browser on startup\n --user-name <name> Custom display name for this session\n --user-avatar <path|url> Custom avatar image (file path or URL)\n --usePassword[=<pwd>] Enable password login (remote needs password or token; bare flag → random 6-char: 2 letters + 4 digits)\n\nClaude Options (passed through):\n -c, --continue Continue the most recent conversation\n -r, --resume [value] Resume a conversation by session ID, or open picker\n -p, --print Print response and exit (useful for pipes)\n -d, --debug [filter] Enable debug mode\n -w, --worktree [name] Create a new git worktree for this session\n --model <model> Model for the current session\n --effort <level> Effort level (low, medium, high, max)\n --dangerously-skip-permissions Bypass all permission checks\n --allowedTools <tools...> Allowed tool names\n --disallowedTools <tools...> Disallowed tool names\n --system-prompt <prompt> System prompt for the session\n --append-system-prompt <prompt> Append to default system prompt\n --mcp-config <configs...> Load MCP servers from JSON files or strings\n --permission-mode <mode> Permission mode for the session\n --add-dir <directories...> Additional directories to allow access to\n --max-budget-usd <amount> Max dollar amount for API calls (--print only)\n --output-format <format> Output format (--print only): text, json, stream-json\n --json-schema <schema> JSON Schema for structured output\n --verbose Enable verbose mode\n -h, --help Show help\n -v, --version Show version\n\n For all options, see: claude --help\n\nExamples:\n ccv Start interactive mode\n ccv -c Continue last conversation\n ccv -r Resume a conversation\n ccv -p \"hello\" Print mode\n ccv --model opus Specify model\n ccv --d Skip permission prompts (shortcut)\n ccv --ad Allow bypass mode (not active, toggle via Shift+Tab)\n ccv -c --model sonnet --effort max Combine options\n ccv -SDK Agent SDK mode (no terminal)\n ccv -SDK --d SDK mode + bypass permissions\n ccv -logger Install/repair hook\n ccv --log-dir ~/my-logs Use custom log directory\n ccv --no-open Start without opening browser\n ccv --user-name \"Alice\" --user-avatar ./avatar.png Custom user profile\n ccv --log-dir /tmp/test --no-open Combine CCV options",
228
228
  "zh-TW": "CC Viewer CLI — Claude Code 的 Web Viewer 前端\n\n用法: ccv [選項] [命令] [提示詞]\n\n預設啟動互動式會話 + Web Viewer,所有參數透傳給 claude。\n\n參數:\n prompt 你的提示詞\n\nCCV 專屬選項:\n -SDK, --sdk 使用 Agent SDK 模式(無終端,結構化互動)\n -logger 安裝/修復 Claude Code Hook\n --uninstall 移除 CC Viewer 整合\n --d --dangerously-skip-permissions 的快捷方式\n --ad --allow-dangerously-skip-permissions 的快捷方式\n --log-dir <path> 自訂 JSONL 日誌儲存目錄\n --no-open 啟動時不自動開啟瀏覽器\n --user-name <name> 自訂顯示名稱\n --user-avatar <path|url> 自訂頭像圖片(檔案路徑或 URL)\n\nClaude 選項(透傳):\n -c, --continue 繼續當前目錄最近的對話\n -r, --resume [value] 透過會話 ID 恢復對話,或開啟互動式選擇器\n -p, --print 列印回應後退出(適用於管道)\n -d, --debug [filter] 啟用除錯模式\n -w, --worktree [name] 為此會話建立新的 git worktree\n --model <model> 當前會話使用的模型\n --effort <level> 努力級別 (low, medium, high, max)\n --dangerously-skip-permissions 跳過所有權限檢查\n --allowedTools <tools...> 允許的工具列表\n --disallowedTools <tools...> 禁止的工具列表\n --system-prompt <prompt> 會話系統提示詞\n --append-system-prompt <prompt> 追加系統提示詞\n --mcp-config <configs...> 載入 MCP 伺服器設定\n --permission-mode <mode> 權限模式\n --add-dir <directories...> 額外允許存取的目錄\n --max-budget-usd <amount> API 呼叫最大預算(僅 --print)\n --output-format <format> 輸出格式(僅 --print): text, json, stream-json\n --json-schema <schema> 結構化輸出的 JSON Schema\n --verbose 詳細模式\n -h, --help 顯示說明\n -v, --version 顯示版本\n\n 更多選項請參考: claude --help\n\n範例:\n ccv 啟動互動模式\n ccv -c 繼續上次對話\n ccv -r 恢復對話\n ccv -p \"hello\" 列印模式\n ccv --model opus 指定模型\n ccv --d 跳過權限確認(快捷方式)\n ccv --ad 允許跳過權限(不立即啟用,可透過 Shift+Tab 切換)\n ccv -c --model sonnet --effort max 組合使用\n ccv -SDK Agent SDK 模式(無終端)\n ccv -SDK --d SDK 模式 + 跳過權限\n ccv -logger 安裝/修復 Hook\n ccv --log-dir ~/my-logs 使用自訂日誌目錄\n ccv --no-open 啟動時不開啟瀏覽器\n ccv --user-name \"Alice\" --user-avatar ./avatar.png 自訂使用者身份\n ccv --log-dir /tmp/test --no-open 組合 CCV 選項"
229
229
  },
230
230
  "cli.cMode.notFound": {
@@ -14,28 +14,39 @@ import { join, dirname } from 'node:path';
14
14
  import { LOG_DIR } from '../../findcc.js';
15
15
  import { tFor, localeFromAcceptLanguage } from '../i18n.js';
16
16
 
17
- // A-Z + 0-9 (36 chars). No lowercase / ambiguous-pair stripping: kept simple and
18
- // matches the user-facing "uppercase letters + digits" spec. Login compares
19
- // case-insensitively (see routes/auth.js), so lowercase input still works.
20
- const PASSWORD_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
17
+ // 默认密码形状 = 前 2 位字母 + 其余数字(默认 6 2 字母 + 4 数字),好读好记又不易撞。
18
+ // 字母用大写 A-Z,数字 0-9;登录侧大小写不敏感(见 routes/auth.js),小写输入照样通过。
19
+ const PASSWORD_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // 26
20
+ const PASSWORD_DIGITS = '0123456789'; // 10
21
21
 
22
22
  /**
23
- * Cryptographically uniform password from A-Z0-9 (default 6 chars).
24
- * Rejection sampling discards bytes ≥ 252 (256 - 256%36) to avoid modulo bias.
23
+ * Cryptographically uniform pick of `count` chars from `chars`.
24
+ * Rejection sampling discards bytes ≥ (256 - 256%n) to avoid modulo bias.
25
25
  */
26
- export function generatePassword(len = 6) {
27
- const n = PASSWORD_CHARS.length; // 36
28
- const limit = 256 - (256 % n); // 252
26
+ function pickFrom(chars, count) {
27
+ const n = chars.length;
28
+ const limit = 256 - (256 % n);
29
29
  let out = '';
30
- while (out.length < len) {
31
- const buf = randomBytes(len * 2);
32
- for (let i = 0; i < buf.length && out.length < len; i++) {
33
- if (buf[i] < limit) out += PASSWORD_CHARS[buf[i] % n];
30
+ while (out.length < count) {
31
+ const buf = randomBytes(Math.max(1, count) * 2);
32
+ for (let i = 0; i < buf.length && out.length < count; i++) {
33
+ if (buf[i] < limit) out += chars[buf[i] % n];
34
34
  }
35
35
  }
36
36
  return out;
37
37
  }
38
38
 
39
+ /**
40
+ * Default password: leading letters + trailing digits, `len` chars total.
41
+ * At len=6 (the default) this is 2 letters + 4 digits, e.g. "AB1234".
42
+ * The letter prefix is fixed at 2 (or `len` when len<2); the rest are digits.
43
+ */
44
+ export function generatePassword(len = 6) {
45
+ const letters = Math.min(2, len);
46
+ const digits = Math.max(0, len - letters);
47
+ return pickFrom(PASSWORD_LETTERS, letters) + pickFrom(PASSWORD_DIGITS, digits);
48
+ }
49
+
39
50
  const DEFAULT_CONFIG = { enabled: false, password: '' };
40
51
 
41
52
  /**
@@ -31,8 +31,8 @@ export function readModelContextSize() {
31
31
  if (sizeMatch) {
32
32
  const num = parseInt(sizeMatch[1], 10);
33
33
  contextSize = sizeMatch[2] === 'm' ? num * 1000000 : num * 1000;
34
- } else if (/opus/i.test(lower)) {
35
- // Opus models default to 1M context
34
+ } else if (/opus|mythons/i.test(lower)) {
35
+ // Opus / mythons models default to 1M context
36
36
  contextSize = 1000000;
37
37
  }
38
38
  // Cache the base name → size mapping
@@ -61,8 +61,8 @@ export function getContextSizeForModel(apiModelName) {
61
61
  if (_startupModelBase && base === _startupModelBase) {
62
62
  return _startupContextSize;
63
63
  }
64
- // Opus always has 1M context; other unknown models default to 200K
65
- if (/opus/i.test(lower)) return 1000000;
64
+ // Opus / mythons always have 1M context; other unknown models default to 200K
65
+ if (/opus|mythons/i.test(lower)) return 1000000;
66
66
  return 200000;
67
67
  }
68
68
 
@@ -113,11 +113,15 @@ export function buildContextWindowEvent(usage, contextSize) {
113
113
  const inputTokens = (usage.input_tokens || 0) + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0);
114
114
  const outputTokens = usage.output_tokens || 0;
115
115
  const totalTokens = inputTokens + outputTokens;
116
- const usedPct = Math.round((totalTokens / contextSize) * 100);
116
+ // 自适应纠偏:真正的 200K 模型输入上下文(input+cache)不可能 > 200K(超了 API 拒收),
117
+ // 一旦越过整窗还判成 200K,必是 model 名识别错 → 升 1M,使 used_percentage / size 不再失真。
118
+ // 与 src/utils/helpers.js 的 adaptContextWindow 同一规则(此处服务端无法 import 前端模块,内联)。
119
+ const effectiveSize = (contextSize === 200000 && inputTokens > 200000) ? 1000000 : contextSize;
120
+ const usedPct = Math.round((totalTokens / effectiveSize) * 100);
117
121
  return {
118
122
  total_input_tokens: inputTokens,
119
123
  total_output_tokens: outputTokens,
120
- context_window_size: contextSize,
124
+ context_window_size: effectiveSize,
121
125
  current_usage: usage,
122
126
  used_percentage: usedPct,
123
127
  remaining_percentage: 100 - usedPct,
@@ -46,9 +46,10 @@ function buildState(deps, isLocal) {
46
46
 
47
47
  // ─── Login rate limiting (per source IP, in-memory) ───
48
48
  // Keyed on req.socket.remoteAddress (X-Forwarded-For is never parsed, so it can't
49
- // be spoofed over LAN). 20/60s is generous enough for NAT'd households yet trivial
50
- // against a 6-char A-Z0-9 space (36^62.2e9; case-insensitive doesn't shrink it since
51
- // the charset is uppercase-only). The Map is bounded to avoid growth.
49
+ // be spoofed over LAN). 20/60s is generous enough for NAT'd households yet far below
50
+ // the default password space (2 letters + 4 digits = 26^2·10^46.76e6; case-insensitive
51
+ // doesn't shrink it since the letters are uppercase-only): ~months to brute-force from a
52
+ // single IP. The Map is bounded to avoid growth.
52
53
  const RATE_WINDOW_MS = 60_000;
53
54
  const RATE_MAX = 20;
54
55
  const RATE_MAP_MAX = 1000;
@@ -271,8 +271,11 @@ async function events(req, res, parsedUrl, isLocal, deps) {
271
271
  const inputTokens = cw.total_input_tokens || 0;
272
272
  const outputTokens = cw.total_output_tokens || 0;
273
273
  const totalTokens = inputTokens + outputTokens;
274
- const usedPct = contextSize > 0 ? Math.round((totalTokens / contextSize) * 100) : 0;
275
- const data = { ...cw, context_window_size: contextSize, used_percentage: usedPct, remaining_percentage: 100 - usedPct };
274
+ // 自适应纠偏:与 buildContextWindowEvent 同规则 —— 200K 但输入上下文用量已越窗,
275
+ // 必是 model 名误判 1M,避免这条 fallback 与已纠偏的主路径产出不一致的血条。
276
+ const effectiveSize = (contextSize === 200000 && inputTokens > 200000) ? 1000000 : contextSize;
277
+ const usedPct = effectiveSize > 0 ? Math.round((totalTokens / effectiveSize) * 100) : 0;
278
+ const data = { ...cw, context_window_size: effectiveSize, used_percentage: usedPct, remaining_percentage: 100 - usedPct };
276
279
  res.write(`event: context_window\ndata: ${JSON.stringify(data)}\n\n`);
277
280
  }
278
281
  } catch { }