feihong-code 0.2.1
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/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- package/.github/ISSUE_TEMPLATE/config.yml +14 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +28 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +46 -0
- package/.github/SECURITY.md +67 -0
- package/.github/workflows/ci.yml +140 -0
- package/AGENT-GUIDE.md +237 -0
- package/CHANGELOG.md +73 -0
- package/CODE_OF_CONDUCT.md +56 -0
- package/CONTRIBUTING.md +68 -0
- package/LICENSE +22 -0
- package/README.md +500 -0
- package/dist/agent/code-review.js +115 -0
- package/dist/agent/code-review.js.map +1 -0
- package/dist/agent/code-writer.js +227 -0
- package/dist/agent/code-writer.js.map +1 -0
- package/dist/agent/context-compactor.js +108 -0
- package/dist/agent/context-compactor.js.map +1 -0
- package/dist/agent/experience.js +190 -0
- package/dist/agent/experience.js.map +1 -0
- package/dist/agent/orchestrator.js +212 -0
- package/dist/agent/orchestrator.js.map +1 -0
- package/dist/agent/parallel-orchestrator.js +94 -0
- package/dist/agent/parallel-orchestrator.js.map +1 -0
- package/dist/agent/planner.js +53 -0
- package/dist/agent/planner.js.map +1 -0
- package/dist/agent/prompts.js +26 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/agent/quality-gate.js +143 -0
- package/dist/agent/quality-gate.js.map +1 -0
- package/dist/agent/repo-reader.js +384 -0
- package/dist/agent/repo-reader.js.map +1 -0
- package/dist/agent/repo-underwriter.js +132 -0
- package/dist/agent/repo-underwriter.js.map +1 -0
- package/dist/agent/self-heal.js +134 -0
- package/dist/agent/self-heal.js.map +1 -0
- package/dist/agent/self-improver.js +148 -0
- package/dist/agent/self-improver.js.map +1 -0
- package/dist/agent/subagent.js +75 -0
- package/dist/agent/subagent.js.map +1 -0
- package/dist/agent/swe-agent.js +162 -0
- package/dist/agent/swe-agent.js.map +1 -0
- package/dist/agent/swe-planner.js +155 -0
- package/dist/agent/swe-planner.js.map +1 -0
- package/dist/agent/swe-verifier.js +113 -0
- package/dist/agent/swe-verifier.js.map +1 -0
- package/dist/cli/commands.js +167 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.js +179 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/repl.js +67 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/cli/run.js +762 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/version.js +14 -0
- package/dist/cli/version.js.map +1 -0
- package/dist/enterprise/audit.js +294 -0
- package/dist/enterprise/audit.js.map +1 -0
- package/dist/enterprise/guard.js +58 -0
- package/dist/enterprise/guard.js.map +1 -0
- package/dist/enterprise/index.js +99 -0
- package/dist/enterprise/index.js.map +1 -0
- package/dist/enterprise/policy.js +242 -0
- package/dist/enterprise/policy.js.map +1 -0
- package/dist/enterprise/quota.js +58 -0
- package/dist/enterprise/quota.js.map +1 -0
- package/dist/enterprise/tenant.js +143 -0
- package/dist/enterprise/tenant.js.map +1 -0
- package/dist/models/cost.js +13 -0
- package/dist/models/cost.js.map +1 -0
- package/dist/models/model-router.js +170 -0
- package/dist/models/model-router.js.map +1 -0
- package/dist/models/model.dto.js +61 -0
- package/dist/models/model.dto.js.map +1 -0
- package/dist/models/model.interface.js +3 -0
- package/dist/models/model.interface.js.map +1 -0
- package/dist/models/providers/mock.provider.js +35 -0
- package/dist/models/providers/mock.provider.js.map +1 -0
- package/dist/models/providers/ollama.provider.js +88 -0
- package/dist/models/providers/ollama.provider.js.map +1 -0
- package/dist/models/providers/openai-compatible.provider.js +106 -0
- package/dist/models/providers/openai-compatible.provider.js.map +1 -0
- package/dist/runtime/event-log.js +42 -0
- package/dist/runtime/event-log.js.map +1 -0
- package/dist/runtime/git.js +106 -0
- package/dist/runtime/git.js.map +1 -0
- package/dist/runtime/session-persist.js +60 -0
- package/dist/runtime/session-persist.js.map +1 -0
- package/dist/runtime/session-store.js +34 -0
- package/dist/runtime/session-store.js.map +1 -0
- package/dist/runtime/worktree.js +117 -0
- package/dist/runtime/worktree.js.map +1 -0
- package/dist/shared/config.js +184 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/errors.js +66 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/logger.js +51 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/types.js +9 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/skills/goal.js +60 -0
- package/dist/skills/goal.js.map +1 -0
- package/dist/skills/grill.js +129 -0
- package/dist/skills/grill.js.map +1 -0
- package/dist/skills/plan.js +40 -0
- package/dist/skills/plan.js.map +1 -0
- package/dist/tools/analysis/code-analyzer.js +136 -0
- package/dist/tools/analysis/code-analyzer.js.map +1 -0
- package/dist/tools/file/edit.tool.js +43 -0
- package/dist/tools/file/edit.tool.js.map +1 -0
- package/dist/tools/file/list.tool.js +36 -0
- package/dist/tools/file/list.tool.js.map +1 -0
- package/dist/tools/file/read.tool.js +34 -0
- package/dist/tools/file/read.tool.js.map +1 -0
- package/dist/tools/file/write.tool.js +39 -0
- package/dist/tools/file/write.tool.js.map +1 -0
- package/dist/tools/generator/code-generator.js +119 -0
- package/dist/tools/generator/code-generator.js.map +1 -0
- package/dist/tools/generator/test-generator.js +65 -0
- package/dist/tools/generator/test-generator.js.map +1 -0
- package/dist/tools/index.js +53 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/safe-path.js +36 -0
- package/dist/tools/safe-path.js.map +1 -0
- package/dist/tools/search/grep.tool.js +81 -0
- package/dist/tools/search/grep.tool.js.map +1 -0
- package/dist/tools/shell/exec.js +50 -0
- package/dist/tools/shell/exec.js.map +1 -0
- package/dist/tools/shell/run-shell.tool.js +49 -0
- package/dist/tools/shell/run-shell.tool.js.map +1 -0
- package/dist/tools/tool.interface.js +8 -0
- package/dist/tools/tool.interface.js.map +1 -0
- package/dist/tools/tool.registry.js +75 -0
- package/dist/tools/tool.registry.js.map +1 -0
- package/dist/tools/verify/build-check.tool.js +37 -0
- package/dist/tools/verify/build-check.tool.js.map +1 -0
- package/dist/tools/verify/test-run.tool.js +37 -0
- package/dist/tools/verify/test-run.tool.js.map +1 -0
- package/dist/web/auth.js +25 -0
- package/dist/web/auth.js.map +1 -0
- package/dist/web/public/index.html +39 -0
- package/dist/web/server.js +60 -0
- package/dist/web/server.js.map +1 -0
- package/docs//344/272/247/345/223/201/345/274/200/345/217/221/346/226/207/346/241/243.md +614 -0
- package/docs//344/274/201/344/270/232/351/203/250/347/275/262/344/270/216/345/220/210/350/247/204.md +267 -0
- package/docs//344/275/277/347/224/250/350/257/264/346/230/216/344/271/246.md +358 -0
- package/docs//345/270/270/350/247/201/351/227/256/351/242/230/344/270/216/346/225/205/351/232/234/346/216/222/346/237/245.md +130 -0
- package/docs//346/212/200/346/234/257/350/257/264/346/230/216/344/271/246.md +303 -0
- package/docs//346/236/266/346/236/204/344/270/216API.md +238 -0
- package/docs//347/224/250/346/210/267/346/211/213/345/206/214.md +196 -0
- package/docs//351/203/250/347/275/262/346/214/207/345/215/227.md +165 -0
- package/docs//351/205/215/347/275/256/345/217/202/350/200/203.md +127 -0
- package/package.json +83 -0
- package/tool-schema.json +127 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorktreeError = void 0;
|
|
4
|
+
exports.getRepoRoot = getRepoRoot;
|
|
5
|
+
exports.createWorktree = createWorktree;
|
|
6
|
+
exports.removeWorktree = removeWorktree;
|
|
7
|
+
exports.listWorktrees = listWorktrees;
|
|
8
|
+
/**
|
|
9
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
10
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
11
|
+
*
|
|
12
|
+
* 工作树隔离:用 git worktree 为每个子代理创建独立工作区(独立目录 + 独立分支),
|
|
13
|
+
* 实现多子代理并行开发的物理隔离。任务完成后可安全移除。
|
|
14
|
+
*/
|
|
15
|
+
const child_process_1 = require("child_process");
|
|
16
|
+
const util_1 = require("util");
|
|
17
|
+
const fs_1 = require("fs");
|
|
18
|
+
const os_1 = require("os");
|
|
19
|
+
const path_1 = require("path");
|
|
20
|
+
const exec = (0, util_1.promisify)(child_process_1.execFile);
|
|
21
|
+
class WorktreeError extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'WorktreeError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.WorktreeError = WorktreeError;
|
|
28
|
+
/** 取当前 git 仓库根目录(用于在其上创建 worktree) */
|
|
29
|
+
async function getRepoRoot(cwd) {
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await exec('git', ['rev-parse', '--show-toplevel'], { cwd });
|
|
32
|
+
return stdout.trim();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new WorktreeError(`当前目录不是 git 仓库,无法创建 worktree: ${cwd}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function getCurrentBranch(repoRoot) {
|
|
39
|
+
const { stdout } = await exec('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: repoRoot });
|
|
40
|
+
return stdout.trim();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 创建隔离工作树。
|
|
44
|
+
* @param repoRoot 主仓库根目录
|
|
45
|
+
* @param name 子代理/任务名(用于分支与目录命名)
|
|
46
|
+
* @param baseBranch 基线分支(默认当前分支)
|
|
47
|
+
*/
|
|
48
|
+
async function createWorktree(repoRoot, name, baseBranch) {
|
|
49
|
+
const base = baseBranch ?? (await getCurrentBranch(repoRoot));
|
|
50
|
+
const branch = `fhcode/${slug(name)}-${Date.now().toString(36)}`;
|
|
51
|
+
const path = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), `fhcode-wt-${slug(name)}-`));
|
|
52
|
+
try {
|
|
53
|
+
await exec('git', ['worktree', 'add', '--force', '-b', branch, path, base], {
|
|
54
|
+
cwd: repoRoot,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
// git worktree add 失败:清理已创建的临时目录,避免孤儿目录泄漏
|
|
59
|
+
try {
|
|
60
|
+
(0, fs_1.rmSync)(path, { recursive: true, force: true });
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
/* best-effort */
|
|
64
|
+
}
|
|
65
|
+
throw new WorktreeError(`创建 worktree 失败 (${name}): ${e instanceof Error ? e.message : String(e)}`);
|
|
66
|
+
}
|
|
67
|
+
return { path, branch };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 移除工作树(--force 即使有未提交改动也删除,子代理产物不进主仓库)。
|
|
71
|
+
*
|
|
72
|
+
* 注意:Windows 上 git 对“顺序移除多个 worktree”存在已知缺陷——
|
|
73
|
+
* 移除第一个时可能连带清掉整个 .git/worktrees 目录,使后续 worktree 的
|
|
74
|
+
* git 原生移除报 “is not a working tree”。因此此处采用“尽力移除 + 强制清目录 + prune”
|
|
75
|
+
* 的鲁棒策略:先尝试 git 原生移除;无论成败都强制删除磁盘目录(防止孤儿目录);
|
|
76
|
+
* 最后 prune 清理残留元数据。子代理结果已在移除前收集,清理仅针对工作区本身。
|
|
77
|
+
*/
|
|
78
|
+
async function removeWorktree(repoRoot, path) {
|
|
79
|
+
const norm = path.replace(/\\/g, '/');
|
|
80
|
+
// 1) 尽力用 git 原生移除(可能因上述 Windows bug 失败,忽略)
|
|
81
|
+
try {
|
|
82
|
+
await exec('git', ['worktree', 'remove', '--force', norm], { cwd: repoRoot });
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
/* best-effort,下面兜底 */
|
|
86
|
+
}
|
|
87
|
+
// 2) 强制删除磁盘目录,避免孤儿临时目录残留
|
|
88
|
+
try {
|
|
89
|
+
(0, fs_1.rmSync)(path, { recursive: true, force: true, maxRetries: 3 });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
/* best-effort */
|
|
93
|
+
}
|
|
94
|
+
// 3) 兜底清理 git 元数据残留
|
|
95
|
+
try {
|
|
96
|
+
await exec('git', ['worktree', 'prune'], { cwd: repoRoot });
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
/* best-effort */
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** 列出所有 worktree 路径 */
|
|
103
|
+
async function listWorktrees(repoRoot) {
|
|
104
|
+
const { stdout } = await exec('git', ['worktree', 'list', '--porcelain'], { cwd: repoRoot });
|
|
105
|
+
return stdout
|
|
106
|
+
.split('\n')
|
|
107
|
+
.filter((l) => l.startsWith('worktree '))
|
|
108
|
+
.map((l) => l.slice('worktree '.length).trim());
|
|
109
|
+
}
|
|
110
|
+
function slug(s) {
|
|
111
|
+
return s
|
|
112
|
+
.toLowerCase()
|
|
113
|
+
.replace(/[^a-z0-9一-龥]+/g, '-')
|
|
114
|
+
.replace(/^-+|-+$/g, '')
|
|
115
|
+
.slice(0, 24) || 'task';
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/runtime/worktree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadDotEnv = loadDotEnv;
|
|
4
|
+
exports.resolveHomeDir = resolveHomeDir;
|
|
5
|
+
exports.loadConfigFile = loadConfigFile;
|
|
6
|
+
exports.loadConfig = loadConfig;
|
|
7
|
+
exports.__resetConfigForTest = __resetConfigForTest;
|
|
8
|
+
/**
|
|
9
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
10
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
11
|
+
*
|
|
12
|
+
* 集中配置(铁律:所有配置来自环境变量,启动时校验,fail-fast,懒加载)
|
|
13
|
+
*/
|
|
14
|
+
const errors_1 = require("./errors");
|
|
15
|
+
const fs_1 = require("fs");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const os_1 = require("os");
|
|
18
|
+
/**
|
|
19
|
+
* 极简 .env 加载器(不引入第三方依赖,离线可用)。
|
|
20
|
+
* 在 cwd 下读取 .env(若存在),将 KEY=VALUE 注入 process.env(仅当该键尚未设置,避免覆盖显式环境变量)。
|
|
21
|
+
* 值两侧的 ' 或 " 会被剥离。.env 必须被 .gitignore 排除,切勿提交真实密钥。
|
|
22
|
+
*/
|
|
23
|
+
function loadDotEnv(cwd = process.cwd()) {
|
|
24
|
+
const file = (0, path_1.join)(cwd, '.env');
|
|
25
|
+
if (!(0, fs_1.existsSync)(file))
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
const text = (0, fs_1.readFileSync)(file, 'utf8');
|
|
29
|
+
for (const raw of text.split('\n')) {
|
|
30
|
+
const line = raw.trim();
|
|
31
|
+
if (!line || line.startsWith('#'))
|
|
32
|
+
continue;
|
|
33
|
+
const eq = line.indexOf('=');
|
|
34
|
+
if (eq === -1)
|
|
35
|
+
continue;
|
|
36
|
+
const key = line.slice(0, eq).trim();
|
|
37
|
+
let val = line.slice(eq + 1).trim();
|
|
38
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
39
|
+
val = val.slice(1, -1);
|
|
40
|
+
}
|
|
41
|
+
if (!(key in process.env))
|
|
42
|
+
process.env[key] = val;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* 加载失败不影响离线模式 */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const APP_VERSION = '0.2.1';
|
|
50
|
+
/**
|
|
51
|
+
* 解析主目录:优先 FH_HOME,缺省 ~/.feihong-code(避免缺环境变量即崩溃)。
|
|
52
|
+
*/
|
|
53
|
+
function resolveHomeDir() {
|
|
54
|
+
const h = process.env.FH_HOME?.trim();
|
|
55
|
+
if (h)
|
|
56
|
+
return h.startsWith('~') ? h.replace(/^~/, (0, os_1.homedir)()) : h;
|
|
57
|
+
return (0, path_1.join)((0, os_1.homedir)(), '.feihong-code');
|
|
58
|
+
}
|
|
59
|
+
let cached = null;
|
|
60
|
+
/**
|
|
61
|
+
* 读取 fhcode 配置文件(JSON),按优先级:
|
|
62
|
+
* 1) 显式 path
|
|
63
|
+
* 2) FH_CONFIG 环境变量
|
|
64
|
+
* 3) cwd/fhcode.config.json
|
|
65
|
+
* 4) FH_HOME/fhcode.config.json
|
|
66
|
+
* 文件缺失或 JSON 损坏时返回 null(不抛错,便于离线/默认配置)。
|
|
67
|
+
*/
|
|
68
|
+
function loadConfigFile(path) {
|
|
69
|
+
const candidates = [
|
|
70
|
+
path,
|
|
71
|
+
process.env.FH_CONFIG,
|
|
72
|
+
(0, path_1.join)(process.cwd(), 'fhcode.config.json'),
|
|
73
|
+
(0, path_1.join)(resolveHomeDir(), 'fhcode.config.json'),
|
|
74
|
+
].filter(Boolean);
|
|
75
|
+
for (const f of candidates) {
|
|
76
|
+
if (!(0, fs_1.existsSync)(f))
|
|
77
|
+
continue;
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse((0, fs_1.readFileSync)(f, 'utf8'));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
/* 忽略损坏的配置文件 */
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 单环境变量快速接入真实模型:
|
|
89
|
+
* FH_MODEL_NAME 模型名(必填,Ollama 即本地模型名)
|
|
90
|
+
* FH_MODEL_TYPE 'ollama' | 'openai-compatible'(缺省按 baseURL 推断)
|
|
91
|
+
* FH_MODEL_BASE_URL 接口地址(ollama 缺省 http://localhost:11434)
|
|
92
|
+
* FH_MODEL_API_KEY OpenAI 兼容接口的 Bearer 令牌
|
|
93
|
+
* FH_MODEL_TAGS 逗号分隔能力标签(缺省 code-gen,reasoning[,local])
|
|
94
|
+
* FH_MODEL_COST_PER_1K 每千 token 成本(USD,统计用,缺省 0)
|
|
95
|
+
* 仅当 FH_PROVIDERS / 配置文件均未提供 provider 时生效。
|
|
96
|
+
*/
|
|
97
|
+
function buildProviderFromEnv() {
|
|
98
|
+
const name = process.env.FH_MODEL_NAME || process.env.FH_OLLAMA_MODEL;
|
|
99
|
+
if (!name)
|
|
100
|
+
return null;
|
|
101
|
+
const type = process.env.FH_MODEL_TYPE ||
|
|
102
|
+
(process.env.FH_MODEL_BASE_URL?.includes('ollama') ? 'ollama' : 'openai-compatible');
|
|
103
|
+
const baseURL = process.env.FH_MODEL_BASE_URL || (type === 'ollama' ? 'http://localhost:11434' : '');
|
|
104
|
+
if (type === 'openai-compatible' && !baseURL)
|
|
105
|
+
return null;
|
|
106
|
+
const tagStr = process.env.FH_MODEL_TAGS ||
|
|
107
|
+
(type === 'ollama' ? 'code-gen,reasoning,local' : 'code-gen,reasoning');
|
|
108
|
+
const tags = tagStr
|
|
109
|
+
.split(',')
|
|
110
|
+
.map((s) => s.trim())
|
|
111
|
+
.filter(Boolean);
|
|
112
|
+
return {
|
|
113
|
+
id: name,
|
|
114
|
+
type,
|
|
115
|
+
baseURL,
|
|
116
|
+
model: name,
|
|
117
|
+
apiKey: process.env.FH_MODEL_API_KEY || undefined,
|
|
118
|
+
tags,
|
|
119
|
+
costPer1k: Number(process.env.FH_MODEL_COST_PER_1K || '0'),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 解析模型供应商列表(三级优先级):
|
|
124
|
+
* 1) FH_PROVIDERS 显式 JSON(最高优先级)
|
|
125
|
+
* 2) 配置文件 models.providers
|
|
126
|
+
* 3) 单环境变量(FH_MODEL_NAME 等)自动构建一个 provider
|
|
127
|
+
* 三者皆无则返回空数组(真实模式会在调用时给出明确报错)。
|
|
128
|
+
*/
|
|
129
|
+
function resolveProviders(fileCfg) {
|
|
130
|
+
if (process.env.FH_PROVIDERS) {
|
|
131
|
+
try {
|
|
132
|
+
const parsed = JSON.parse(process.env.FH_PROVIDERS);
|
|
133
|
+
if (!Array.isArray(parsed))
|
|
134
|
+
throw new Error('FH_PROVIDERS 必须为数组');
|
|
135
|
+
return parsed;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
throw new errors_1.ConfigError('FH_PROVIDERS');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const fileProviders = fileCfg?.models?.providers;
|
|
142
|
+
if (Array.isArray(fileProviders) && fileProviders.length > 0) {
|
|
143
|
+
return fileProviders;
|
|
144
|
+
}
|
|
145
|
+
const envProv = buildProviderFromEnv();
|
|
146
|
+
if (envProv)
|
|
147
|
+
return [envProv];
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
/** 加载并校验配置(首次调用时执行,之后复用)。缺必需项立即抛 ConfigError。 */
|
|
151
|
+
function loadConfig() {
|
|
152
|
+
if (cached)
|
|
153
|
+
return cached;
|
|
154
|
+
const fileCfg = loadConfigFile();
|
|
155
|
+
const providers = resolveProviders(fileCfg);
|
|
156
|
+
cached = {
|
|
157
|
+
app: {
|
|
158
|
+
name: 'feihong-code',
|
|
159
|
+
version: APP_VERSION,
|
|
160
|
+
homeDir: resolveHomeDir(),
|
|
161
|
+
},
|
|
162
|
+
models: {
|
|
163
|
+
providers,
|
|
164
|
+
defaultStrategy: process.env.FH_MODEL_STRATEGY ||
|
|
165
|
+
fileCfg?.models?.defaultStrategy ||
|
|
166
|
+
'cost',
|
|
167
|
+
budgetPerTaskUsd: Number(process.env.FH_BUDGET_USD || fileCfg?.models?.budgetPerTaskUsd || '0.5'),
|
|
168
|
+
},
|
|
169
|
+
runtime: {
|
|
170
|
+
logDir: process.env.FH_LOG_DIR || (0, path_1.join)(resolveHomeDir(), 'sessions'),
|
|
171
|
+
maxRetries: 3,
|
|
172
|
+
},
|
|
173
|
+
security: {
|
|
174
|
+
shellAllowlist: (process.env.FH_SHELL_ALLOW || '').split(',').map((s) => s.trim()).filter(Boolean),
|
|
175
|
+
requireApproval: process.env.FH_REQUIRE_APPROVAL !== 'false',
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
return cached;
|
|
179
|
+
}
|
|
180
|
+
/** 仅用于测试:重置缓存 */
|
|
181
|
+
function __resetConfigForTest() {
|
|
182
|
+
cached = null;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/shared/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
4
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
5
|
+
*
|
|
6
|
+
* 类型化错误层级(铁律:业务错误必须可分类、可结构化返回)
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.SecurityError = exports.ApprovalRequiredError = exports.ToolError = exports.ModelError = exports.ConfigError = exports.AppError = void 0;
|
|
10
|
+
exports.isAppError = isAppError;
|
|
11
|
+
/** 所有可预期错误的基类 */
|
|
12
|
+
class AppError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
status;
|
|
15
|
+
isOperational;
|
|
16
|
+
constructor(message, code, status, isOperational = true) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.status = status;
|
|
20
|
+
this.isOperational = isOperational;
|
|
21
|
+
this.name = this.constructor.name;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.AppError = AppError;
|
|
25
|
+
/** 配置缺失:启动即失败(fail-fast) */
|
|
26
|
+
class ConfigError extends AppError {
|
|
27
|
+
constructor(key) {
|
|
28
|
+
super(`缺少必需环境变量: ${key}`, 'CONFIG_ERROR', 500);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.ConfigError = ConfigError;
|
|
32
|
+
/** 模型调用失败 */
|
|
33
|
+
class ModelError extends AppError {
|
|
34
|
+
provider;
|
|
35
|
+
constructor(message, provider) {
|
|
36
|
+
super(`模型调用失败[${provider}]: ${message}`, 'MODEL_ERROR', 502);
|
|
37
|
+
this.provider = provider;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.ModelError = ModelError;
|
|
41
|
+
/** 工具执行失败 */
|
|
42
|
+
class ToolError extends AppError {
|
|
43
|
+
constructor(tool, message) {
|
|
44
|
+
super(`工具[${tool}]执行失败: ${message}`, 'TOOL_ERROR', 400);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.ToolError = ToolError;
|
|
48
|
+
/** 需要人工审批 */
|
|
49
|
+
class ApprovalRequiredError extends AppError {
|
|
50
|
+
constructor(action) {
|
|
51
|
+
super(`需人工审批: ${action}`, 'APPROVAL_REQUIRED', 401);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.ApprovalRequiredError = ApprovalRequiredError;
|
|
55
|
+
/** 安全拦截(危险命令 / 越权) */
|
|
56
|
+
class SecurityError extends AppError {
|
|
57
|
+
constructor(message) {
|
|
58
|
+
super(`安全拦截: ${message}`, 'SECURITY_ERROR', 403);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.SecurityError = SecurityError;
|
|
62
|
+
/** 类型守卫:判断是否为可预期的业务错误 */
|
|
63
|
+
function isAppError(e) {
|
|
64
|
+
return e instanceof AppError;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/shared/errors.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAmDH,gCAEC;AAnDD,iBAAiB;AACjB,MAAa,QAAS,SAAQ,KAAK;IAGf;IACA;IACA;IAJlB,YACE,OAAe,EACC,IAAY,EACZ,MAAc,EACd,gBAAgB,IAAI;QAEpC,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAAQ;QACd,kBAAa,GAAb,aAAa,CAAO;QAGpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IACpC,CAAC;CACF;AAVD,4BAUC;AAED,4BAA4B;AAC5B,MAAa,WAAY,SAAQ,QAAQ;IACvC,YAAY,GAAW;QACrB,KAAK,CAAC,aAAa,GAAG,EAAE,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;IACjD,CAAC;CACF;AAJD,kCAIC;AAED,aAAa;AACb,MAAa,UAAW,SAAQ,QAAQ;IACO;IAA7C,YAAY,OAAe,EAAkB,QAAgB;QAC3D,KAAK,CAAC,UAAU,QAAQ,MAAM,OAAO,EAAE,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QADlB,aAAQ,GAAR,QAAQ,CAAQ;IAE7D,CAAC;CACF;AAJD,gCAIC;AAED,aAAa;AACb,MAAa,SAAU,SAAQ,QAAQ;IACrC,YAAY,IAAY,EAAE,OAAe;QACvC,KAAK,CAAC,MAAM,IAAI,UAAU,OAAO,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;CACF;AAJD,8BAIC;AAED,aAAa;AACb,MAAa,qBAAsB,SAAQ,QAAQ;IACjD,YAAY,MAAc;QACxB,KAAK,CAAC,UAAU,MAAM,EAAE,EAAE,mBAAmB,EAAE,GAAG,CAAC,CAAC;IACtD,CAAC;CACF;AAJD,sDAIC;AAED,sBAAsB;AACtB,MAAa,aAAc,SAAQ,QAAQ;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,SAAS,OAAO,EAAE,EAAE,gBAAgB,EAAE,GAAG,CAAC,CAAC;IACnD,CAAC;CACF;AAJD,sCAIC;AAED,yBAAyB;AACzB,SAAgB,UAAU,CAAC,CAAU;IACnC,OAAO,CAAC,YAAY,QAAQ,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logger = void 0;
|
|
4
|
+
exports.setRunId = setRunId;
|
|
5
|
+
exports.redact = redact;
|
|
6
|
+
let runId = 'unknown';
|
|
7
|
+
/** 在 CLI 启动时为本次运行设置唯一 runId */
|
|
8
|
+
function setRunId(id) {
|
|
9
|
+
runId = id;
|
|
10
|
+
}
|
|
11
|
+
// M12 修复:扩充敏感 key 识别(覆盖常见密钥/令牌字段名)
|
|
12
|
+
const SENSITIVE_KEY_RE = /api[_-]?key|apikey|secret|client[_-]?secret|access[_-]?token|refresh[_-]?token|token|password|passwd|pwd|authorization|bearer|private[_-]?key|cookie|credential|passphrase|session[_-]?id|x-api-key/i;
|
|
13
|
+
// 值形态:sk-... / JWT / 长十六进制或 base64 令牌(仅对字符串值生效,避免误伤普通文本)
|
|
14
|
+
const SENSITIVE_VALUE_RE = /\bsk-[A-Za-z0-9_-]{8,}\b|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|[A-Za-z0-9+/]{32,}={0,2}/;
|
|
15
|
+
/** 元数据脱敏:敏感 key 整值遮蔽;普通 key 但值疑似令牌也遮蔽。 */
|
|
16
|
+
function redact(meta) {
|
|
17
|
+
const out = {};
|
|
18
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
19
|
+
if (SENSITIVE_KEY_RE.test(k)) {
|
|
20
|
+
out[k] = '[REDACTED]';
|
|
21
|
+
}
|
|
22
|
+
else if (typeof v === 'string' && SENSITIVE_VALUE_RE.test(v)) {
|
|
23
|
+
out[k] = '[REDACTED]';
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
out[k] = v;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
function emit(level, msg, meta) {
|
|
32
|
+
const entry = {
|
|
33
|
+
ts: new Date().toISOString(),
|
|
34
|
+
level,
|
|
35
|
+
msg,
|
|
36
|
+
runId,
|
|
37
|
+
...redact(meta),
|
|
38
|
+
};
|
|
39
|
+
const line = JSON.stringify(entry);
|
|
40
|
+
if (level === 'error')
|
|
41
|
+
process.stderr.write(line + '\n');
|
|
42
|
+
else
|
|
43
|
+
process.stdout.write(line + '\n');
|
|
44
|
+
}
|
|
45
|
+
exports.logger = {
|
|
46
|
+
info: (msg, meta = {}) => emit('info', msg, meta),
|
|
47
|
+
warn: (msg, meta = {}) => emit('warn', msg, meta),
|
|
48
|
+
error: (msg, meta = {}) => emit('error', msg, meta),
|
|
49
|
+
debug: (msg, meta = {}) => emit('debug', msg, meta),
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/shared/logger.ts"],"names":[],"mappings":";;;AAWA,4BAEC;AAUD,wBAYC;AA3BD,IAAI,KAAK,GAAU,SAAS,CAAC;AAE7B,+BAA+B;AAC/B,SAAgB,QAAQ,CAAC,EAAS;IAChC,KAAK,GAAG,EAAE,CAAC;AACb,CAAC;AAED,mCAAmC;AACnC,MAAM,gBAAgB,GACpB,sMAAsM,CAAC;AACzM,yDAAyD;AACzD,MAAM,kBAAkB,GACtB,qGAAqG,CAAC;AAExG,0CAA0C;AAC1C,SAAgB,MAAM,CAAC,IAA6B;IAClD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7B,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;QACxB,CAAC;aAAM,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CAAC,KAAwB,EAAE,GAAW,EAAE,IAA6B;IAChF,MAAM,KAAK,GAAa;QACtB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,KAAK;QACL,GAAG;QACH,KAAK;QACL,GAAG,MAAM,CAAC,IAAI,CAAC;KAChB,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,OAAO;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;;QACpD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AACzC,CAAC;AAEY,QAAA,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,GAAW,EAAE,OAAgC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;IAClF,IAAI,EAAE,CAAC,GAAW,EAAE,OAAgC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;IAClF,KAAK,EAAE,CAAC,GAAW,EAAE,OAAgC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;IACpF,KAAK,EAAE,CAAC,GAAW,EAAE,OAAgC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;CACrF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decomposeGoalToGoal = decomposeGoalToGoal;
|
|
4
|
+
exports.saveGoal = saveGoal;
|
|
5
|
+
exports.listGoals = listGoals;
|
|
6
|
+
exports.renderGoal = renderGoal;
|
|
7
|
+
/**
|
|
8
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
9
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
10
|
+
*
|
|
11
|
+
* /goal 技能:把高层目标分解为可跟踪子目标,保存为本地目标文件,支持查询进度。
|
|
12
|
+
* 离线用规则分解;联调时可由模型生成。
|
|
13
|
+
*/
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const path_1 = require("path");
|
|
16
|
+
const crypto_1 = require("crypto");
|
|
17
|
+
function decomposeGoalToGoal(title) {
|
|
18
|
+
const subs = title
|
|
19
|
+
.split(/[;;\n。并且同时分别以及]+/)
|
|
20
|
+
.map((s) => s.trim())
|
|
21
|
+
.filter(Boolean);
|
|
22
|
+
return {
|
|
23
|
+
id: (0, crypto_1.randomUUID)().slice(0, 8),
|
|
24
|
+
title,
|
|
25
|
+
subGoals: subs.length ? subs : [title],
|
|
26
|
+
createdAt: new Date().toISOString(),
|
|
27
|
+
status: 'active',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function saveGoal(goal, homeDir) {
|
|
31
|
+
const dir = (0, path_1.join)(homeDir, 'goals');
|
|
32
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
33
|
+
const file = (0, path_1.join)(dir, `${goal.id}.json`);
|
|
34
|
+
(0, fs_1.writeFileSync)(file, JSON.stringify(goal, null, 2), 'utf8');
|
|
35
|
+
return file;
|
|
36
|
+
}
|
|
37
|
+
function listGoals(homeDir) {
|
|
38
|
+
const dir = (0, path_1.join)(homeDir, 'goals');
|
|
39
|
+
if (!(0, fs_1.existsSync)(dir))
|
|
40
|
+
return [];
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const name of (0, fs_1.readdirSync)(dir)) {
|
|
43
|
+
if (!name.endsWith('.json'))
|
|
44
|
+
continue;
|
|
45
|
+
try {
|
|
46
|
+
out.push(JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(dir, name), 'utf8')));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* ignore 损坏文件 */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
function renderGoal(goal) {
|
|
55
|
+
return (`目标: ${goal.title}\n` +
|
|
56
|
+
`ID: ${goal.id} · 状态: ${goal.status} · 创建: ${goal.createdAt}\n` +
|
|
57
|
+
`子目标(${goal.subGoals.length}):\n` +
|
|
58
|
+
goal.subGoals.map((s, i) => ` ${i + 1}. ${s}`).join('\n'));
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=goal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal.js","sourceRoot":"","sources":["../../src/skills/goal.ts"],"names":[],"mappings":";;AAmBA,kDAYC;AAED,4BAMC;AAED,8BAaC;AAED,gCAOC;AA/DD;;;;;;GAMG;AACH,2BAAqF;AACrF,+BAA4B;AAC5B,mCAAoC;AAUpC,SAAgB,mBAAmB,CAAC,KAAa;IAC/C,MAAM,IAAI,GAAG,KAAK;SACf,KAAK,CAAC,kBAAkB,CAAC;SACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,OAAO;QACL,EAAE,EAAE,IAAA,mBAAU,GAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5B,KAAK;QACL,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACtC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM,EAAE,QAAQ;KACjB,CAAC;AACJ,CAAC;AAED,SAAgB,QAAQ,CAAC,IAAU,EAAE,OAAe;IAClD,MAAM,GAAG,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACnC,IAAA,cAAS,EAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1C,IAAA,kBAAa,EAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,SAAS,CAAC,OAAe;IACvC,MAAM,GAAG,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,IAAA,eAAU,EAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,GAAG,GAAW,EAAE,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,IAAA,gBAAW,EAAC,GAAG,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,SAAS;QACtC,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAS,CAAC,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAgB,UAAU,CAAC,IAAU;IACnC,OAAO,CACL,OAAO,IAAI,CAAC,KAAK,IAAI;QACrB,OAAO,IAAI,CAAC,EAAE,UAAU,IAAI,CAAC,MAAM,UAAU,IAAI,CAAC,SAAS,IAAI;QAC/D,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,MAAM;QACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC3D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runGrill = runGrill;
|
|
4
|
+
/**
|
|
5
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
6
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
7
|
+
*
|
|
8
|
+
* /grill 技能:红队式代码审查。对指定路径/文件做安全与质量审查,
|
|
9
|
+
* 输出问题清单与严重级别(不修改任何文件,只审查)。
|
|
10
|
+
*/
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
const RULES = [
|
|
14
|
+
{
|
|
15
|
+
id: 'secret-leak',
|
|
16
|
+
severity: 'critical',
|
|
17
|
+
pattern: /(?:api[_-]?key|secret|token|password|passwd|私钥)\s*[:=]\s*['"][^'"]{6,}['"]/i,
|
|
18
|
+
detail: '疑似硬编码密钥/凭证,应迁移到环境变量并经脱敏处理',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'eval-injection',
|
|
22
|
+
severity: 'critical',
|
|
23
|
+
pattern: /\b(eval|execSync|child_process\.exec|new\s+Function)\s*\(/,
|
|
24
|
+
detail: '动态执行/构造代码存在注入风险,需白名单与校验',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'path-traversal',
|
|
28
|
+
severity: 'high',
|
|
29
|
+
pattern: /\.\.\//,
|
|
30
|
+
detail: '路径中出现 ../,需确认已做沙箱边界校验防止越权访问',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'no-input-validation',
|
|
34
|
+
severity: 'medium',
|
|
35
|
+
pattern: /JSON\.parse\s*\([^)]*\)\s*(?!\.catch)/,
|
|
36
|
+
detail: 'JSON.parse 未包裹 try/catch,畸形输入将导致进程崩溃',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'todo-fixme',
|
|
40
|
+
severity: 'low',
|
|
41
|
+
pattern: /\b(TODO|FIXME|XXX)\b/,
|
|
42
|
+
detail: '存在待办标记,需跟进或关联任务跟踪',
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
function runGrill(rootDir, target = '.') {
|
|
46
|
+
const base = (0, path_1.join)(rootDir, target);
|
|
47
|
+
const files = collectFiles(base);
|
|
48
|
+
const findings = [];
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
if (!/\.(ts|js|tsx|jsx|json|md|py|go|java)$/.test(file))
|
|
51
|
+
continue;
|
|
52
|
+
const isCode = /\.(ts|js|tsx|jsx|py|go|java)$/.test(file);
|
|
53
|
+
let content;
|
|
54
|
+
try {
|
|
55
|
+
content = (0, fs_1.readFileSync)(file, 'utf8');
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const lines = content.split('\n');
|
|
61
|
+
lines.forEach((line, idx) => {
|
|
62
|
+
// 规则定义本身(id/severity/pattern/detail)不参与自检,避免自我误报
|
|
63
|
+
if (/^\s*(id|severity|pattern|detail)\s*:/.test(line))
|
|
64
|
+
return;
|
|
65
|
+
const isComment = /^\s*(\/\/|\*|\/\*)/.test(line);
|
|
66
|
+
for (const rule of RULES) {
|
|
67
|
+
// 路径穿越与 JSON.parse 仅针对真实代码文件,文档(.md/.json)按行文处理,避免误报
|
|
68
|
+
if ((rule.id === 'path-traversal' || rule.id === 'no-input-validation') && !isCode)
|
|
69
|
+
continue;
|
|
70
|
+
// 排除 import/require/export 语句里的 ../(模块相对路径,非文件穿越)
|
|
71
|
+
if (rule.id === 'path-traversal' && /(import\s|export\s|require\(|from\s+['"])/.test(line))
|
|
72
|
+
continue;
|
|
73
|
+
// 注释中的 ../ 多为文档说明,非真实路径穿越
|
|
74
|
+
if (rule.id === 'path-traversal' && isComment)
|
|
75
|
+
continue;
|
|
76
|
+
// no-input-validation:若 JSON.parse 已在 try/catch 窗口内则视为已防护
|
|
77
|
+
if (rule.id === 'no-input-validation') {
|
|
78
|
+
const before = lines.slice(Math.max(0, idx - 3), idx + 1).join('\n');
|
|
79
|
+
const after = lines.slice(idx, Math.min(lines.length, idx + 4)).join('\n');
|
|
80
|
+
if (/try\s*\{/.test(before) || /catch\s*[\(\{]/.test(after))
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (rule.pattern.test(line)) {
|
|
84
|
+
findings.push({
|
|
85
|
+
file: (0, path_1.relative)(rootDir, file),
|
|
86
|
+
line: idx + 1,
|
|
87
|
+
severity: rule.severity,
|
|
88
|
+
rule: rule.id,
|
|
89
|
+
detail: rule.detail,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const counts = findings.reduce((acc, f) => {
|
|
96
|
+
acc[f.severity] += 1;
|
|
97
|
+
return acc;
|
|
98
|
+
}, { critical: 0, high: 0, medium: 0, low: 0 });
|
|
99
|
+
const summary = `审查 ${files.length} 个文件,发现 ${findings.length} 个问题:` +
|
|
100
|
+
`致命 ${counts.critical} / 高危 ${counts.high} / 中 ${counts.medium} / 低 ${counts.low}`;
|
|
101
|
+
return { scanned: files.map((f) => (0, path_1.relative)(rootDir, f)), findings, summary };
|
|
102
|
+
}
|
|
103
|
+
function collectFiles(dir) {
|
|
104
|
+
if (!(0, fs_1.existsSync)(dir))
|
|
105
|
+
return [];
|
|
106
|
+
const out = [];
|
|
107
|
+
const walk = (p) => {
|
|
108
|
+
let entries;
|
|
109
|
+
try {
|
|
110
|
+
entries = (0, fs_1.readdirSync)(p);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
for (const e of entries) {
|
|
116
|
+
if (e === 'node_modules' || e === '.git' || e === 'dist')
|
|
117
|
+
continue;
|
|
118
|
+
const full = (0, path_1.join)(p, e);
|
|
119
|
+
const st = (0, fs_1.statSync)(full);
|
|
120
|
+
if (st.isDirectory())
|
|
121
|
+
walk(full);
|
|
122
|
+
else
|
|
123
|
+
out.push(full);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
walk(dir);
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=grill.js.map
|