ppxans-harness 2.4.0 → 3.2.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/LICENSE +201 -201
- package/README.md +218 -265
- package/bin/ppx-channels.js +2 -2
- package/bin/ppx-serve.js +5 -5
- package/bin/ppx-setup.js +124 -0
- package/bin/ppx-web.js +140 -0
- package/bin/ppx.js +2 -2
- package/config/identity.md +6 -6
- package/config/ishiki.md +16 -16
- package/config/ppx.json +15 -151
- package/config/ppx.json.example +143 -0
- package/package.json +17 -10
- package/skills/.usage.json +6 -0
- package/skills/agent-professional-training/SKILL.md +94 -0
- package/skills/brainstorm/SKILL.md +24 -0
- package/skills/cupid-lover-comms/SKILL.md +37 -0
- package/skills/debug/SKILL.md +26 -0
- package/skills/plan/SKILL.md +25 -0
- package/skills/ponytail/SKILL.md +25 -0
- package/skills/ppx-memory/SKILL.md +91 -0
- package/skills/ppx-memory/scripts/cli.js +192 -0
- package/skills/ppx-memory/scripts/experience.js +133 -0
- package/skills/ppx-memory/scripts/fact-store.js +842 -0
- package/skills/ppx-memory/scripts/l0.js +52 -0
- package/skills/ppx-memory/scripts/l2.js +146 -0
- package/skills/ppx-memory/scripts/l3.js +112 -0
- package/skills/ppx-memory/scripts/memory-ticker.js +238 -0
- package/skills/ppx-memory/scripts/pii.js +42 -0
- package/skills/ppx-memory/scripts/schema.js +80 -0
- package/skills/ppx-memory/scripts/session.js +398 -0
- package/skills/ppx-memory/scripts/similarity.js +43 -0
- package/skills/ppx-memory/scripts/store.js +116 -0
- package/skills/ppx-memory/scripts/wal.js +38 -0
- package/skills/ppx-selfheal/SKILL.md +24 -0
- package/skills/ppx-selfheal/scripts/cli.js +80 -0
- package/skills/ppx-selfheal/scripts/healer.js +184 -0
- package/skills/ppx-selfheal/scripts/logger.js +17 -0
- package/skills/ppx-selfheal/scripts/store.js +116 -0
- package/skills/prompt-depth-kit/SKILL.md +28 -0
- package/skills/session-naming/SKILL.md +36 -0
- package/skills/verify/SKILL.md +25 -0
- package/src/agent/index.js +1340 -717
- package/src/agent/prompts.js +47 -3
- package/src/aml-server.js +197 -151
- package/src/ans/eviction.js +123 -143
- package/src/ans/guard.js +159 -120
- package/src/ans/lifecycle.js +96 -93
- package/src/ans/proactive.js +112 -129
- package/src/ans/reward.js +95 -111
- package/src/ans/values.js +15 -15
- package/src/audit/audit-chain.js +43 -7
- package/src/audit/verifier.js +157 -120
- package/src/bus/circuit-breaker.js +9 -1
- package/src/bus/runtime-bus.js +107 -93
- package/src/channels/base.js +57 -34
- package/src/channels/feishu.js +118 -126
- package/src/channels/http.js +1090 -592
- package/src/channels/index.js +111 -110
- package/src/channels/log.js +29 -29
- package/src/channels/wechat-crypto.js +73 -74
- package/src/channels/wechat.js +191 -197
- package/src/channels/workspace.js +94 -0
- package/src/channels-cli.js +126 -124
- package/src/cli.js +129 -120
- package/src/commands/index.js +142 -0
- package/src/config/channels.js +137 -170
- package/src/config/index.js +277 -224
- package/src/config/placeholder.js +31 -0
- package/src/config/providers.js +148 -188
- package/src/config/settings.js +156 -182
- package/src/core/policy.js +69 -18
- package/src/core/trace.js +8 -6
- package/src/edit/editblock.js +266 -0
- package/src/edit/snapshot.js +67 -0
- package/src/evidence/index.js +153 -0
- package/src/evolve/playbook.js +9 -10
- package/src/hooks/index.js +113 -0
- package/src/llm/client.js +188 -446
- package/src/llm/dsml.js +74 -74
- package/src/llm/embedder.js +41 -35
- package/src/llm/fence.js +52 -105
- package/src/llm/index.js +4 -4
- package/src/llm/local-embedder.js +94 -0
- package/src/llm/presets.js +113 -0
- package/src/llm/pricing.js +93 -0
- package/src/llm/retry.js +73 -73
- package/src/llm/router.js +92 -97
- package/src/mcp/admin.js +326 -0
- package/src/mcp/client.js +487 -375
- package/src/mcp/http.js +203 -0
- package/src/mcp/index.js +116 -116
- package/src/mcp/server.js +392 -0
- package/src/mcp/tasks.js +133 -0
- package/src/memory/asset-hub.js +6 -11
- package/src/memory/canvas.js +2 -5
- package/src/memory/compaction.js +28 -28
- package/src/memory/experience.js +133 -122
- package/src/memory/fact-store.js +914 -698
- package/src/memory/failure-episode.js +20 -11
- package/src/memory/fork.js +17 -8
- package/src/memory/index.js +8 -6
- package/src/memory/l0.js +53 -52
- package/src/memory/l2.js +145 -130
- package/src/memory/l3.js +111 -111
- package/src/memory/legion-board.js +71 -0
- package/src/memory/memory-ticker.js +239 -240
- package/src/memory/session.js +398 -397
- package/src/memory/sqlite-store.js +581 -0
- package/src/mode/blackboard.js +49 -49
- package/src/mode/graph.js +42 -41
- package/src/mode/index.js +64 -64
- package/src/mode/legion.js +54 -51
- package/src/mode/plan-exec.js +50 -50
- package/src/mode/router.js +28 -40
- package/src/orchestrator/agent-worker.js +69 -69
- package/src/orchestrator/dag.js +90 -83
- package/src/orchestrator/experts.js +76 -0
- package/src/orchestrator/index.js +1 -1
- package/src/orchestrator/legion.js +179 -187
- package/src/orchestrator/supervisor.js +6 -8
- package/src/permissions/index.js +378 -0
- package/src/persona/index.js +28 -29
- package/src/plugin/builtin.js +313 -212
- package/src/plugin/context.js +80 -79
- package/src/plugin/index.js +62 -62
- package/src/plugin/v3.js +73 -0
- package/src/protocol/index.js +148 -0
- package/src/repomap/index.js +309 -0
- package/src/review/index.js +393 -0
- package/src/seam/registry.js +3 -0
- package/src/seam/shell.js +55 -55
- package/src/security/injection.js +79 -0
- package/src/selfheal/evolve.js +67 -67
- package/src/selfheal/healer.js +184 -167
- package/src/selfheal/run.js +9 -9
- package/src/server.js +63 -60
- package/src/services/diagnose.js +180 -0
- package/src/services/learning-service.js +9 -0
- package/src/services/memory-health.js +34 -6
- package/src/services/memory-service.js +42 -9
- package/src/services/triage.js +138 -0
- package/src/session/parts.js +76 -0
- package/src/session/projection.js +73 -0
- package/src/session/rollout.js +54 -0
- package/src/session/turn.js +137 -0
- package/src/skills/lint.js +72 -0
- package/src/skills/loader.js +231 -150
- package/src/skills/search.js +58 -0
- package/src/skills/verify.js +95 -100
- package/src/tools/advanced.js +388 -352
- package/src/tools/builtin.js +384 -297
- package/src/tools/catalog.js +283 -159
- package/src/tools/command-guard.js +112 -112
- package/src/tools/custom.js +47 -47
- package/src/tools/delegate.js +383 -297
- package/src/tools/document.js +254 -253
- package/src/tools/git.js +151 -0
- package/src/tools/governance.js +47 -20
- package/src/tools/index.js +16 -11
- package/src/tools/methods.js +178 -178
- package/src/tools/ocr.js +59 -59
- package/src/tools/sandbox-worker.js +40 -0
- package/src/tools/sandbox.js +92 -0
- package/src/tools/seam.js +162 -125
- package/src/tools/selfmod.js +196 -176
- package/src/tools/v3.js +225 -0
- package/src/tools/vad.js +176 -0
- package/src/tools/voice.js +238 -0
- package/src/utils/async.js +14 -0
- package/src/utils/config-file.js +53 -0
- package/src/utils/crashguard.js +88 -0
- package/src/utils/http.js +53 -0
- package/src/utils/id.js +8 -0
- package/src/utils/json-state.js +33 -0
- package/src/utils/logger.js +17 -17
- package/src/utils/ndjson.js +25 -0
- package/src/utils/pii.js +42 -42
- package/src/utils/rate-limit.js +50 -0
- package/src/utils/schema.js +80 -0
- package/src/utils/similarity.js +43 -0
- package/src/utils/store.js +170 -108
- package/src/utils/text.js +15 -15
- package/src/utils/trace.js +153 -153
- package/src/utils/wal.js +39 -0
- package/src/utils/winutf8.js +16 -15
- package/src/wiki/index.js +170 -0
package/src/selfheal/healer.js
CHANGED
|
@@ -1,167 +1,184 @@
|
|
|
1
|
-
// src/selfheal/healer.js - 自愈引擎
|
|
2
|
-
// 1. 启动检查: 修复缺失目录/损坏JSON/权限
|
|
3
|
-
// 2. 崩溃恢复: 检测上次异常退出, 清理残留
|
|
4
|
-
// 3. 数据一致性: 校验记忆文件
|
|
5
|
-
import fs from "node:fs";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { ensureDir, readJson } from "../utils/store.js";
|
|
8
|
-
import { info, warn,
|
|
9
|
-
|
|
10
|
-
export class Healer {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
1
|
+
// src/selfheal/healer.js - 自愈引擎
|
|
2
|
+
// 1. 启动检查: 修复缺失目录/损坏JSON/权限
|
|
3
|
+
// 2. 崩溃恢复: 检测上次异常退出, 清理残留
|
|
4
|
+
// 3. 数据一致性: 校验记忆文件
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { ensureDir, readJson } from "../utils/store.js";
|
|
8
|
+
import { info, warn, debug } from "../utils/logger.js";
|
|
9
|
+
|
|
10
|
+
export class Healer {
|
|
11
|
+
// dataDir 可选: 显式传入真实数据目录 (PPX_DATA_DIR 可能指向非默认位置)。
|
|
12
|
+
// v1.0.8 修复 (P1-2): 原实现把 dataDir 硬编码为 path.join(rootDir, "data"),
|
|
13
|
+
// 而调用方 builtin.js 传进来的是 root 而非数据目录 —— 一旦 PPX_DATA_DIR 改到别处,
|
|
14
|
+
// 自愈就会在错误的目录里创建 memory/experience/logs、写 integrity.json、清理 .tmp,
|
|
15
|
+
// 形成"数据目录 / 自愈目录"分叉: 真实数据目录永不被体检, 空目录反而被反复重建。
|
|
16
|
+
// 默认值保留 rootDir/data, 让既有 15 处 `new Healer(root)` 调用点行为不变 (向后兼容)。
|
|
17
|
+
constructor(rootDir, dataDir = null) {
|
|
18
|
+
this.root = rootDir;
|
|
19
|
+
this.dataDir = dataDir || path.join(rootDir, "data");
|
|
20
|
+
this.integrity = path.join(this.dataDir, "integrity.json");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 启动体检: 返回修复清单
|
|
24
|
+
runStartupChecks() {
|
|
25
|
+
const fixes = [];
|
|
26
|
+
ensureDir(this.dataDir);
|
|
27
|
+
for (const sub of ["memory", "memory/daily", "experience", "sessions", "logs"]) {
|
|
28
|
+
const d = path.join(this.dataDir, sub);
|
|
29
|
+
if (!fs.existsSync(d)) { fs.mkdirSync(d, { recursive: true }); fixes.push(`created dir: ${sub}`); }
|
|
30
|
+
}
|
|
31
|
+
// 校验关键 JSON 可解析
|
|
32
|
+
const facts = path.join(this.dataDir, "memory", "facts.json");
|
|
33
|
+
if (fs.existsSync(facts)) {
|
|
34
|
+
const parsed = readJson(facts, null);
|
|
35
|
+
if (parsed === null) {
|
|
36
|
+
// 损坏: 备份后重建
|
|
37
|
+
const bak = facts + ".corrupt-" + Date.now();
|
|
38
|
+
fs.renameSync(facts, bak);
|
|
39
|
+
fs.writeFileSync(facts, "[]", "utf8");
|
|
40
|
+
fixes.push(`facts.json corrupt -> backed up to ${path.basename(bak)}, reset`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return fixes;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 崩溃恢复: 检查上次是否干净退出
|
|
47
|
+
checkCrash() {
|
|
48
|
+
const state = readJson(this.integrity, { clean: true, pid: null });
|
|
49
|
+
const result = { crashed: false, detail: null };
|
|
50
|
+
if (state.clean === false) {
|
|
51
|
+
result.crashed = true;
|
|
52
|
+
result.detail = `上次进程 (pid=${state.pid}) 未干净退出, 可能残留临时文件`;
|
|
53
|
+
this._cleanupTmp();
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_cleanupTmp() {
|
|
59
|
+
const walk = (dir) => {
|
|
60
|
+
if (!fs.existsSync(dir)) return;
|
|
61
|
+
for (const f of fs.readdirSync(dir)) {
|
|
62
|
+
const p = path.join(dir, f);
|
|
63
|
+
let st;
|
|
64
|
+
try { st = fs.statSync(p); } catch { continue; } // 并发删除竞态容错
|
|
65
|
+
if (st.isDirectory()) walk(p);
|
|
66
|
+
else if (f.endsWith(".tmp")) { try { fs.unlinkSync(p); } catch (e) { debug(`[selfheal/healer] 已忽略异常: ${e && e.message ? e.message : e}`); } info(`cleaned tmp: ${f}`); }
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
walk(this.dataDir);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
markDirty() {
|
|
73
|
+
ensureDir(this.dataDir);
|
|
74
|
+
fs.writeFileSync(this.integrity, JSON.stringify({ clean: false, pid: process.pid, ts: Date.now() }), "utf8");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
markClean() {
|
|
78
|
+
ensureDir(this.dataDir);
|
|
79
|
+
fs.writeFileSync(this.integrity, JSON.stringify({ clean: true, pid: process.pid, ts: Date.now() }), "utf8");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---- 备份清理 (2026-09-18 重构: 原先三个方法各抄一份「收集→按 mtime 排序→保留最近 N→删其余」) ----
|
|
83
|
+
|
|
84
|
+
// 通用清理器: collect() 给出候选 [{p, mtime}] → 按 mtime 新→旧排序 → 保留最近 keep 个, 其余逐个 remove
|
|
85
|
+
// 单个删除失败只告警不中断 (与原来逐方法 try/catch 语义一致)。
|
|
86
|
+
_pruneByMtime({ keep, collect, remove, logLabel, warnLabel }) {
|
|
87
|
+
const all = collect();
|
|
88
|
+
all.sort((a, b) => b.mtime - a.mtime);
|
|
89
|
+
const removed = [];
|
|
90
|
+
for (const item of all.slice(keep)) {
|
|
91
|
+
try { remove(item.p); removed.push(path.basename(item.p)); }
|
|
92
|
+
catch (e) { warn(`清理${warnLabel}失败: ${item.p}: ${e.message}`); }
|
|
93
|
+
}
|
|
94
|
+
if (removed.length) info(`selfheal: 清理旧${logLabel} ${removed.length} 个: ${removed.join(", ")}`);
|
|
95
|
+
return removed;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 递归收集 dataDir 下满足 pred(fileName) 的文件 (带 mtime)
|
|
99
|
+
_collectFilesBy(pred) {
|
|
100
|
+
const found = [];
|
|
101
|
+
const walk = (d) => {
|
|
102
|
+
if (!fs.existsSync(d)) return;
|
|
103
|
+
for (const f of fs.readdirSync(d)) {
|
|
104
|
+
const p = path.join(d, f);
|
|
105
|
+
let st;
|
|
106
|
+
try { st = fs.statSync(p); } catch { continue; }
|
|
107
|
+
if (st.isDirectory()) walk(p);
|
|
108
|
+
else if (pred(f)) found.push({ p, mtime: st.mtimeMs });
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
walk(this.dataDir);
|
|
112
|
+
return found;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 清理历史 corrupt 备份: 保留最近 N 个, 更早自动删除 (默认保留 2)
|
|
116
|
+
cleanupCorruptBackups(keep = 2) {
|
|
117
|
+
return this._pruneByMtime({
|
|
118
|
+
keep,
|
|
119
|
+
collect: () => this._collectFilesBy((f) => f.includes(".corrupt-")),
|
|
120
|
+
remove: (p) => fs.unlinkSync(p),
|
|
121
|
+
logLabel: "corrupt 备份",
|
|
122
|
+
warnLabel: "损坏备份",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 清理历史手动备份目录 (memory-backup-*): 保留最近 N 个, 更早自动删除 (默认保留 2)
|
|
127
|
+
// 手动全量备份目录不在 corrupt 备份机制内, 若无清理会持续累积磁盘占用
|
|
128
|
+
cleanupStaleBackupDirs(keep = 2) {
|
|
129
|
+
return this._pruneByMtime({
|
|
130
|
+
keep,
|
|
131
|
+
collect: () => {
|
|
132
|
+
if (!fs.existsSync(this.dataDir)) return [];
|
|
133
|
+
const dirs = [];
|
|
134
|
+
for (const f of fs.readdirSync(this.dataDir)) {
|
|
135
|
+
if (!f.startsWith("memory-backup-")) continue;
|
|
136
|
+
const p = path.join(this.dataDir, f);
|
|
137
|
+
let st;
|
|
138
|
+
try { st = fs.statSync(p); } catch { continue; }
|
|
139
|
+
if (st.isDirectory()) dirs.push({ p, mtime: st.mtimeMs });
|
|
140
|
+
}
|
|
141
|
+
return dirs;
|
|
142
|
+
},
|
|
143
|
+
remove: (p) => fs.rmSync(p, { recursive: true, force: true }),
|
|
144
|
+
logLabel: "手动备份目录",
|
|
145
|
+
warnLabel: "过期备份目录",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 清理历史 .bak-* 文件 (去重/迁移等一次性工具留下的手动备份): 保留最近 N 个, 更早自动删除 (默认保留 2)
|
|
150
|
+
// 覆盖 facts.json.bak-* / lessons.json.bak-* 等, 防手动备份文件在 data/ 内无限累积
|
|
151
|
+
cleanupStaleBakFiles(keep = 2) {
|
|
152
|
+
return this._pruneByMtime({
|
|
153
|
+
keep,
|
|
154
|
+
collect: () => this._collectFilesBy((f) => f.includes(".bak-") && !f.endsWith(".lock")),
|
|
155
|
+
remove: (p) => fs.unlinkSync(p),
|
|
156
|
+
logLabel: ".bak-* 备份文件",
|
|
157
|
+
warnLabel: "过期备份文件",
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 完整自愈入口
|
|
162
|
+
// 2026-09-17 体检修复: 原实现把"崩溃残留已清理, 状态置回 clean"打在"修复 N 项"与
|
|
163
|
+
// "检测到崩溃残留 -> ..."之前, 日志里表现为「先说痊愈、再说发现崩溃」的因果颠倒,
|
|
164
|
+
// 使用者会怀疑自愈到底有没有生效。现按"先报问题 → 再报处置 → 最后报结果"的顺序输出。
|
|
165
|
+
heal() {
|
|
166
|
+
// 1) 先判定上次是否崩溃 (必须在 runStartupChecks 之前判定, 否则重建的目录会掩盖证据)
|
|
167
|
+
const crash = this.checkCrash();
|
|
168
|
+
if (crash.crashed) warn(`selfheal: 检测到崩溃残留 -> ${crash.detail}`);
|
|
169
|
+
// 2) 启动体检 + 备份清理
|
|
170
|
+
const fixes = this.runStartupChecks();
|
|
171
|
+
// 清理历史 corrupt 备份 (保留最近 2 个, 更早自动删除) — 之前漏调用导致 corrupt 持续累积
|
|
172
|
+
const cleanedCorrupt = this.cleanupCorruptBackups(2);
|
|
173
|
+
// 清理历史手动备份目录 (保留最近 2 个)
|
|
174
|
+
const cleanedBackupDirs = this.cleanupStaleBackupDirs(2);
|
|
175
|
+
// 清理历史 .bak-* 文件 (保留最近 2 个)
|
|
176
|
+
const cleanedBakFiles = this.cleanupStaleBakFiles(2);
|
|
177
|
+
// 3) 处置回执: 崩溃残留清理后主动翻回 clean —— 自愈闭环, 清了毒就要"宣布痊愈",
|
|
178
|
+
// 否则下次启动还误报崩溃
|
|
179
|
+
if (crash.crashed) { this.markClean(); info("selfheal: 崩溃残留已清理, 状态置回 clean"); }
|
|
180
|
+
if (fixes.length) info(`selfheal: 修复 ${fixes.length} 项: ${fixes.join("; ")}`);
|
|
181
|
+
else info("selfheal: 无异常");
|
|
182
|
+
return { fixes, crashed: crash.crashed, crashDetail: crash.detail, cleanedCorrupt, cleanedBackupDirs, cleanedBakFiles };
|
|
183
|
+
}
|
|
184
|
+
}
|
package/src/selfheal/run.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// src/selfheal/run.js - 自愈命令行入口
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { Healer } from "./healer.js";
|
|
5
|
-
|
|
6
|
-
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
7
|
-
const healer = new Healer(root);
|
|
8
|
-
const report = healer.heal();
|
|
9
|
-
console.log(JSON.stringify(report, null, 2));
|
|
1
|
+
// src/selfheal/run.js - 自愈命令行入口
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Healer } from "./healer.js";
|
|
5
|
+
|
|
6
|
+
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
7
|
+
const healer = new Healer(root);
|
|
8
|
+
const report = healer.heal();
|
|
9
|
+
console.log(JSON.stringify(report, null, 2));
|
package/src/server.js
CHANGED
|
@@ -1,60 +1,63 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// src/server.js - 皮皮虾 HTTP 服务入口
|
|
3
|
-
// 提供: /health, /message (HTTP通道), /feishu/webhook, /wechat/webhook
|
|
4
|
-
// 通道统一走 ChannelManager 注册表: connect + webhook 挂载由各通道 mount() 完成
|
|
5
|
-
import { ensureUTF8Console } from "./utils/winutf8.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
channelsCfg
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
manager,
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
console.log(
|
|
50
|
-
console.log(`
|
|
51
|
-
console.log(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// src/server.js - 皮皮虾 HTTP 服务入口
|
|
3
|
+
// 提供: /health, /message (HTTP通道), /feishu/webhook, /wechat/webhook
|
|
4
|
+
// 通道统一走 ChannelManager 注册表: connect + webhook 挂载由各通道 mount() 完成
|
|
5
|
+
import { ensureUTF8Console } from "./utils/winutf8.js";
|
|
6
|
+
import { installCrashGuard } from "./utils/crashguard.js";
|
|
7
|
+
import { PPXAgent } from "./agent/index.js";
|
|
8
|
+
import { ChannelManager } from "./channels/index.js";
|
|
9
|
+
|
|
10
|
+
ensureUTF8Console();
|
|
11
|
+
export async function startServer({ root = process.cwd(), port = 8899, host = "127.0.0.1", config = {}, llm = null } = {}) {
|
|
12
|
+
const agent = new PPXAgent({ root });
|
|
13
|
+
if (llm) {
|
|
14
|
+
// 测试注入 stub LLM, 避免依赖真实网络; 同步把 allProviders 替换为同源 stub,
|
|
15
|
+
// 让 /api/providers/test 等"按 id 取客户端"的路径也能命中 stub
|
|
16
|
+
agent.llm = llm;
|
|
17
|
+
if (Array.isArray(agent.allProviders)) agent.allProviders = [llm];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 统一走注册表: HTTP 通道始终启动 (port/host 参数覆盖配置), 其余按 config.channels
|
|
21
|
+
// 基础配置来自 agent.config (config/ppx.json 加载的), 调用方传入的 config.channels 只做覆盖
|
|
22
|
+
const baseChannels = agent.config.channels || {};
|
|
23
|
+
const channelsCfg = { ...baseChannels, ...((config.channels) || {}) };
|
|
24
|
+
channelsCfg.http = { ...(channelsCfg.http || {}), enabled: true, port, host }; // port/host 参数最高优先级
|
|
25
|
+
const manager = new ChannelManager(agent, channelsCfg);
|
|
26
|
+
await manager.start();
|
|
27
|
+
|
|
28
|
+
// 主动提醒 → 广播到所有已启用通道 (agent.proactive.enabled 才接; 默认关不打扰)
|
|
29
|
+
if (agent.config.agent?.proactive?.enabled) {
|
|
30
|
+
agent.startProactiveTicker((payload) => manager.broadcast(payload.text));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const server = manager.httpServer;
|
|
34
|
+
return {
|
|
35
|
+
agent,
|
|
36
|
+
server,
|
|
37
|
+
http: manager.get("http"),
|
|
38
|
+
feishu: manager.get("feishu"),
|
|
39
|
+
wechat: manager.get("wechat"),
|
|
40
|
+
manager,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// CLI 启动公共逻辑: 供 bin/ppx-serve 与 src/server.js 直接运行共用
|
|
45
|
+
export async function runServer({ root = process.cwd(), port = Number(process.env.PPX_PORT || 8899) } = {}) {
|
|
46
|
+
// 全局异常兜底 (P1): 工具/通道里的意外异常不应让整个服务消失
|
|
47
|
+
installCrashGuard({ tag: "ppx-serve" });
|
|
48
|
+
const { agent, server, manager } = await startServer({ root, port });
|
|
49
|
+
console.log(`皮皮虾 服务已启动: http://127.0.0.1:${port}`);
|
|
50
|
+
console.log(` /health 健康检查`);
|
|
51
|
+
console.log(` /message HTTP 对话通道 (POST {message})`);
|
|
52
|
+
console.log(` 通道: ${manager.list().filter((c) => c.enabled).map((c) => c.name).join(", ") || "(无)"}`);
|
|
53
|
+
console.log(` 工具: ${agent.tools.list().join(", ")}`);
|
|
54
|
+
console.log(" Ctrl+C 退出");
|
|
55
|
+
process.on("SIGINT", () => { agent.shutdown(); process.exit(0); });
|
|
56
|
+
return { agent, server, manager };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 直接运行 (node src/server.js, scripts/start-web.js)
|
|
60
|
+
const _entry = (process.argv[1] || "").replace(/\\/g, "/");
|
|
61
|
+
if (_entry.endsWith("src/server.js")) {
|
|
62
|
+
await runServer({ root: process.cwd() });
|
|
63
|
+
}
|