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.
@@ -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" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect width='100' height='100' rx='20' fill='%231a1a2e'/><text x='50' y='68' font-size='52' font-family='monospace' font-weight='bold' fill='%2300d4aa' text-anchor='middle'>&lt;/&gt;</text></svg>" />
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
@@ -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);