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,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.safeJoin = safeJoin;
|
|
4
|
+
/**
|
|
5
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
6
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
7
|
+
*
|
|
8
|
+
* 路径安全:工具只能在工作区范围内操作,防止 ../ 穿越越权
|
|
9
|
+
*/
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
const errors_1 = require("../shared/errors");
|
|
13
|
+
/**
|
|
14
|
+
* 若路径已存在则规范化符号链接(realpath),否则原样返回。
|
|
15
|
+
* 用于对已存在的文件/目录做符号链接逃逸检测,而新建文件场景不受影响。
|
|
16
|
+
*/
|
|
17
|
+
function canonicalIfExists(p) {
|
|
18
|
+
try {
|
|
19
|
+
return (0, fs_1.realpathSync)(p);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return p;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** 将 target 解析为绝对路径,并校验未超出 base(防 ../ 穿越 + 符号链接逃逸) */
|
|
26
|
+
function safeJoin(base, target) {
|
|
27
|
+
const absBase = canonicalIfExists((0, path_1.resolve)(base));
|
|
28
|
+
const rawTarget = (0, path_1.isAbsolute)(target) ? (0, path_1.resolve)(target) : (0, path_1.resolve)(absBase, target);
|
|
29
|
+
const absTarget = canonicalIfExists(rawTarget);
|
|
30
|
+
const rel = (0, path_1.relative)(absBase, absTarget);
|
|
31
|
+
if (rel.startsWith('..') || (rel !== '' && (0, path_1.isAbsolute)(rel))) {
|
|
32
|
+
throw new errors_1.SecurityError(`路径越权: ${target} 超出工作区 ${base}`);
|
|
33
|
+
}
|
|
34
|
+
return absTarget;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=safe-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-path.js","sourceRoot":"","sources":["../../src/tools/safe-path.ts"],"names":[],"mappings":";;AAuBA,4BASC;AAhCD;;;;;GAKG;AACH,+BAAqD;AACrD,2BAAkC;AAClC,6CAAiD;AAEjD;;;GAGG;AACH,SAAS,iBAAiB,CAAC,CAAS;IAClC,IAAI,CAAC;QACH,OAAO,IAAA,iBAAY,EAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,SAAgB,QAAQ,CAAC,IAAY,EAAE,MAAc;IACnD,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAA,cAAO,EAAC,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,IAAA,iBAAU,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAA,cAAO,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAA,cAAO,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClF,MAAM,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAA,eAAQ,EAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACzC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,EAAE,IAAI,IAAA,iBAAU,EAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,sBAAa,CAAC,SAAS,MAAM,UAAU,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.grepTool = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
6
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
7
|
+
*
|
|
8
|
+
* 工具:递归文本搜索
|
|
9
|
+
*/
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
const promises_1 = require("fs/promises");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
const safe_path_1 = require("../safe-path");
|
|
14
|
+
const MAX_DEPTH = 8;
|
|
15
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'out', '.workbuddy']);
|
|
16
|
+
async function walk(dir, depth, pattern, base, hits) {
|
|
17
|
+
if (depth > MAX_DEPTH)
|
|
18
|
+
return;
|
|
19
|
+
let entries;
|
|
20
|
+
try {
|
|
21
|
+
entries = await (0, promises_1.readdir)(dir, { withFileTypes: true });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
for (const e of entries) {
|
|
27
|
+
const abs = (0, path_1.join)(dir, e.name);
|
|
28
|
+
if (e.isDirectory()) {
|
|
29
|
+
if (SKIP_DIRS.has(e.name))
|
|
30
|
+
continue;
|
|
31
|
+
await walk(abs, depth + 1, pattern, base, hits);
|
|
32
|
+
}
|
|
33
|
+
else if (e.isFile()) {
|
|
34
|
+
try {
|
|
35
|
+
const content = await (0, promises_1.readFile)(abs, 'utf8');
|
|
36
|
+
const lines = content.split('\n');
|
|
37
|
+
for (let i = 0; i < lines.length; i++) {
|
|
38
|
+
if (pattern.test(lines[i])) {
|
|
39
|
+
hits.push(`${(0, path_1.relative)(base, abs)}:${i + 1}: ${lines[i].trim().slice(0, 200)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// 二进制/无法读取的文件忽略
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.grepTool = {
|
|
50
|
+
name: 'grep',
|
|
51
|
+
description: '在工作区内递归搜索匹配文本(忽略 node_modules/.git 等),返回 file:line:content',
|
|
52
|
+
jsonSchema: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
pattern: { type: 'string', description: '正则模式' },
|
|
56
|
+
path: { type: 'string', description: '搜索起始目录,默认工作区根' },
|
|
57
|
+
},
|
|
58
|
+
required: ['pattern'],
|
|
59
|
+
},
|
|
60
|
+
schema: zod_1.z.object({ pattern: zod_1.z.string().min(1), path: zod_1.z.string().min(1).optional() }),
|
|
61
|
+
async execute(args, ctx) {
|
|
62
|
+
try {
|
|
63
|
+
const { pattern, path } = args;
|
|
64
|
+
let regex;
|
|
65
|
+
try {
|
|
66
|
+
regex = new RegExp(pattern, 'i');
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return { ok: false, output: '', error: `无效的正则表达式: "${pattern}"` };
|
|
70
|
+
}
|
|
71
|
+
const base = (0, safe_path_1.safeJoin)(ctx.cwd, path ?? '.');
|
|
72
|
+
const hits = [];
|
|
73
|
+
await walk(base, 0, regex, base, hits);
|
|
74
|
+
return { ok: true, output: hits.length ? hits.slice(0, 50).join('\n') : '无匹配' };
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
return { ok: false, output: '', error: `grep 搜索失败: ${err instanceof Error ? err.message : String(err)}` };
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=grep.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grep.tool.js","sourceRoot":"","sources":["../../../src/tools/search/grep.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runCommand = runCommand;
|
|
4
|
+
exports.sanitizeManagedCommand = sanitizeManagedCommand;
|
|
5
|
+
exports.commandHead = commandHead;
|
|
6
|
+
/**
|
|
7
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
8
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
9
|
+
*
|
|
10
|
+
* 命令执行辅助(跨平台,spawn shell)
|
|
11
|
+
*/
|
|
12
|
+
const child_process_1 = require("child_process");
|
|
13
|
+
function runCommand(cmd, cwd, timeoutMs = 120000) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const child = (0, child_process_1.spawn)(cmd, { cwd, shell: true, timeout: timeoutMs });
|
|
16
|
+
let stdout = '';
|
|
17
|
+
let stderr = '';
|
|
18
|
+
child.stdout?.on('data', (d) => (stdout += d.toString()));
|
|
19
|
+
child.stderr?.on('data', (d) => (stderr += d.toString()));
|
|
20
|
+
// spawn 启动失败时只会触发 error 且不再触发 close,必须在此 resolve,否则调用方 await 永久挂起
|
|
21
|
+
child.on('error', (e) => {
|
|
22
|
+
stderr += e.message;
|
|
23
|
+
resolve({ code: 1, stdout, stderr });
|
|
24
|
+
});
|
|
25
|
+
child.on('close', (code) => resolve({ code: code ?? 1, stdout, stderr }));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 受管命令约束(run_tests / build_check 等):仅允许包管理器脚本,
|
|
30
|
+
* 禁止 shell 注入元字符与危险子命令,杜绝任意命令执行(沙箱逃逸)。
|
|
31
|
+
*/
|
|
32
|
+
const MANAGED_INJECTION_RE = /[;&|`$(){}<>!]|&&|\|\||\b(alias|eval|exec|source)\b/;
|
|
33
|
+
const MANAGED_DANGEROUS_RE = /\b(rm\s+-rf|curl|wget|sh\s+-c|bash\s+-c|eval|nc\b|telnet|mkfs)\b/i;
|
|
34
|
+
const MANAGED_PKG_RE = /^(npm|pnpm|yarn|bun)(\s|$)/;
|
|
35
|
+
function sanitizeManagedCommand(raw, fallback = 'npm test') {
|
|
36
|
+
const cmd = (raw ?? fallback).trim();
|
|
37
|
+
if (MANAGED_INJECTION_RE.test(cmd))
|
|
38
|
+
return null;
|
|
39
|
+
if (!MANAGED_PKG_RE.test(cmd))
|
|
40
|
+
return null;
|
|
41
|
+
if (MANAGED_DANGEROUS_RE.test(cmd))
|
|
42
|
+
return null;
|
|
43
|
+
return cmd;
|
|
44
|
+
}
|
|
45
|
+
/** 取命令首词(用于白名单校验) */
|
|
46
|
+
function commandHead(cmd) {
|
|
47
|
+
const m = cmd.trim().match(/^(\S+)/);
|
|
48
|
+
return m ? m[1] : '';
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/tools/shell/exec.ts"],"names":[],"mappings":";;AAcA,gCAcC;AAUD,wDAMC;AAGD,kCAGC;AAlDD;;;;;GAKG;AACH,iDAAsC;AAQtC,SAAgB,UAAU,CAAC,GAAW,EAAE,GAAW,EAAE,SAAS,GAAG,MAAM;IACrE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,IAAA,qBAAK,EAAC,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACnE,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC1D,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC1D,kEAAkE;QAClE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;YACtB,MAAM,IAAI,CAAC,CAAC,OAAO,CAAC;YACpB,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,oBAAoB,GAAG,qDAAqD,CAAC;AACnF,MAAM,oBAAoB,GAAG,mEAAmE,CAAC;AACjG,MAAM,cAAc,GAAG,4BAA4B,CAAC;AAEpD,SAAgB,sBAAsB,CAAC,GAAY,EAAE,QAAQ,GAAG,UAAU;IACxE,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,qBAAqB;AACrB,SAAgB,WAAW,CAAC,GAAW;IACrC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runShellTool = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
6
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
7
|
+
*
|
|
8
|
+
* 工具:执行 shell 命令(白名单 + 审批)
|
|
9
|
+
*/
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
const exec_1 = require("./exec");
|
|
12
|
+
/** Shell 注入元字符 — 命中则需额外审批 */
|
|
13
|
+
const SHELL_INJECTION_RE = /[;&|`$(){}<>!]|&&|\|\||\b(alias|eval|exec|source)\b/;
|
|
14
|
+
exports.runShellTool = {
|
|
15
|
+
name: 'run_shell',
|
|
16
|
+
description: '执行 shell 命令(受白名单约束,需审批时会被拦截)',
|
|
17
|
+
jsonSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: { command: { type: 'string', description: '要执行的完整命令' } },
|
|
20
|
+
required: ['command'],
|
|
21
|
+
},
|
|
22
|
+
schema: zod_1.z.object({ command: zod_1.z.string().min(1) }),
|
|
23
|
+
async execute(args, ctx) {
|
|
24
|
+
const { command } = args;
|
|
25
|
+
const head = (0, exec_1.commandHead)(command);
|
|
26
|
+
// 白名单检查(仅当配置了白名单时约束首词)
|
|
27
|
+
if (ctx.security.shellAllowlist.length > 0) {
|
|
28
|
+
if (!ctx.security.shellAllowlist.includes(head)) {
|
|
29
|
+
return { ok: false, output: '', error: `命令不在白名单: ${head}` };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// 注入元字符校验独立于白名单,始终执行(防止白名单关闭后命令注入逃逸)
|
|
33
|
+
if (SHELL_INJECTION_RE.test(command)) {
|
|
34
|
+
return { ok: false, output: '', error: `命令含 shell 注入风险,已被拦截: ${command.slice(0, 100)}` };
|
|
35
|
+
}
|
|
36
|
+
if (ctx.security.requireApproval) {
|
|
37
|
+
const approved = ctx.approve ? await ctx.approve(`run_shell: ${command}`) : false;
|
|
38
|
+
if (!approved)
|
|
39
|
+
return { ok: false, output: '', error: '已拒绝执行(需审批)' };
|
|
40
|
+
}
|
|
41
|
+
const res = await (0, exec_1.runCommand)(command, ctx.cwd);
|
|
42
|
+
return {
|
|
43
|
+
ok: res.code === 0,
|
|
44
|
+
output: `${res.stdout}${res.stderr}`.slice(0, 4000),
|
|
45
|
+
error: res.code === 0 ? undefined : `exit code ${res.code}`,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=run-shell.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-shell.tool.js","sourceRoot":"","sources":["../../../src/tools/shell/run-shell.tool.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,6BAAwB;AAExB,iCAAiD;AAEjD,6BAA6B;AAC7B,MAAM,kBAAkB,GAAG,qDAAqD,CAAC;AAEpE,QAAA,YAAY,GAAS;IAChC,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,8BAA8B;IAC3C,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE;QACpE,QAAQ,EAAE,CAAC,SAAS,CAAC;KACtB;IACD,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAChD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAgB;QAClC,MAAM,EAAE,OAAO,EAAE,GAAG,IAA2B,CAAC;QAChD,MAAM,IAAI,GAAG,IAAA,kBAAW,EAAC,OAAO,CAAC,CAAC;QAClC,uBAAuB;QACvB,IAAI,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,IAAI,EAAE,EAAE,CAAC;YAC9D,CAAC;QACH,CAAC;QACD,qCAAqC;QACrC,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,wBAAwB,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;QAC3F,CAAC;QACD,IAAI,GAAG,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAClF,IAAI,CAAC,QAAQ;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QACvE,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAA,iBAAU,EAAC,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/C,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC;YAClB,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;YACnD,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,IAAI,EAAE;SAC5D,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toDefinition = toDefinition;
|
|
4
|
+
/** 工具 → 模型可见的工具定义 */
|
|
5
|
+
function toDefinition(tool) {
|
|
6
|
+
return { name: tool.name, description: tool.description, parameters: tool.jsonSchema };
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=tool.interface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.interface.js","sourceRoot":"","sources":["../../src/tools/tool.interface.ts"],"names":[],"mappings":";;AA0DA,oCAEC;AAHD,qBAAqB;AACrB,SAAgB,YAAY,CAAC,IAAU;IACrC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToolRegistry = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
6
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
7
|
+
*
|
|
8
|
+
* 工具注册表:注册、查找、执行(含 zod 校验 + 错误归一)
|
|
9
|
+
*/
|
|
10
|
+
const logger_1 = require("../shared/logger");
|
|
11
|
+
const tool_interface_1 = require("./tool.interface");
|
|
12
|
+
class ToolRegistry {
|
|
13
|
+
tools = new Map();
|
|
14
|
+
register(tool) {
|
|
15
|
+
if (this.tools.has(tool.name)) {
|
|
16
|
+
logger_1.logger.warn('tool overwritten', { name: tool.name });
|
|
17
|
+
}
|
|
18
|
+
this.tools.set(tool.name, tool);
|
|
19
|
+
}
|
|
20
|
+
has(name) {
|
|
21
|
+
return this.tools.has(name);
|
|
22
|
+
}
|
|
23
|
+
get(name) {
|
|
24
|
+
return this.tools.get(name);
|
|
25
|
+
}
|
|
26
|
+
list() {
|
|
27
|
+
return [...this.tools.values()];
|
|
28
|
+
}
|
|
29
|
+
/** 模型可见的工具定义列表 */
|
|
30
|
+
definitions() {
|
|
31
|
+
return this.list().map(tool_interface_1.toDefinition);
|
|
32
|
+
}
|
|
33
|
+
/** 执行工具(自动 zod 校验入参,错误归一为 ToolResult) */
|
|
34
|
+
async execute(name, rawArgs, ctx) {
|
|
35
|
+
const tool = this.tools.get(name);
|
|
36
|
+
if (!tool) {
|
|
37
|
+
return { ok: false, output: '', error: `未知工具: ${name}` };
|
|
38
|
+
}
|
|
39
|
+
const parsed = tool.schema.safeParse(rawArgs);
|
|
40
|
+
if (!parsed.success) {
|
|
41
|
+
return { ok: false, output: '', error: `参数校验失败: ${parsed.error.message}` };
|
|
42
|
+
}
|
|
43
|
+
const args = parsed.data;
|
|
44
|
+
// M4:企业守卫前置(策略 → 审批 → 审计)。未注入 guard 时行为不变。
|
|
45
|
+
if (ctx.guard) {
|
|
46
|
+
let verdict;
|
|
47
|
+
try {
|
|
48
|
+
verdict = await ctx.guard.check(name, args);
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
// 审计写入失败等异常一律按拒绝处理:宁可不做,不可无痕
|
|
52
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
53
|
+
logger_1.logger.error('工具守卫校验异常,已拒绝执行', { tool: name, error: msg });
|
|
54
|
+
return { ok: false, output: '', error: `守卫校验失败,操作被拒绝: ${msg}` };
|
|
55
|
+
}
|
|
56
|
+
if (!verdict.allowed) {
|
|
57
|
+
logger_1.logger.warn('工具调用被守卫拒绝', { tool: name, reason: verdict.reason });
|
|
58
|
+
return { ok: false, output: '', error: verdict.reason };
|
|
59
|
+
}
|
|
60
|
+
// 守卫已是唯一权威闸门(策略 + 审批 + 审计都已完成),
|
|
61
|
+
// 此处关闭工具内的二次审批与白名单硬拦截,避免重复弹审批 / 决策打架。
|
|
62
|
+
ctx = { ...ctx, security: { shellAllowlist: [], requireApproval: false } };
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
return await tool.execute(args, ctx);
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
69
|
+
logger_1.logger.error('tool execution failed', { tool: name, error: msg });
|
|
70
|
+
return { ok: false, output: '', error: msg };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.ToolRegistry = ToolRegistry;
|
|
75
|
+
//# sourceMappingURL=tool.registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.registry.js","sourceRoot":"","sources":["../../src/tools/tool.registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildCheckTool = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
6
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
7
|
+
*
|
|
8
|
+
* 工具:构建检查
|
|
9
|
+
*/
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
const exec_1 = require("../shell/exec");
|
|
12
|
+
exports.buildCheckTool = {
|
|
13
|
+
name: 'build_check',
|
|
14
|
+
description: '执行构建检查(默认 npm run build)',
|
|
15
|
+
jsonSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: { command: { type: 'string', description: '构建命令,默认 npm run build' } },
|
|
18
|
+
},
|
|
19
|
+
schema: zod_1.z.object({ command: zod_1.z.string().min(1).optional() }),
|
|
20
|
+
async execute(args, ctx) {
|
|
21
|
+
const cmd = (0, exec_1.sanitizeManagedCommand)(args.command, 'npm run build');
|
|
22
|
+
if (!cmd) {
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
output: '',
|
|
26
|
+
error: '仅允许 npm/pnpm/yarn/bun 的 test 或 build 等脚本,且禁止命令注入字符(; & | ` $ 等)与危险命令',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const res = await (0, exec_1.runCommand)(cmd, ctx.cwd);
|
|
30
|
+
return {
|
|
31
|
+
ok: res.code === 0,
|
|
32
|
+
output: `${res.stdout}${res.stderr}`.slice(0, 4000),
|
|
33
|
+
error: res.code === 0 ? undefined : `exit code ${res.code}`,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=build-check.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-check.tool.js","sourceRoot":"","sources":["../../../src/tools/verify/build-check.tool.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,6BAAwB;AAExB,wCAAmE;AAEtD,QAAA,cAAc,GAAS;IAClC,IAAI,EAAE,aAAa;IACnB,WAAW,EAAE,0BAA0B;IACvC,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE,EAAE;KAClF;IACD,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC3D,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAgB;QAClC,MAAM,GAAG,GAAG,IAAA,6BAAsB,EAAC,IAAI,CAAC,OAA6B,EAAE,eAAe,CAAC,CAAC;QACxF,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,sEAAsE;aAC9E,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAA,iBAAU,EAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC;YAClB,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;YACnD,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,IAAI,EAAE;SAC5D,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runTestsTool = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
6
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
7
|
+
*
|
|
8
|
+
* 工具:运行测试套件
|
|
9
|
+
*/
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
const exec_1 = require("../shell/exec");
|
|
12
|
+
exports.runTestsTool = {
|
|
13
|
+
name: 'run_tests',
|
|
14
|
+
description: '运行项目测试套件(默认 npm test)',
|
|
15
|
+
jsonSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: { command: { type: 'string', description: '测试命令,默认 npm test' } },
|
|
18
|
+
},
|
|
19
|
+
schema: zod_1.z.object({ command: zod_1.z.string().min(1).optional() }),
|
|
20
|
+
async execute(args, ctx) {
|
|
21
|
+
const cmd = (0, exec_1.sanitizeManagedCommand)(args.command, 'npm test');
|
|
22
|
+
if (!cmd) {
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
output: '',
|
|
26
|
+
error: '仅允许 npm/pnpm/yarn/bun 的 test 或 build 等脚本,且禁止命令注入字符(; & | ` $ 等)与危险命令',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const res = await (0, exec_1.runCommand)(cmd, ctx.cwd);
|
|
30
|
+
return {
|
|
31
|
+
ok: res.code === 0,
|
|
32
|
+
output: `${res.stdout}${res.stderr}`.slice(0, 4000),
|
|
33
|
+
error: res.code === 0 ? undefined : `exit code ${res.code}`,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=test-run.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-run.tool.js","sourceRoot":"","sources":["../../../src/tools/verify/test-run.tool.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,6BAAwB;AAExB,wCAAmE;AAEtD,QAAA,YAAY,GAAS;IAChC,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,uBAAuB;IACpC,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,EAAE;KAC7E;IACD,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC3D,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAgB;QAClC,MAAM,GAAG,GAAG,IAAA,6BAAsB,EAAC,IAAI,CAAC,OAA6B,EAAE,UAAU,CAAC,CAAC;QACnF,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,sEAAsE;aAC9E,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAA,iBAAU,EAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC;YAClB,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;YACnD,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,GAAG,CAAC,IAAI,EAAE;SAC5D,CAAC;IACJ,CAAC;CACF,CAAC"}
|
package/dist/web/auth.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireToken = requireToken;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
/** 计时安全的字符串比较,避免令牌逐字节泄露的计时攻击面 */
|
|
6
|
+
function safeEqual(a, b) {
|
|
7
|
+
const ab = Buffer.from(a);
|
|
8
|
+
const bb = Buffer.from(b);
|
|
9
|
+
if (ab.length !== bb.length)
|
|
10
|
+
return false;
|
|
11
|
+
return (0, crypto_1.timingSafeEqual)(ab, bb);
|
|
12
|
+
}
|
|
13
|
+
/** 返回 Express 中间件:校验 Authorization: Bearer <token> */
|
|
14
|
+
function requireToken(token) {
|
|
15
|
+
return (req, res, next) => {
|
|
16
|
+
const header = req.header('authorization') || '';
|
|
17
|
+
const m = /^Bearer\s+(.+)$/i.exec(header);
|
|
18
|
+
if (!m || !safeEqual(m[1], token)) {
|
|
19
|
+
res.status(401).json({ ok: false, error: 'unauthorized' });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
next();
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/web/auth.ts"],"names":[],"mappings":";;AAmBA,oCAUC;AArBD,mCAAyC;AAEzC,iCAAiC;AACjC,SAAS,SAAS,CAAC,CAAS,EAAE,CAAS;IACrC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,IAAA,wBAAe,EAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,sDAAsD;AACtD,SAAgB,YAAY,CAAC,KAAa;IACxC,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;QACjD,MAAM,CAAC,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;YAClC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;YAC3D,OAAO;QACT,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>飞虹 Code · Web 控制台</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { --brand: #c0392b; --ink: #1f2937; --muted: #6b7280; --bg: #f7f8fa; }
|
|
9
|
+
* { box-sizing: border-box; }
|
|
10
|
+
body { margin: 0; font-family: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
11
|
+
background: var(--bg); color: var(--ink); }
|
|
12
|
+
header { background: var(--brand); color: #fff; padding: 14px 20px; font-weight: 600; letter-spacing: .5px; }
|
|
13
|
+
main { max-width: 760px; margin: 40px auto; padding: 0 20px; }
|
|
14
|
+
.card { background: #fff; border: 1px solid #e5e7eb; border-radius: 10px; padding: 24px; }
|
|
15
|
+
h1 { font-size: 20px; margin: 0 0 12px; }
|
|
16
|
+
p { color: var(--muted); line-height: 1.7; }
|
|
17
|
+
code { background: #f1f3f5; padding: 2px 6px; border-radius: 4px; color: var(--brand); }
|
|
18
|
+
.sig { margin-top: 28px; font-size: 12px; color: #9ca3af; border-top: 1px dashed #e5e7eb; padding-top: 14px; }
|
|
19
|
+
.badge { display: inline-block; background: #e8f5e9; color: #2e7d32; border-radius: 999px;
|
|
20
|
+
padding: 2px 10px; font-size: 12px; margin-bottom: 12px; }
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<header>飞虹 Code · Web 控制台</header>
|
|
25
|
+
<main>
|
|
26
|
+
<div class="card">
|
|
27
|
+
<span class="badge">M5 骨架就绪</span>
|
|
28
|
+
<h1>飞虹 Code Web 控制台(M5 建设中)</h1>
|
|
29
|
+
<p>
|
|
30
|
+
S1 服务骨架 + Bearer 令牌鉴权 + <code>GET /api/health</code> 已打通。
|
|
31
|
+
观测 API(租户 / 策略 / 审计链 / 会话 / 配额)将在 <strong>S2</strong> 接入,
|
|
32
|
+
可视化仪表盘将在 <strong>S3</strong> 填充。
|
|
33
|
+
</p>
|
|
34
|
+
<p>本页为占位页,S3 上线后由完整仪表盘替换。</p>
|
|
35
|
+
<p class="sig">晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹</p>
|
|
36
|
+
</div>
|
|
37
|
+
</main>
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startWebServer = startWebServer;
|
|
7
|
+
/**
|
|
8
|
+
* 飞虹 Code (Muse Code 参照复刻)
|
|
9
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
10
|
+
*
|
|
11
|
+
* M5 Web 控制台:自包含 Express 服务骨架。
|
|
12
|
+
* S1:服务骨架 + 鉴权 + /api/health(占位;S2 扩展观测 API,S3 填充仪表盘)。
|
|
13
|
+
*/
|
|
14
|
+
const express_1 = __importDefault(require("express"));
|
|
15
|
+
const crypto_1 = require("crypto");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const auth_1 = require("./auth");
|
|
18
|
+
const version_1 = require("../cli/version");
|
|
19
|
+
const enterprise_1 = require("../enterprise");
|
|
20
|
+
/**
|
|
21
|
+
* 启动 Web 控制台。
|
|
22
|
+
* - 端口:opts.port > FH_WEB_PORT > 8080
|
|
23
|
+
* - 令牌:opts.token > FH_WEB_TOKEN > 自动生成(仅本次会话有效,fail-closed)
|
|
24
|
+
*/
|
|
25
|
+
function startWebServer(opts = {}) {
|
|
26
|
+
const port = opts.port ?? Number(process.env.FH_WEB_PORT ?? 8080);
|
|
27
|
+
let token = opts.token ?? process.env.FH_WEB_TOKEN ?? '';
|
|
28
|
+
if (!token) {
|
|
29
|
+
token = (0, crypto_1.randomBytes)(24).toString('hex');
|
|
30
|
+
console.log(`[飞虹 Code] Web 控制台令牌已自动生成(FH_WEB_TOKEN): ${token}`);
|
|
31
|
+
}
|
|
32
|
+
const app = (0, express_1.default)();
|
|
33
|
+
// M8 修复:限制请求体大小,防止超大 payload 触发内存耗尽(DoS 面)
|
|
34
|
+
app.use(express_1.default.json({ limit: '1mb' }));
|
|
35
|
+
// 静态仪表盘(S3 填充;S1 占位页)
|
|
36
|
+
const publicDir = (0, path_1.join)(__dirname, 'public');
|
|
37
|
+
app.use(express_1.default.static(publicDir));
|
|
38
|
+
// 鉴权:全部 /api 需 Bearer token(静态资源除外)
|
|
39
|
+
app.use('/api', (0, auth_1.requireToken)(token));
|
|
40
|
+
app.get('/api/health', (_req, res) => {
|
|
41
|
+
res.json({
|
|
42
|
+
ok: true,
|
|
43
|
+
product: version_1.PRODUCT,
|
|
44
|
+
version: version_1.VERSION,
|
|
45
|
+
signature: version_1.SIGNATURE,
|
|
46
|
+
enterprise: (0, enterprise_1.isEnterpriseEnabled)(),
|
|
47
|
+
time: new Date().toISOString(),
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
const server = app.listen(port, () => {
|
|
51
|
+
console.log(`[飞虹 Code] Web 控制台已启动: http://localhost:${port}`);
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
port,
|
|
55
|
+
token,
|
|
56
|
+
url: `http://localhost:${port}`,
|
|
57
|
+
close: () => server.close(),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":";;;;;AA+BA,wCAwCC;AAvED;;;;;;GAMG;AACH,sDAA+D;AAC/D,mCAAqC;AACrC,+BAA4B;AAC5B,iCAAsC;AACtC,4CAA6D;AAC7D,8CAAoD;AAcpD;;;;GAIG;AACH,SAAgB,cAAc,CAAC,OAAqB,EAAE;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;IAClE,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;IACzD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,2CAA2C,KAAK,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;IACtB,2CAA2C;IAC3C,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAExC,sBAAsB;IACtB,MAAM,SAAS,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC5C,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IAEnC,oCAAoC;IACpC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,IAAA,mBAAY,EAAC,KAAK,CAAC,CAAC,CAAC;IAErC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QACtD,GAAG,CAAC,IAAI,CAAC;YACP,EAAE,EAAE,IAAI;YACR,OAAO,EAAE,iBAAO;YAChB,OAAO,EAAE,iBAAO;YAChB,SAAS,EAAE,mBAAS;YACpB,UAAU,EAAE,IAAA,gCAAmB,GAAE;YACjC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAC/B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;QACnC,OAAO,CAAC,GAAG,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,IAAI;QACJ,KAAK;QACL,GAAG,EAAE,oBAAoB,IAAI,EAAE;QAC/B,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;KAC5B,CAAC;AACJ,CAAC"}
|