cc-viewer 1.6.281 → 1.6.283
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/README.md +8 -0
- package/concepts/ar/Tool-LSP.md +28 -0
- package/concepts/ar/Tool-Workflow.md +26 -0
- package/concepts/da/Tool-LSP.md +28 -0
- package/concepts/da/Tool-Workflow.md +26 -0
- package/concepts/de/Tool-LSP.md +28 -0
- package/concepts/de/Tool-Workflow.md +26 -0
- package/concepts/en/Tool-LSP.md +28 -0
- package/concepts/en/Tool-Workflow.md +26 -0
- package/concepts/es/Tool-LSP.md +28 -0
- package/concepts/es/Tool-Workflow.md +26 -0
- package/concepts/fr/Tool-LSP.md +28 -0
- package/concepts/fr/Tool-Workflow.md +26 -0
- package/concepts/it/Tool-LSP.md +28 -0
- package/concepts/it/Tool-Workflow.md +26 -0
- package/concepts/ja/Tool-LSP.md +28 -0
- package/concepts/ja/Tool-Workflow.md +26 -0
- package/concepts/ko/Tool-LSP.md +28 -0
- package/concepts/ko/Tool-Workflow.md +26 -0
- package/concepts/no/Tool-LSP.md +28 -0
- package/concepts/no/Tool-Workflow.md +26 -0
- package/concepts/pl/Tool-LSP.md +28 -0
- package/concepts/pl/Tool-Workflow.md +26 -0
- package/concepts/pt-BR/Tool-LSP.md +28 -0
- package/concepts/pt-BR/Tool-Workflow.md +26 -0
- package/concepts/ru/Tool-LSP.md +28 -0
- package/concepts/ru/Tool-Workflow.md +26 -0
- package/concepts/th/Tool-LSP.md +28 -0
- package/concepts/th/Tool-Workflow.md +26 -0
- package/concepts/tr/Tool-LSP.md +28 -0
- package/concepts/tr/Tool-Workflow.md +26 -0
- package/concepts/uk/Tool-LSP.md +28 -0
- package/concepts/uk/Tool-Workflow.md +26 -0
- package/concepts/zh/Tool-LSP.md +28 -0
- package/concepts/zh/Tool-Workflow.md +26 -0
- package/concepts/zh-TW/Tool-LSP.md +28 -0
- package/concepts/zh-TW/Tool-Workflow.md +26 -0
- package/dist/assets/App-ByUIC-yr.css +1 -0
- package/dist/assets/App-C-jwJCC7.js +1 -0
- package/dist/assets/{MdxEditorPanel-DZNsukah.js → MdxEditorPanel-DcHac_1Y.js} +1 -1
- package/dist/assets/Mobile-D4IM7nzP.js +1 -0
- package/dist/assets/index-BmzQMRa6.js +2 -0
- package/dist/assets/index-Dh0xUI2m.css +1 -0
- package/dist/assets/seqResourceLoaders-Bwq1pWpw.css +41 -0
- package/dist/assets/seqResourceLoaders-soK06gBr.js +2 -0
- package/dist/index.html +2 -2
- package/package.json +3 -2
- package/server/_paths.js +2 -0
- package/server/i18n.js +2 -2
- package/server/lib/auth.js +24 -13
- package/server/lib/context-watcher.js +10 -6
- package/server/lib/proxy-env.js +16 -1
- package/server/lib/ultra-agents-api.js +113 -0
- package/server/proxy.js +23 -15
- package/server/routes/auth.js +4 -3
- package/server/routes/events.js +5 -2
- package/server/routes/ultra-agents.js +20 -0
- package/server/server.js +2 -0
- package/ultraAgents/README.md +62 -0
- package/ultraAgents/code-expert.json +45 -0
- package/ultraAgents/research-expert.json +45 -0
- package/dist/assets/App-C9RgVVSe.js +0 -1
- package/dist/assets/App-Ce0JeS8W.css +0 -1
- package/dist/assets/Mobile-DYlkN9q2.js +0 -1
- package/dist/assets/index-CWPrqkgG.js +0 -2
- package/dist/assets/index-wU9AHa7Y.css +0 -1
- package/dist/assets/seqResourceLoaders-CS74SVvL.js +0 -2
- package/dist/assets/seqResourceLoaders-qVFYq-6q.css +0 -41
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-
|
|
22
|
+
<script type="module" crossorigin src="/assets/index-BmzQMRa6.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-
|
|
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.
|
|
3
|
+
"version": "1.6.283",
|
|
4
4
|
"description": "Claude Code Logger visualization management tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "server.js",
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"interceptor.js",
|
|
57
57
|
"server/",
|
|
58
58
|
"plugins/",
|
|
59
|
-
"concepts/"
|
|
59
|
+
"concepts/",
|
|
60
|
+
"ultraAgents/"
|
|
60
61
|
],
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"@codemirror/lang-cpp": "^6.0.3",
|
package/server/_paths.js
CHANGED
|
@@ -31,5 +31,7 @@ export const PUBLIC_DIR = join(PACKAGE_ROOT, 'public');
|
|
|
31
31
|
export const CONCEPTS_DIR = join(PACKAGE_ROOT, 'concepts');
|
|
32
32
|
/** Bundled plugin 目录(plugin-loader 启动时扫这里) */
|
|
33
33
|
export const PLUGINS_DIR = join(PACKAGE_ROOT, 'plugins');
|
|
34
|
+
/** 随包发布的 ultraplan 预设专家目录(ultraAgents/*.json,/api/ultra-agents 扫这里) */
|
|
35
|
+
export const ULTRA_AGENTS_DIR = join(PACKAGE_ROOT, 'ultraAgents');
|
|
34
36
|
/** cc-viewer 自己的 package.json(updater/server.js 读 version) */
|
|
35
37
|
export const PACKAGE_JSON = join(PACKAGE_ROOT, 'package.json');
|
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;不带值则随机生成
|
|
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
|
|
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": {
|
package/server/lib/auth.js
CHANGED
|
@@ -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
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
const
|
|
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
|
|
24
|
-
* Rejection sampling discards bytes ≥
|
|
23
|
+
* Cryptographically uniform pick of `count` chars from `chars`.
|
|
24
|
+
* Rejection sampling discards bytes ≥ (256 - 256%n) to avoid modulo bias.
|
|
25
25
|
*/
|
|
26
|
-
|
|
27
|
-
const n =
|
|
28
|
-
const limit = 256 - (256 % n);
|
|
26
|
+
function pickFrom(chars, count) {
|
|
27
|
+
const n = chars.length;
|
|
28
|
+
const limit = 256 - (256 % n);
|
|
29
29
|
let out = '';
|
|
30
|
-
while (out.length <
|
|
31
|
-
const buf = randomBytes(
|
|
32
|
-
for (let i = 0; i < buf.length && out.length <
|
|
33
|
-
if (buf[i] < limit) out +=
|
|
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
|
|
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
|
-
|
|
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:
|
|
124
|
+
context_window_size: effectiveSize,
|
|
121
125
|
current_usage: usage,
|
|
122
126
|
used_percentage: usedPct,
|
|
123
127
|
remaining_percentage: 100 - usedPct,
|
package/server/lib/proxy-env.js
CHANGED
|
@@ -10,14 +10,29 @@ export function resolveProxyConfig(env = process.env) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// 关键坑位(Node 26 起的回归):代理转发上游用的是 Node 内置全局 fetch,背后是 Node 自带的
|
|
14
|
+
// 那份 undici。Node ≤25 时它与 userland undici 包共享同一个 global dispatcher(Symbol.for
|
|
15
|
+
// 同键),所以单调 setGlobalDispatcher 也能让转发请求走代理——这也是为何旧代码一直好用。
|
|
16
|
+
// 实测 Node 26 起两份 undici 不再共享 global dispatcher,单靠 setGlobalDispatcher,转发请求
|
|
17
|
+
// 读不到 http_proxy/https_proxy,会直连 api.anthropic.com 绕过用户的网络代理。
|
|
18
|
+
// 解法:把这里构造的 EnvHttpProxyAgent 显式保存下来,由 proxy 转发处作为 fetch 的 dispatcher
|
|
19
|
+
// 选项传入(内置 fetch 接受 userland undici 的 dispatcher 实例,各 Node 版本通用)。
|
|
20
|
+
let _proxyDispatcher = null;
|
|
21
|
+
|
|
13
22
|
export function setupProxyEnv() {
|
|
14
23
|
const { httpProxy, httpsProxy, noProxy } = resolveProxyConfig();
|
|
15
24
|
if (!httpProxy && !httpsProxy) return;
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
_proxyDispatcher = new EnvHttpProxyAgent({ httpProxy, httpsProxy, noProxy });
|
|
27
|
+
setGlobalDispatcher(_proxyDispatcher); // 仍保留:覆盖直接 import 'undici' 的 fetch 调用路径
|
|
18
28
|
if (process.env.CCV_DEBUG) {
|
|
19
29
|
console.error(`[CC Viewer] HTTP proxy: http=${httpProxy || '(none)'}, https=${httpsProxy || '(none)'}${noProxy ? `, no_proxy=${noProxy}` : ''}`);
|
|
20
30
|
}
|
|
21
31
|
}
|
|
22
32
|
|
|
33
|
+
// 返回供"内置全局 fetch"使用的代理 dispatcher;无代理配置时返回 null(调用方不传即直连)。
|
|
34
|
+
export function getProxyDispatcher() {
|
|
35
|
+
return _proxyDispatcher;
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
setupProxyEnv();
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// 随包发布的 ultraplan「预设专家」加载器 —— 扫 `ultraAgents/*.json`,校验后返回给前端。
|
|
2
|
+
// 纯 Node 实现,无 React / 浏览器依赖,方便 test/ultra-agents-api.test.js 直接 import。
|
|
3
|
+
//
|
|
4
|
+
// 安全模型:只读 cc-viewer 包自带的 ULTRA_AGENTS_DIR,文件名由 readdirSync 枚举(不接受
|
|
5
|
+
// 请求参数 / 项目目录),故无路径穿越面。文件随包发布属可信内容,但仍做防御性体量限制
|
|
6
|
+
// (单文件 ≤256KB、有效专家 ≤100)以免被异常大文件 / 海量文件拖垮端点。
|
|
7
|
+
|
|
8
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { ULTRA_AGENTS_DIR } from '../_paths.js';
|
|
11
|
+
|
|
12
|
+
// 单文件体量上限:预设专家正文再长也远小于 256KB,超出视为异常文件,跳过。
|
|
13
|
+
const MAX_FILE_BYTES = 256 * 1024;
|
|
14
|
+
// 有效专家数量上限:防止目录被塞入海量文件导致响应体过大 / 事件循环阻塞。
|
|
15
|
+
const MAX_AGENTS = 100;
|
|
16
|
+
// agent id 安全字符集:字母数字 . _ -(不含冒号,agent id 不是 plugin 名)。
|
|
17
|
+
// id 仅作去重键与前端 key,不参与拼路径,但仍约束格式以保持数据干净。
|
|
18
|
+
const SAFE_ID = /^[A-Za-z0-9._-]+$/;
|
|
19
|
+
|
|
20
|
+
export function validateAgentId(id) {
|
|
21
|
+
if (typeof id !== 'string' || !id) return false;
|
|
22
|
+
if (id.length > 200) return false;
|
|
23
|
+
if (id.startsWith('.')) return false;
|
|
24
|
+
if (id.includes('..') || id.includes('/') || id.includes('\\') || id.includes('\0')) return false;
|
|
25
|
+
return SAFE_ID.test(id);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 文本字段校验:支持「纯字符串」或「{lang: str} 本地化对象」两种合法形态。
|
|
29
|
+
// · 字符串 → trim 后非空即可;
|
|
30
|
+
// · 普通对象(非数组)→ 至少一个值是非空字符串;
|
|
31
|
+
// · 其它(数组 / 数字 / 布尔 / null / 空对象 / 全空串对象)→ 非法。
|
|
32
|
+
// title / description 在 JSON 协议层内联本地化(可为对象),前端按当前语言解析;content 为单语言字符串。
|
|
33
|
+
export function isValidTextField(v) {
|
|
34
|
+
if (typeof v === 'string') return v.trim().length > 0;
|
|
35
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
36
|
+
return Object.values(v).some(x => typeof x === 'string' && x.trim().length > 0);
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// content 专用校验:content 是单语言字符串(不内联本地化,见 README / resolveLocalized 注释),
|
|
42
|
+
// 必须为 trim 后非空的字符串。对象/数组/标量一律非法——否则对象会被前端当字符串渲染成
|
|
43
|
+
// `[object Object]`(预览框 / 存入编辑器)。与 isValidTextField 分开,正是为了不放行本地化对象。
|
|
44
|
+
export function isNonEmptyString(v) {
|
|
45
|
+
return typeof v === 'string' && v.trim().length > 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 扫描 dir 下所有 *.json,解析 + 校验 + 去重,返回 [{ id, title, description, content }]。
|
|
49
|
+
// title/description 原样返回(可能是 {lang: str} 本地化对象),由前端 resolveLocalized 按当前语言解析;
|
|
50
|
+
// content 为单语言字符串、原样返回。
|
|
51
|
+
// 其它字段(如 version)一律忽略:version 为前向兼容标记、当前不读不校验,
|
|
52
|
+
// 未知 key 不报错也不透传,保证前向兼容。
|
|
53
|
+
// 任一文件损坏 / 不合法只 console.warn 跳过,绝不抛错中断整体加载。
|
|
54
|
+
export function listUltraAgents({ dir = ULTRA_AGENTS_DIR } = {}) {
|
|
55
|
+
if (!existsSync(dir)) return [];
|
|
56
|
+
const out = [];
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
let entries;
|
|
59
|
+
try {
|
|
60
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.warn('[ultra-agents] 读取目录失败,返回空列表:', err?.message);
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
// 文件名排序,保证「先到先得」去重与跨平台输出顺序稳定。
|
|
66
|
+
const files = entries
|
|
67
|
+
.filter(e => e.isFile() && e.name.toLowerCase().endsWith('.json'))
|
|
68
|
+
.map(e => e.name)
|
|
69
|
+
.sort();
|
|
70
|
+
|
|
71
|
+
for (const name of files) {
|
|
72
|
+
if (out.length >= MAX_AGENTS) {
|
|
73
|
+
console.warn(`[ultra-agents] 预设专家数超过上限 ${MAX_AGENTS},其余忽略`);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
const filePath = join(dir, name);
|
|
77
|
+
try {
|
|
78
|
+
if (statSync(filePath).size > MAX_FILE_BYTES) {
|
|
79
|
+
console.warn(`[ultra-agents] 文件过大已跳过: ${name}`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
83
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
84
|
+
console.warn(`[ultra-agents] 非对象 JSON 已跳过: ${name}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!validateAgentId(parsed.id)) {
|
|
88
|
+
console.warn(`[ultra-agents] id 非法已跳过: ${name}`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// title 可内联本地化(字符串或对象);content 必须是单语言字符串。
|
|
92
|
+
if (!isValidTextField(parsed.title) || !isNonEmptyString(parsed.content)) {
|
|
93
|
+
console.warn(`[ultra-agents] title/content 缺失或非法已跳过: ${name}`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (seen.has(parsed.id)) {
|
|
97
|
+
console.warn(`[ultra-agents] id 重复已跳过(先到先得): ${parsed.id} (${name})`);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
seen.add(parsed.id);
|
|
101
|
+
out.push({
|
|
102
|
+
id: parsed.id,
|
|
103
|
+
title: parsed.title,
|
|
104
|
+
// description 可选;非法 / 缺失时给空串,前端 resolveLocalized 容错。
|
|
105
|
+
description: isValidTextField(parsed.description) ? parsed.description : '',
|
|
106
|
+
content: parsed.content,
|
|
107
|
+
});
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.warn(`[ultra-agents] 解析失败已跳过: ${name} (${err?.message})`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
package/server/proxy.js
CHANGED
|
@@ -6,26 +6,26 @@ import { homedir } from 'node:os';
|
|
|
6
6
|
import * as interceptor from './interceptor.js';
|
|
7
7
|
import { setupInterceptor } from './interceptor.js';
|
|
8
8
|
import { extractApiErrorMessage, formatProxyRequestError } from './lib/proxy-errors.js';
|
|
9
|
+
import { getProxyDispatcher } from './lib/proxy-env.js';
|
|
9
10
|
import { getClaudeConfigDir } from '../findcc.js';
|
|
10
11
|
|
|
11
12
|
// Setup interceptor to patch fetch
|
|
12
13
|
setupInterceptor();
|
|
13
14
|
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
15
|
+
// 强制上游返回未压缩响应,取代仅剥 zstd 的旧策略。
|
|
16
|
+
// 原因:链路中的网关/代理(典型是本地 MITM 网络代理)可能把上游的压缩 body 原样透传,
|
|
17
|
+
// 却把 content-encoding 响应头剥掉。undici 看不到 content-encoding 就不会解压,于是把一坨
|
|
18
|
+
// gzip 字节当明文交回;本代理再把它当 SSE 透传给 Claude CLI →
|
|
19
|
+
// "API returned an empty or malformed response (HTTP 200)"。
|
|
20
|
+
// 让上游直接不压缩,整条链路就没有可被剥离/错配的 content-encoding,从根上消除这类问题。
|
|
21
|
+
export function forceIdentityAcceptEncoding(headers) {
|
|
18
22
|
if (!headers) return headers;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.map(s => s.trim())
|
|
26
|
-
.filter(s => s && !/^zstd(\s*;.*)?$/i.test(s))
|
|
27
|
-
.join(', ');
|
|
28
|
-
return { ...headers, [key]: filtered || 'gzip, deflate, br' };
|
|
23
|
+
const out = {};
|
|
24
|
+
for (const k of Object.keys(headers)) {
|
|
25
|
+
if (k.toLowerCase() !== 'accept-encoding') out[k] = headers[k];
|
|
26
|
+
}
|
|
27
|
+
out['accept-encoding'] = 'identity';
|
|
28
|
+
return out;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// 代理会改写请求 body(interceptor 的模型替换 JSON.parse→改 model→JSON.stringify),
|
|
@@ -97,7 +97,7 @@ export function startProxy() {
|
|
|
97
97
|
// Convert incoming headers
|
|
98
98
|
let headers = { ...req.headers };
|
|
99
99
|
delete headers.host; // Let fetch set the host
|
|
100
|
-
headers =
|
|
100
|
+
headers = forceIdentityAcceptEncoding(headers); // 让上游不压缩,规避网关剥 content-encoding 头导致的 body 错配
|
|
101
101
|
headers = stripContentLengthHeader(headers); // body 会被改写,旧 content-length 必须丢弃
|
|
102
102
|
|
|
103
103
|
const buffers = [];
|
|
@@ -119,6 +119,14 @@ export function startProxy() {
|
|
|
119
119
|
fetchOptions.body = body;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// 走用户的网络代理:Node 内置全局 fetch 既不读 http_proxy/https_proxy,也看不到
|
|
123
|
+
// userland undici 的 setGlobalDispatcher,必须把代理 dispatcher 显式传进来,否则
|
|
124
|
+
// 上游请求会绕过代理直连 api.anthropic.com(详见 lib/proxy-env.js 注释)。
|
|
125
|
+
const proxyDispatcher = getProxyDispatcher();
|
|
126
|
+
if (proxyDispatcher) {
|
|
127
|
+
fetchOptions.dispatcher = proxyDispatcher;
|
|
128
|
+
}
|
|
129
|
+
|
|
122
130
|
// 拼接完整 URL,保留 originalBaseUrl 中的路径前缀
|
|
123
131
|
const cleanBase = originalBaseUrl.endsWith('/') ? originalBaseUrl.slice(0, -1) : originalBaseUrl;
|
|
124
132
|
const cleanReq = req.url.startsWith('/') ? req.url.slice(1) : req.url;
|
package/server/routes/auth.js
CHANGED
|
@@ -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
|
|
50
|
-
//
|
|
51
|
-
// the
|
|
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^4 ≈ 6.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;
|
package/server/routes/events.js
CHANGED
|
@@ -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
|
-
|
|
275
|
-
|
|
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 { }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ultraplan 预设专家路由 —— 枚举随包发布的 ultraAgents/*.json 供前端「添加预设专家」弹窗使用。
|
|
2
|
+
// 无参数、不读项目目录:listUltraAgents() 默认 dir = ULTRA_AGENTS_DIR(包内置目录),
|
|
3
|
+
// 不接受任何 query/body,杜绝路径穿越与越权读取被查看项目的风险。
|
|
4
|
+
import { listUltraAgents } from '../lib/ultra-agents-api.js';
|
|
5
|
+
|
|
6
|
+
async function ultraAgentsList(req, res) {
|
|
7
|
+
try {
|
|
8
|
+
const agents = listUltraAgents();
|
|
9
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10
|
+
res.end(JSON.stringify({ ok: true, agents }));
|
|
11
|
+
} catch (err) {
|
|
12
|
+
console.error('[api/ultra-agents]', err);
|
|
13
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
14
|
+
res.end(JSON.stringify({ error: 'internal_error' }));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ultraAgentsRoutes = [
|
|
19
|
+
{ method: 'GET', match: 'exact', path: '/api/ultra-agents', handler: ultraAgentsList },
|
|
20
|
+
];
|
package/server/server.js
CHANGED
|
@@ -21,6 +21,7 @@ import { pluginsRoutes } from './routes/plugins.js';
|
|
|
21
21
|
import { logsRoutes } from './routes/logs.js';
|
|
22
22
|
import { voicePackRoutes } from './routes/voice-pack.js';
|
|
23
23
|
import { skillsRoutes } from './routes/skills.js';
|
|
24
|
+
import { ultraAgentsRoutes } from './routes/ultra-agents.js';
|
|
24
25
|
import { filesContentRoutes } from './routes/files-content.js';
|
|
25
26
|
import { filesFsRoutes } from './routes/files-fs.js';
|
|
26
27
|
import { workspacesRoutes } from './routes/workspaces.js';
|
|
@@ -510,6 +511,7 @@ const _routes = [
|
|
|
510
511
|
...logsRoutes,
|
|
511
512
|
...voicePackRoutes,
|
|
512
513
|
...skillsRoutes,
|
|
514
|
+
...ultraAgentsRoutes,
|
|
513
515
|
...filesContentRoutes,
|
|
514
516
|
...filesFsRoutes,
|
|
515
517
|
...workspacesRoutes,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# ultraAgents — 预设专家 (Preset ultraplan experts)
|
|
2
|
+
|
|
3
|
+
本目录随 cc-viewer 包发布。其中每个 `*.json` 文件定义一个「预设专家」,会出现在
|
|
4
|
+
**UltraPlan → 自定义专家编辑器 → 「载入模版」** 弹窗中,供用户选中后将名称与内容
|
|
5
|
+
一键载入编辑面板(再按需改写并另存为自己的自定义专家)。
|
|
6
|
+
|
|
7
|
+
> 加载入口:服务端 `GET /api/ultra-agents`(只读本目录、无参数),实现见
|
|
8
|
+
> `server/lib/ultra-agents-api.js`。本目录中的非 `*.json` 文件(如本 README)会被忽略。
|
|
9
|
+
|
|
10
|
+
## JSON 格式
|
|
11
|
+
|
|
12
|
+
```jsonc
|
|
13
|
+
{
|
|
14
|
+
"id": "code-expert", // 必填。唯一标识,仅 [A-Za-z0-9._-]、长度 ≤200、不以 . 开头。
|
|
15
|
+
// 仅作去重键(同 id 取文件名排序靠前者),不参与拼路径。
|
|
16
|
+
"version": 1, // 可选。前向兼容标记,当前加载器忽略(不读不校验)。
|
|
17
|
+
"title": { "zh": "代码专家", "en": "Code Expert" }, // 必填。专家名称,内联本地化,见下。
|
|
18
|
+
"description": { "zh": "资深工程师…", "en": "Senior engineer…" }, // 可选。一句话描述,内联本地化。
|
|
19
|
+
"content": "<system-reminder>\n…\n</system-reminder>" // 必填。单语言正文,见下「content」。
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### title / description:在 JSON 协议层内联本地化
|
|
24
|
+
|
|
25
|
+
`title` 与 `description` 两个字段都支持两种写法,**本地化在 JSON 协议层内联完成**(不依赖外部 i18n):
|
|
26
|
+
|
|
27
|
+
- **纯字符串**:所有语言都用同一份文本。
|
|
28
|
+
- **本地化对象** `{ "zh": "…", "en": "…", "zh-TW": "…" }`:前端按当前界面语言解析,
|
|
29
|
+
回退顺序为 **精确语言 → 去区域的主语言(`zh-TW`→`zh`、`pt-BR`→`pt`)→ `en` → `zh`
|
|
30
|
+
→ 首个非空值**(解析逻辑见 `src/utils/resolveLocalized.js`)。
|
|
31
|
+
|
|
32
|
+
因此一个文件即可覆盖任意多种语言;未列出的界面语言走上述回退。
|
|
33
|
+
|
|
34
|
+
### content:单语言
|
|
35
|
+
|
|
36
|
+
`content` 是**单语言字符串**(不做本地化)。载入并保存后,发送给 Claude Code 时被当作 ultraplan
|
|
37
|
+
的作用域指令;若 `content`(trim 后)以 `<system-reminder>` 开头,则原样使用、**不会被二次包裹**
|
|
38
|
+
(见 `buildCustomTemplate`,`src/utils/ultraplanTemplates.js`)。因此预设 `content` 建议以
|
|
39
|
+
`<system-reminder>`、`[SCOPED INSTRUCTION]` 风格书写。
|
|
40
|
+
|
|
41
|
+
> 内置 `code-expert` / `research-expert` 的 `content` **直接取自**
|
|
42
|
+
> `src/utils/ultraplanTemplates.js` 的 `ULTRAPLAN_VARIANTS.codeExpert` / `researchExpert`,
|
|
43
|
+
> 并由 `test/ultra-agents-api.test.js` 钉死逐字节一致——改正文请改那个源文件后重新生成本目录 JSON,
|
|
44
|
+
> 不要在此手写另一份。
|
|
45
|
+
|
|
46
|
+
## 校验与限制
|
|
47
|
+
|
|
48
|
+
加载时对每个文件做防御性校验,不合法者仅 `console.warn` 跳过、不影响其它文件:
|
|
49
|
+
|
|
50
|
+
- 必须是合法 JSON 的**普通对象**(非数组/标量)。
|
|
51
|
+
- `id` 通过上述规则;`title`、`content` 为「非空字符串」或「至少含一个非空字符串值的对象」。
|
|
52
|
+
- 单文件 **≤ 256KB**,超出跳过;有效专家 **≤ 100** 个,超出忽略。
|
|
53
|
+
- `description` 缺失或非法时按空串处理。
|
|
54
|
+
|
|
55
|
+
## 现有 demo
|
|
56
|
+
|
|
57
|
+
| 文件 | 专家 | `title` / `description` | `content` 来源 |
|
|
58
|
+
| --- | --- | --- | --- |
|
|
59
|
+
| `code-expert.json` | 代码专家 / Code Expert | 内联本地化(全 18 语言) | `ULTRAPLAN_VARIANTS.codeExpert` |
|
|
60
|
+
| `research-expert.json` | 调研专家 / Research Expert | 内联本地化(全 18 语言) | `ULTRAPLAN_VARIANTS.researchExpert` |
|
|
61
|
+
|
|
62
|
+
新增预设:在本目录放一个新的 `*.json`(建议文件名与 `id` 一致),重启/刷新即可在弹窗中看到。
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "code-expert",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"title": {
|
|
5
|
+
"zh": "代码专家",
|
|
6
|
+
"en": "Code Expert",
|
|
7
|
+
"zh-TW": "程式碼專家",
|
|
8
|
+
"ko": "코드 전문가",
|
|
9
|
+
"ja": "コード専門家",
|
|
10
|
+
"de": "Code-Experte",
|
|
11
|
+
"es": "Experto en código",
|
|
12
|
+
"fr": "Expert en code",
|
|
13
|
+
"it": "Esperto di codice",
|
|
14
|
+
"da": "Kodeekspert",
|
|
15
|
+
"pl": "Ekspert ds. kodu",
|
|
16
|
+
"ru": "Эксперт по коду",
|
|
17
|
+
"ar": "خبير الأكواد",
|
|
18
|
+
"no": "Kodeekspert",
|
|
19
|
+
"pt-BR": "Especialista em código",
|
|
20
|
+
"th": "ผู้เชี่ยวชาญด้านโค้ด",
|
|
21
|
+
"tr": "Kod uzmanı",
|
|
22
|
+
"uk": "Експерт із коду"
|
|
23
|
+
},
|
|
24
|
+
"description": {
|
|
25
|
+
"zh": "资深工程师:多智能体探索 + 计划评审 + 高质量实现,专注代码审查、重构、性能与可维护性。",
|
|
26
|
+
"en": "Senior engineer: multi-agent exploration, plan review and high-quality implementation — code review, refactor, performance, maintainability.",
|
|
27
|
+
"zh-TW": "資深工程師:多智能體探索 + 計畫評審 + 高品質實作,專注程式碼審查、重構、效能與可維護性。",
|
|
28
|
+
"ko": "시니어 엔지니어: 멀티 에이전트 탐색 + 계획 검토 + 고품질 구현 — 코드 리뷰, 리팩터링, 성능, 유지보수성에 집중.",
|
|
29
|
+
"ja": "シニアエンジニア:マルチエージェント探索 + 計画レビュー + 高品質な実装 — コードレビュー、リファクタリング、性能、保守性に注力。",
|
|
30
|
+
"de": "Senior-Ingenieur: Multi-Agent-Exploration, Planprüfung und hochwertige Umsetzung – Code-Review, Refactoring, Performance, Wartbarkeit.",
|
|
31
|
+
"es": "Ingeniero sénior: exploración multiagente, revisión de planes e implementación de alta calidad: revisión de código, refactorización, rendimiento y mantenibilidad.",
|
|
32
|
+
"fr": "Ingénieur senior : exploration multi-agents, revue de plan et implémentation de haute qualité — revue de code, refactorisation, performance, maintenabilité.",
|
|
33
|
+
"it": "Ingegnere senior: esplorazione multi-agente, revisione del piano e implementazione di alta qualità — revisione del codice, refactoring, prestazioni, manutenibilità.",
|
|
34
|
+
"da": "Senioringeniør: multi-agent-udforskning, plangennemgang og implementering af høj kvalitet – kodegennemgang, refaktorering, ydeevne, vedligeholdelse.",
|
|
35
|
+
"pl": "Starszy inżynier: eksploracja wieloagentowa, przegląd planu i wysokiej jakości implementacja — przegląd kodu, refaktoryzacja, wydajność, łatwość utrzymania.",
|
|
36
|
+
"ru": "Старший инженер: многоагентное исследование, ревью плана и качественная реализация — ревью кода, рефакторинг, производительность, поддерживаемость.",
|
|
37
|
+
"ar": "مهندس أول: استكشاف متعدد الوكلاء، ومراجعة الخطة، وتنفيذ عالي الجودة — مراجعة الشيفرة وإعادة الهيكلة والأداء وقابلية الصيانة.",
|
|
38
|
+
"no": "Senioringeniør: multiagent-utforsking, plangjennomgang og implementering av høy kvalitet – kodegjennomgang, refaktorering, ytelse, vedlikeholdbarhet.",
|
|
39
|
+
"pt-BR": "Engenheiro sênior: exploração multiagente, revisão de plano e implementação de alta qualidade — revisão de código, refatoração, desempenho e manutenibilidade.",
|
|
40
|
+
"th": "วิศวกรอาวุโส: การสำรวจแบบหลายเอเจนต์ + การรีวิวแผน + การพัฒนาคุณภาพสูง — เน้นการรีวิวโค้ด รีแฟกเตอร์ ประสิทธิภาพ และการบำรุงรักษา",
|
|
41
|
+
"tr": "Kıdemli mühendis: çoklu ajan keşfi, plan incelemesi ve yüksek kaliteli uygulama — kod incelemesi, yeniden düzenleme, performans, sürdürülebilirlik.",
|
|
42
|
+
"uk": "Senior-інженер: багатоагентне дослідження, рев'ю плану та якісна реалізація — рев'ю коду, рефакторинг, продуктивність, супровід."
|
|
43
|
+
},
|
|
44
|
+
"content": "<system-reminder>\n[SCOPED INSTRUCTION] The following instructions apply only to the next 1–3 interactions. Once the task is complete, these instructions should gradually decrease in priority and no longer affect subsequent interactions. You should be adept at utilizing tools such as `AskUserQuestion`, `EnterPlanMode`, and `TeamCreate`, rather than relying solely on plain text processing.\n\nPre-requisite: Use `AskUserQuestion` to clarify user intent whenever the request is ambiguous (target element, interaction style, scope of platforms, etc.). Skip only if the intent is unambiguous.\n\nLeverage a multi-agent exploration mechanism to formulate a highly detailed implementation plan.\n\nInstructions:\n1. Use the `Agent` tool to spawn parallel agents that simultaneously explore different aspects of the codebase:\n- If necessary, assign a preliminary researcher to use the `webSearch` tool to first investigate cutting-edge solutions in the relevant industry domain;\n- One agent responsible for understanding the relevant existing code and architecture;\n- One agent responsible for identifying all files that need to be modified;\n- One agent responsible for identifying potential risks, edge cases, and dependencies;\n- You may add other roles or deploy additional agents beyond the three listed above; the maximum number of concurrently dispatched agents is 5.\n\n2. Synthesize the findings from all agents into a detailed, step-by-step implementation plan.\n\n3. Use the `Agent` tool to spawn 2-3 review agents that examine the plan from different perspectives, checking for missing steps, potential risks, or corresponding mitigation strategies.\n\n4. Integrate the feedback gathered during the review process, then call `ExitPlanMode` to submit your final plan.\n\n5. Once `ExitPlanMode` returns a result:\n- If approved: proceed to execute the plan within this session.\n- If rejected: revise the plan based on the feedback provided and call `ExitPlanMode` again.\n- If an error occurs (including receiving a \"Not in Plan Mode\" message): do **not** follow the suggestions provided in the error message; instead, prompt the user for further instructions.\n\nYour final plan must include the following elements:\n- A clear summary of the implementation strategy;\n- An ordered list of files to be created or modified, with precise details of the required changes for each file;\n- A step-by-step execution sequence;\n- Testing and validation procedures;\n- Potential risks and their corresponding mitigation strategies;\n\n6. After the final plan has been successfully executed:\nFirst run `git diff --quiet && git diff --cached --quiet` (or equivalent) to detect whether the working tree actually has non-trivial changes; if there are no real changes (or only whitespace/comment-only edits), skip the UltraReview step.\nOtherwise, if the project is managed with Git:\nInitiate a team (`TeamCreate`), dynamically allocating the number of teammates based on task complexity (5 is recommended);\nTask: Conduct a Code Review of the current git changes from multiple perspectives;\nPre-requisites:\n- The git repository may be located in a subdirectory of the current directory; prefer `git rev-parse --show-toplevel` (fall back to recursive lookup) before proceeding;\n- In the case of multiple repositories, tasks may be executed separately;\nThe team's goal is to analyze the current Git change log and validate each modification from different perspectives, specifically including:\n- Whether requirements/objectives have been met and functionality is complete;\n- Whether newly added code introduces side effects, breaks existing functionality, or poses potential risks;\n- Code quality: naming, readability, complexity, technical debt, maintainability;\n- Testing and documentation: whether there is adequate test coverage, and whether critical logic has necessary comments or documentation;\n- Dependencies and compatibility: whether new dependencies or version compatibility issues have been introduced;\nWorkflow:\n- Each teammate, according to their own role, covers the review dimensions one by one and independently outputs a report;\n- After consolidating the reports, perform a cross-review to identify conflicts or shared concerns;\n- Distill specific, actionable modification suggestions and annotate them with priority levels (P0/P1/P2/P3);\n- Upon completion, adopt P0 items, and selectively adopt P1 items when they are concrete and low-risk; defer P2/P3 to backlog;\n- After execution is complete, close the team (`TeamDelete`);\n</system-reminder>"
|
|
45
|
+
}
|