feihong-code 7.6.0 → 8.0.0
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 +36 -22
- package/.github/ISSUE_TEMPLATE/config.yml +10 -13
- package/.github/ISSUE_TEMPLATE/feature_request.md +38 -13
- package/.github/PULL_REQUEST_TEMPLATE.md +65 -30
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/release.yml +5 -5
- package/.github/workflows/swebench-real.yml +2 -2
- package/CONTRIBUTING.md +52 -3
- package/dist/cli/version.js +1 -1
- package/dist/memory/honcho-store.js +231 -0
- package/dist/memory/honcho-store.js.map +1 -0
- package/dist/shared/log-rotator.js +238 -0
- package/dist/shared/log-rotator.js.map +1 -0
- package/dist/shared/sqlite-store.js +512 -0
- package/dist/shared/sqlite-store.js.map +1 -0
- package/dist/tools/docker-sandbox.js +212 -0
- package/dist/tools/docker-sandbox.js.map +1 -0
- package/dist/web/public/favicon.svg +11 -0
- package/dist/web/public/index.html +4 -1
- package/dist/web/public/robots.txt +14 -0
- package/dist/web/server.js +70 -0
- package/dist/web/server.js.map +1 -1
- package/docs/API.md +827 -0
- package/docs/PROJECT-ANALYSIS-AND-TEST.md +721 -0
- package/docs/TECHNICAL-SPECIFICATION.md +746 -0
- package/docs/UPGRADE-v7.9.0.md +291 -0
- package/docs/UPGRADE-v8.0.0.md +237 -0
- package/docs//344/275/277/347/224/250/350/257/264/346/230/216/344/271/246-v8.0.0.md +922 -0
- package/package.json +6 -6
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 飞虹 Code — Docker 沙盒执行层(v8.0)
|
|
4
|
+
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
5
|
+
*
|
|
6
|
+
* 对齐 sandbox.ts 的 'container' 沙箱模式:
|
|
7
|
+
* - shell 命令在 Docker 容器内执行(docker run 挂载工作区)
|
|
8
|
+
* - 镜像由 FH_SANDBOX_IMAGE 配置,默认 node:22-alpine
|
|
9
|
+
* - 网络受限:默认禁用(--network none),可 FH_SANDBOX_NETWORK=true 开启
|
|
10
|
+
* - 资源受限:内存/CPU 上限可配置
|
|
11
|
+
* - 只读挂载模式可选(防止容器内修改宿主文件)
|
|
12
|
+
*
|
|
13
|
+
* 设计:
|
|
14
|
+
* - 惰性拉取镜像(首次运行时 docker pull)
|
|
15
|
+
* - 每次执行创建一次性容器(--rm),执行完自动销毁
|
|
16
|
+
* - 超时强杀(docker kill)
|
|
17
|
+
* - 不依赖 dockerode(减少依赖),用子进程调用 docker CLI
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.DockerSandbox = void 0;
|
|
21
|
+
exports.getDockerSandbox = getDockerSandbox;
|
|
22
|
+
exports.resetDockerSandbox = resetDockerSandbox;
|
|
23
|
+
const child_process_1 = require("child_process");
|
|
24
|
+
const util_1 = require("util");
|
|
25
|
+
const fs_1 = require("fs");
|
|
26
|
+
const path_1 = require("path");
|
|
27
|
+
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
28
|
+
const DEFAULT_OPTIONS = {
|
|
29
|
+
image: 'node:22-alpine',
|
|
30
|
+
workspaceDir: process.cwd(),
|
|
31
|
+
mountPoint: '/workspace',
|
|
32
|
+
memoryLimit: '512m',
|
|
33
|
+
cpuLimit: '1',
|
|
34
|
+
networkEnabled: false,
|
|
35
|
+
readOnly: true,
|
|
36
|
+
timeoutMs: 60000,
|
|
37
|
+
};
|
|
38
|
+
class DockerSandbox {
|
|
39
|
+
options;
|
|
40
|
+
debugEnabled;
|
|
41
|
+
imageReady = false;
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
44
|
+
this.debugEnabled = options.debug || false;
|
|
45
|
+
if (process.env.FH_SANDBOX_IMAGE)
|
|
46
|
+
this.options.image = process.env.FH_SANDBOX_IMAGE;
|
|
47
|
+
if (process.env.FH_SANDBOX_NETWORK === 'true')
|
|
48
|
+
this.options.networkEnabled = true;
|
|
49
|
+
if (process.env.FH_SANDBOX_READONLY === 'false')
|
|
50
|
+
this.options.readOnly = false;
|
|
51
|
+
// 确保工作区目录存在
|
|
52
|
+
if (!(0, fs_1.existsSync)(this.options.workspaceDir)) {
|
|
53
|
+
(0, fs_1.mkdirSync)(this.options.workspaceDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
debug(...args) {
|
|
57
|
+
if (this.debugEnabled)
|
|
58
|
+
console.log('[DockerSandbox]', ...args);
|
|
59
|
+
}
|
|
60
|
+
/** 检查 Docker CLI 与 daemon 是否均可用(docker info 而非 --version,确保 daemon 在运行) */
|
|
61
|
+
static async isDockerAvailable() {
|
|
62
|
+
try {
|
|
63
|
+
const { stdout } = await execFileAsync('docker', ['info', '--format', '{{.ServerVersion}}']);
|
|
64
|
+
return /^\d+\.\d+\.\d+/.test(stdout.trim());
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** 确保镜像已存在(首次执行自动拉取) */
|
|
71
|
+
async ensureImage() {
|
|
72
|
+
if (this.imageReady)
|
|
73
|
+
return;
|
|
74
|
+
this.debug(`检查镜像 ${this.options.image}...`);
|
|
75
|
+
try {
|
|
76
|
+
const { stdout } = await execFileAsync('docker', ['image', 'inspect', this.options.image]);
|
|
77
|
+
if (stdout) {
|
|
78
|
+
this.imageReady = true;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch { /* 镜像不存在,需要拉取 */ }
|
|
83
|
+
this.debug(`拉取镜像 ${this.options.image}...`);
|
|
84
|
+
await new Promise((resolve, reject) => {
|
|
85
|
+
const proc = (0, child_process_1.spawn)('docker', ['pull', this.options.image], { stdio: this.debugEnabled ? 'inherit' : 'ignore' });
|
|
86
|
+
proc.on('close', (code) => {
|
|
87
|
+
if (code === 0) {
|
|
88
|
+
this.imageReady = true;
|
|
89
|
+
resolve();
|
|
90
|
+
}
|
|
91
|
+
else
|
|
92
|
+
reject(new Error(`镜像拉取失败(exit ${code}): ${this.options.image}`));
|
|
93
|
+
});
|
|
94
|
+
proc.on('error', reject);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** 在 Docker 容器中执行命令 */
|
|
98
|
+
async execute(command, options = {}) {
|
|
99
|
+
const start = Date.now();
|
|
100
|
+
const dockerAvailable = await DockerSandbox.isDockerAvailable();
|
|
101
|
+
if (!dockerAvailable) {
|
|
102
|
+
return {
|
|
103
|
+
success: false, exitCode: -1, stdout: '', stderr: 'Docker 不可用:请安装并启动 Docker Desktop',
|
|
104
|
+
timedOut: false, durationMs: Date.now() - start,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
await this.ensureImage();
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
return {
|
|
112
|
+
success: false, exitCode: -1, stdout: '',
|
|
113
|
+
stderr: `镜像准备失败: ${e instanceof Error ? e.message : String(e)}`,
|
|
114
|
+
timedOut: false, durationMs: Date.now() - start,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// 构造 docker run 参数
|
|
118
|
+
const dockerArgs = ['run', '--rm'];
|
|
119
|
+
dockerArgs.push('--name', `fh-sandbox-${Date.now()}`);
|
|
120
|
+
dockerArgs.push('-m', this.options.memoryLimit);
|
|
121
|
+
dockerArgs.push('--cpus', this.options.cpuLimit);
|
|
122
|
+
dockerArgs.push('--network', this.options.networkEnabled ? 'bridge' : 'none');
|
|
123
|
+
dockerArgs.push('--workdir', this.options.mountPoint);
|
|
124
|
+
dockerArgs.push('--mount', `type=bind,source=${this.options.workspaceDir},target=${this.options.mountPoint},readonly=${this.options.readOnly}`);
|
|
125
|
+
if (options.workdir)
|
|
126
|
+
dockerArgs.push('--workdir', options.workdir);
|
|
127
|
+
// 环境变量
|
|
128
|
+
for (const [k, v] of Object.entries(options.env || {})) {
|
|
129
|
+
dockerArgs.push('-e', `${k}=${v}`);
|
|
130
|
+
}
|
|
131
|
+
// 危险命令拒绝列表(纵深防御,即使容器内也拦截)
|
|
132
|
+
const DANGEROUS_PATTERNS = [
|
|
133
|
+
/rm\s+-rf\s+\/(?!workspace)/, // 删除根目录(排除 workspace)
|
|
134
|
+
/mkfs/, // 格式化
|
|
135
|
+
/dd\s+if=.*of=\/dev/, // 写入块设备
|
|
136
|
+
/:\(\)\s*\{\s*:\|:&\s*\};:/, // fork 炸弹
|
|
137
|
+
];
|
|
138
|
+
for (const pat of DANGEROUS_PATTERNS) {
|
|
139
|
+
if (pat.test(command)) {
|
|
140
|
+
return {
|
|
141
|
+
success: false, exitCode: -1, stdout: '',
|
|
142
|
+
stderr: `危险命令被容器沙盒拦截: ${pat}`,
|
|
143
|
+
timedOut: false, durationMs: Date.now() - start,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// 容器内执行命令(用 sh -c)
|
|
148
|
+
dockerArgs.push(this.options.image);
|
|
149
|
+
dockerArgs.push('/bin/sh', '-c', command);
|
|
150
|
+
this.debug('docker run 参数:', dockerArgs.join(' '));
|
|
151
|
+
return new Promise((resolve) => {
|
|
152
|
+
const proc = (0, child_process_1.spawn)('docker', dockerArgs, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
153
|
+
let stdout = '';
|
|
154
|
+
let stderr = '';
|
|
155
|
+
let timedOut = false;
|
|
156
|
+
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
157
|
+
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
158
|
+
const timer = setTimeout(() => {
|
|
159
|
+
timedOut = true;
|
|
160
|
+
// 超时强杀:通过容器名 kill
|
|
161
|
+
const containerName = dockerArgs[dockerArgs.indexOf('--name') + 1];
|
|
162
|
+
(0, child_process_1.execFile)('docker', ['kill', containerName], () => { });
|
|
163
|
+
proc.kill('SIGKILL');
|
|
164
|
+
}, this.options.timeoutMs);
|
|
165
|
+
proc.on('close', (code) => {
|
|
166
|
+
clearTimeout(timer);
|
|
167
|
+
resolve({
|
|
168
|
+
success: code === 0,
|
|
169
|
+
exitCode: code ?? -1,
|
|
170
|
+
stdout,
|
|
171
|
+
stderr,
|
|
172
|
+
timedOut,
|
|
173
|
+
durationMs: Date.now() - start,
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
proc.on('error', (e) => {
|
|
177
|
+
clearTimeout(timer);
|
|
178
|
+
resolve({
|
|
179
|
+
success: false, exitCode: -1, stdout, stderr: `执行失败: ${e.message}`,
|
|
180
|
+
timedOut, durationMs: Date.now() - start,
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/** 便捷:在容器中运行脚本文件 */
|
|
186
|
+
async runScript(scriptPath, options = {}) {
|
|
187
|
+
const absPath = (0, path_1.join)(this.options.workspaceDir, scriptPath);
|
|
188
|
+
if (!(0, fs_1.existsSync)(absPath)) {
|
|
189
|
+
return {
|
|
190
|
+
success: false, exitCode: -1, stdout: '', stderr: `脚本不存在: ${scriptPath}`,
|
|
191
|
+
timedOut: false, durationMs: 0,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return this.execute(`node ${this.options.mountPoint}/${scriptPath}`, options);
|
|
195
|
+
}
|
|
196
|
+
/** 获取沙盒配置信息 */
|
|
197
|
+
getConfig() {
|
|
198
|
+
return { ...this.options };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
exports.DockerSandbox = DockerSandbox;
|
|
202
|
+
/** 全局单例 */
|
|
203
|
+
let sandboxInstance = null;
|
|
204
|
+
function getDockerSandbox(options) {
|
|
205
|
+
if (!sandboxInstance)
|
|
206
|
+
sandboxInstance = new DockerSandbox(options);
|
|
207
|
+
return sandboxInstance;
|
|
208
|
+
}
|
|
209
|
+
function resetDockerSandbox() {
|
|
210
|
+
sandboxInstance = null;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=docker-sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-sandbox.js","sourceRoot":"","sources":["../../src/tools/docker-sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
+
<stop offset="0%" style="stop-color:#4F46E5;stop-opacity:1" />
|
|
5
|
+
<stop offset="100%" style="stop-color:#7C3AED;stop-opacity:1" />
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect width="64" height="64" rx="12" fill="url(#bg)"/>
|
|
9
|
+
<path d="M20 22 L20 42 L28 42 L28 30 L36 42 L44 42 L44 22 L36 22 L36 34 L28 22 Z" fill="white"/>
|
|
10
|
+
<circle cx="48" cy="18" r="4" fill="#10B981"/>
|
|
11
|
+
</svg>
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title data-i18n="app.title_login">飞虹 Code 控制台 / fhcode Console</title>
|
|
7
|
-
<link rel="icon"
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
|
8
|
+
<link rel="apple-touch-icon" href="favicon.svg" />
|
|
9
|
+
<meta name="theme-color" content="#4F46E5" />
|
|
10
|
+
<meta name="description" content="飞虹 Code — 终端 AI 编程智能体" />
|
|
8
11
|
<link rel="stylesheet" href="css/style.css">
|
|
9
12
|
<!-- Monaco Editor AMD Loader(代码编辑器内核,VSCode 同款) -->
|
|
10
13
|
<script src="vendor/monaco/vs/loader.js"></script>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# robots.txt for 飞虹 Code (feihong-code)
|
|
2
|
+
# https://github.com/feihong-code/feihong-code
|
|
3
|
+
|
|
4
|
+
User-agent: *
|
|
5
|
+
Allow: /
|
|
6
|
+
Disallow: /api/
|
|
7
|
+
Disallow: /admin/
|
|
8
|
+
Disallow: /dashboard/
|
|
9
|
+
|
|
10
|
+
# Sitemap
|
|
11
|
+
# Sitemap: https://feihong-code.com/sitemap.xml
|
|
12
|
+
|
|
13
|
+
# Crawl-delay
|
|
14
|
+
Crawl-delay: 1
|
package/dist/web/server.js
CHANGED
|
@@ -85,6 +85,9 @@ const wechat_bridge_1 = require("../integrations/wechat-bridge");
|
|
|
85
85
|
const feishu_bridge_1 = require("../integrations/feishu-bridge");
|
|
86
86
|
const yuanbao_bridge_1 = require("../integrations/yuanbao-bridge");
|
|
87
87
|
const collaboration_1 = require("../integrations/collaboration");
|
|
88
|
+
// v8.0:SQLite 数据存储 + Honcho 云端记忆(本地部署)
|
|
89
|
+
const sqlite_store_1 = require("../shared/sqlite-store");
|
|
90
|
+
const honcho_store_1 = require("../memory/honcho-store");
|
|
88
91
|
/**
|
|
89
92
|
* 启动 Web 控制台。
|
|
90
93
|
* - 端口:opts.port > FH_WEB_PORT > 8080
|
|
@@ -160,6 +163,21 @@ function startWebServer(opts = {}) {
|
|
|
160
163
|
const yuanbaoConfig = (0, yuanbao_bridge_1.initYuanbaoBridge)();
|
|
161
164
|
// 公开健康检查(仅暴露版本/状态等观测信息,无敏感数据)
|
|
162
165
|
app.get('/api/health', (_req, res) => {
|
|
166
|
+
let storage = null;
|
|
167
|
+
let honcho = null;
|
|
168
|
+
try {
|
|
169
|
+
storage = (0, sqlite_store_1.getStore)().health();
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
storage = { ok: false, backend: 'error', dbFile: '' };
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const h = (0, honcho_store_1.getHonchoStore)().health();
|
|
176
|
+
honcho = { ok: h.ok, backend: h.backend };
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
honcho = { ok: false, backend: 'unavailable' };
|
|
180
|
+
}
|
|
163
181
|
res.json({
|
|
164
182
|
ok: true,
|
|
165
183
|
product: version_1.PRODUCT,
|
|
@@ -170,6 +188,9 @@ function startWebServer(opts = {}) {
|
|
|
170
188
|
wechat: (0, wechat_bridge_1.isWechatEnabled)(wechatConfig) ? wechatConfig.mode : 'disabled',
|
|
171
189
|
feishu: (0, feishu_bridge_1.isFeishuEnabled)(feishuConfig) ? 'enabled' : 'disabled',
|
|
172
190
|
yuanbao: (0, yuanbao_bridge_1.isYuanbaoEnabled)(yuanbaoConfig) ? 'enabled' : 'disabled',
|
|
191
|
+
// v8.0:SQLite 数据存储 + Honcho 云端记忆状态
|
|
192
|
+
storage,
|
|
193
|
+
honcho,
|
|
173
194
|
time: new Date().toISOString(),
|
|
174
195
|
});
|
|
175
196
|
});
|
|
@@ -323,6 +344,55 @@ function startWebServer(opts = {}) {
|
|
|
323
344
|
const history = (0, auto_summarize_1.getSummaryHistory)(memoryConfig, limit);
|
|
324
345
|
res.json({ ok: true, history });
|
|
325
346
|
});
|
|
347
|
+
// ===== v8.0:SQLite 数据存储 + Honcho 云端记忆 API =====
|
|
348
|
+
app.get('/api/storage/stats', (_req, res) => {
|
|
349
|
+
try {
|
|
350
|
+
const stats = (0, sqlite_store_1.getStore)().stats();
|
|
351
|
+
res.json({ ok: true, ...stats });
|
|
352
|
+
}
|
|
353
|
+
catch (e) {
|
|
354
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
app.get('/api/honcho/context', (_req, res) => {
|
|
358
|
+
try {
|
|
359
|
+
const honcho = (0, honcho_store_1.getHonchoStore)();
|
|
360
|
+
res.json({
|
|
361
|
+
ok: true,
|
|
362
|
+
profile: honcho.getUserProfile(),
|
|
363
|
+
memories: honcho.listMemories(10),
|
|
364
|
+
contextPrompt: honcho.buildContextPrompt(),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
app.post('/api/honcho/remember', (req, res) => {
|
|
372
|
+
try {
|
|
373
|
+
const body = (req.body || {});
|
|
374
|
+
if (!body.content) {
|
|
375
|
+
res.status(400).json({ ok: false, error: 'content required' });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
(0, honcho_store_1.getHonchoStore)().rememberFact(body.content, body.importance);
|
|
379
|
+
res.json({ ok: true });
|
|
380
|
+
}
|
|
381
|
+
catch (e) {
|
|
382
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
app.get('/api/honcho/recall', (req, res) => {
|
|
386
|
+
try {
|
|
387
|
+
const q = typeof req.query.q === 'string' ? req.query.q : '';
|
|
388
|
+
const limit = typeof req.query.limit === 'string' ? parseInt(req.query.limit) || 5 : 5;
|
|
389
|
+
const result = (0, honcho_store_1.getHonchoStore)().recall(q, limit);
|
|
390
|
+
res.json({ ok: true, ...result });
|
|
391
|
+
}
|
|
392
|
+
catch (e) {
|
|
393
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
394
|
+
}
|
|
395
|
+
});
|
|
326
396
|
app.post('/api/auth/clear-first-login', (req, res) => {
|
|
327
397
|
const header = req.header('authorization') || '';
|
|
328
398
|
const m = /^Bearer\s+(.+)$/i.exec(header);
|