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.
Files changed (154) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +14 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +28 -0
  4. package/.github/PULL_REQUEST_TEMPLATE.md +46 -0
  5. package/.github/SECURITY.md +67 -0
  6. package/.github/workflows/ci.yml +140 -0
  7. package/AGENT-GUIDE.md +237 -0
  8. package/CHANGELOG.md +73 -0
  9. package/CODE_OF_CONDUCT.md +56 -0
  10. package/CONTRIBUTING.md +68 -0
  11. package/LICENSE +22 -0
  12. package/README.md +500 -0
  13. package/dist/agent/code-review.js +115 -0
  14. package/dist/agent/code-review.js.map +1 -0
  15. package/dist/agent/code-writer.js +227 -0
  16. package/dist/agent/code-writer.js.map +1 -0
  17. package/dist/agent/context-compactor.js +108 -0
  18. package/dist/agent/context-compactor.js.map +1 -0
  19. package/dist/agent/experience.js +190 -0
  20. package/dist/agent/experience.js.map +1 -0
  21. package/dist/agent/orchestrator.js +212 -0
  22. package/dist/agent/orchestrator.js.map +1 -0
  23. package/dist/agent/parallel-orchestrator.js +94 -0
  24. package/dist/agent/parallel-orchestrator.js.map +1 -0
  25. package/dist/agent/planner.js +53 -0
  26. package/dist/agent/planner.js.map +1 -0
  27. package/dist/agent/prompts.js +26 -0
  28. package/dist/agent/prompts.js.map +1 -0
  29. package/dist/agent/quality-gate.js +143 -0
  30. package/dist/agent/quality-gate.js.map +1 -0
  31. package/dist/agent/repo-reader.js +384 -0
  32. package/dist/agent/repo-reader.js.map +1 -0
  33. package/dist/agent/repo-underwriter.js +132 -0
  34. package/dist/agent/repo-underwriter.js.map +1 -0
  35. package/dist/agent/self-heal.js +134 -0
  36. package/dist/agent/self-heal.js.map +1 -0
  37. package/dist/agent/self-improver.js +148 -0
  38. package/dist/agent/self-improver.js.map +1 -0
  39. package/dist/agent/subagent.js +75 -0
  40. package/dist/agent/subagent.js.map +1 -0
  41. package/dist/agent/swe-agent.js +162 -0
  42. package/dist/agent/swe-agent.js.map +1 -0
  43. package/dist/agent/swe-planner.js +155 -0
  44. package/dist/agent/swe-planner.js.map +1 -0
  45. package/dist/agent/swe-verifier.js +113 -0
  46. package/dist/agent/swe-verifier.js.map +1 -0
  47. package/dist/cli/commands.js +167 -0
  48. package/dist/cli/commands.js.map +1 -0
  49. package/dist/cli/index.js +179 -0
  50. package/dist/cli/index.js.map +1 -0
  51. package/dist/cli/repl.js +67 -0
  52. package/dist/cli/repl.js.map +1 -0
  53. package/dist/cli/run.js +762 -0
  54. package/dist/cli/run.js.map +1 -0
  55. package/dist/cli/version.js +14 -0
  56. package/dist/cli/version.js.map +1 -0
  57. package/dist/enterprise/audit.js +294 -0
  58. package/dist/enterprise/audit.js.map +1 -0
  59. package/dist/enterprise/guard.js +58 -0
  60. package/dist/enterprise/guard.js.map +1 -0
  61. package/dist/enterprise/index.js +99 -0
  62. package/dist/enterprise/index.js.map +1 -0
  63. package/dist/enterprise/policy.js +242 -0
  64. package/dist/enterprise/policy.js.map +1 -0
  65. package/dist/enterprise/quota.js +58 -0
  66. package/dist/enterprise/quota.js.map +1 -0
  67. package/dist/enterprise/tenant.js +143 -0
  68. package/dist/enterprise/tenant.js.map +1 -0
  69. package/dist/models/cost.js +13 -0
  70. package/dist/models/cost.js.map +1 -0
  71. package/dist/models/model-router.js +170 -0
  72. package/dist/models/model-router.js.map +1 -0
  73. package/dist/models/model.dto.js +61 -0
  74. package/dist/models/model.dto.js.map +1 -0
  75. package/dist/models/model.interface.js +3 -0
  76. package/dist/models/model.interface.js.map +1 -0
  77. package/dist/models/providers/mock.provider.js +35 -0
  78. package/dist/models/providers/mock.provider.js.map +1 -0
  79. package/dist/models/providers/ollama.provider.js +88 -0
  80. package/dist/models/providers/ollama.provider.js.map +1 -0
  81. package/dist/models/providers/openai-compatible.provider.js +106 -0
  82. package/dist/models/providers/openai-compatible.provider.js.map +1 -0
  83. package/dist/runtime/event-log.js +42 -0
  84. package/dist/runtime/event-log.js.map +1 -0
  85. package/dist/runtime/git.js +106 -0
  86. package/dist/runtime/git.js.map +1 -0
  87. package/dist/runtime/session-persist.js +60 -0
  88. package/dist/runtime/session-persist.js.map +1 -0
  89. package/dist/runtime/session-store.js +34 -0
  90. package/dist/runtime/session-store.js.map +1 -0
  91. package/dist/runtime/worktree.js +117 -0
  92. package/dist/runtime/worktree.js.map +1 -0
  93. package/dist/shared/config.js +184 -0
  94. package/dist/shared/config.js.map +1 -0
  95. package/dist/shared/errors.js +66 -0
  96. package/dist/shared/errors.js.map +1 -0
  97. package/dist/shared/logger.js +51 -0
  98. package/dist/shared/logger.js.map +1 -0
  99. package/dist/shared/types.js +9 -0
  100. package/dist/shared/types.js.map +1 -0
  101. package/dist/skills/goal.js +60 -0
  102. package/dist/skills/goal.js.map +1 -0
  103. package/dist/skills/grill.js +129 -0
  104. package/dist/skills/grill.js.map +1 -0
  105. package/dist/skills/plan.js +40 -0
  106. package/dist/skills/plan.js.map +1 -0
  107. package/dist/tools/analysis/code-analyzer.js +136 -0
  108. package/dist/tools/analysis/code-analyzer.js.map +1 -0
  109. package/dist/tools/file/edit.tool.js +43 -0
  110. package/dist/tools/file/edit.tool.js.map +1 -0
  111. package/dist/tools/file/list.tool.js +36 -0
  112. package/dist/tools/file/list.tool.js.map +1 -0
  113. package/dist/tools/file/read.tool.js +34 -0
  114. package/dist/tools/file/read.tool.js.map +1 -0
  115. package/dist/tools/file/write.tool.js +39 -0
  116. package/dist/tools/file/write.tool.js.map +1 -0
  117. package/dist/tools/generator/code-generator.js +119 -0
  118. package/dist/tools/generator/code-generator.js.map +1 -0
  119. package/dist/tools/generator/test-generator.js +65 -0
  120. package/dist/tools/generator/test-generator.js.map +1 -0
  121. package/dist/tools/index.js +53 -0
  122. package/dist/tools/index.js.map +1 -0
  123. package/dist/tools/safe-path.js +36 -0
  124. package/dist/tools/safe-path.js.map +1 -0
  125. package/dist/tools/search/grep.tool.js +81 -0
  126. package/dist/tools/search/grep.tool.js.map +1 -0
  127. package/dist/tools/shell/exec.js +50 -0
  128. package/dist/tools/shell/exec.js.map +1 -0
  129. package/dist/tools/shell/run-shell.tool.js +49 -0
  130. package/dist/tools/shell/run-shell.tool.js.map +1 -0
  131. package/dist/tools/tool.interface.js +8 -0
  132. package/dist/tools/tool.interface.js.map +1 -0
  133. package/dist/tools/tool.registry.js +75 -0
  134. package/dist/tools/tool.registry.js.map +1 -0
  135. package/dist/tools/verify/build-check.tool.js +37 -0
  136. package/dist/tools/verify/build-check.tool.js.map +1 -0
  137. package/dist/tools/verify/test-run.tool.js +37 -0
  138. package/dist/tools/verify/test-run.tool.js.map +1 -0
  139. package/dist/web/auth.js +25 -0
  140. package/dist/web/auth.js.map +1 -0
  141. package/dist/web/public/index.html +39 -0
  142. package/dist/web/server.js +60 -0
  143. package/dist/web/server.js.map +1 -0
  144. package/docs//344/272/247/345/223/201/345/274/200/345/217/221/346/226/207/346/241/243.md +614 -0
  145. 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
  146. package/docs//344/275/277/347/224/250/350/257/264/346/230/216/344/271/246.md +358 -0
  147. 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
  148. package/docs//346/212/200/346/234/257/350/257/264/346/230/216/344/271/246.md +303 -0
  149. package/docs//346/236/266/346/236/204/344/270/216API.md +238 -0
  150. package/docs//347/224/250/346/210/267/346/211/213/345/206/214.md +196 -0
  151. package/docs//351/203/250/347/275/262/346/214/207/345/215/227.md +165 -0
  152. package/docs//351/205/215/347/275/256/345/217/202/350/200/203.md +127 -0
  153. package/package.json +83 -0
  154. 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"}
@@ -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"}