





















@@ -0,0 +1,174 @@
1+import fs from "node:fs";
2+import path from "node:path";
3+4+const command = process.argv[2];
5+6+function requireEnv(name) {
7+const value = process.env[name];
8+if (!value) {
9+throw new Error(`${name} is required`);
10+}
11+return value;
12+}
13+14+function readJson(file) {
15+return JSON.parse(fs.readFileSync(file, "utf8"));
16+}
17+18+function write(file, contents) {
19+fs.mkdirSync(path.dirname(file), { recursive: true });
20+fs.writeFileSync(file, contents);
21+}
22+23+function writeJson(file, value) {
24+write(file, `${JSON.stringify(value, null, 2)}\n`);
25+}
26+27+function assert(condition, message) {
28+if (!condition) {
29+throw new Error(message);
30+}
31+}
32+33+function getConfig() {
34+return readJson(requireEnv("OPENCLAW_CONFIG_PATH"));
35+}
36+37+function seedState() {
38+const stateDir = requireEnv("OPENCLAW_STATE_DIR");
39+const workspace = requireEnv("OPENCLAW_TEST_WORKSPACE_DIR");
40+41+write(
42+path.join(workspace, "IDENTITY.md"),
43+"# Upgrade Survivor\n\nThis workspace must survive package update and doctor repair.\n",
44+);
45+writeJson(path.join(workspace, ".openclaw", "workspace-state.json"), {
46+version: 1,
47+setupCompletedAt: "2026-04-01T00:00:00.000Z",
48+});
49+writeJson(path.join(stateDir, "agents", "main", "sessions", "legacy-session.json"), {
50+id: "legacy-session",
51+agentId: "main",
52+title: "Existing user session",
53+});
54+55+const runtimeRoot = path.join(stateDir, "plugin-runtime-deps");
56+for (const plugin of ["discord", "telegram", "whatsapp"]) {
57+writeJson(path.join(runtimeRoot, plugin, ".openclaw-runtime-deps-stamp.json"), {
58+version: 0,
59+ plugin,
60+stale: true,
61+});
62+write(
63+path.join(
64+runtimeRoot,
65+plugin,
66+".openclaw-runtime-deps-copy-stale",
67+"node_modules",
68+"stale-sentinel",
69+"package.json",
70+),
71+`${JSON.stringify({ name: "stale-sentinel", version: "0.0.0" }, null, 2)}\n`,
72+);
73+}
74+75+writeJson(path.join(stateDir, "survivor-baseline.json"), {
76+agents: ["main", "ops"],
77+discordGuild: "222222222222222222",
78+discordChannel: "333333333333333333",
79+telegramGroup: "-1001234567890",
80+whatsappGroup: "120363000000000000@g.us",
81+workspaceIdentity: path.join(workspace, "IDENTITY.md"),
82+});
83+}
84+85+function assertConfigSurvived() {
86+const config = getConfig();
87+assert(config.update?.channel === "stable", "update.channel was not preserved");
88+assert(config.gateway?.auth?.mode === "token", "gateway auth mode was not preserved");
89+90+const agents = config.agents?.list ?? [];
91+assert(Array.isArray(agents), "agents.list missing after update/doctor");
92+assert(
93+agents.some((agent) => agent?.id === "main"),
94+"main agent missing",
95+);
96+assert(
97+agents.some((agent) => agent?.id === "ops"),
98+"ops agent missing",
99+);
100+assert(
101+agents.find((agent) => agent?.id === "main")?.contextTokens === 64000,
102+"main agent contextTokens changed",
103+);
104+assert(
105+agents.find((agent) => agent?.id === "ops")?.fastModeDefault === true,
106+"ops fastModeDefault changed",
107+);
108+109+const discord = config.channels?.discord;
110+assert(discord?.enabled === true, "discord enabled flag changed");
111+const discordAllowFrom = discord.allowFrom ?? discord.dm?.allowFrom;
112+const discordDmPolicy = discord.dmPolicy ?? discord.dm?.policy;
113+assert(discordDmPolicy === "allowlist", "discord DM policy changed");
114+assert(
115+Array.isArray(discordAllowFrom) && discordAllowFrom.includes("111111111111111111"),
116+"discord allowFrom changed",
117+);
118+assert(
119+discord.guilds?.["222222222222222222"]?.channels?.["333333333333333333"]?.requireMention ===
120+true,
121+"discord guild channel mention policy changed",
122+);
123+assert(discord.threadBindings?.idleHours === 72, "discord thread binding ttl changed");
124+125+assert(config.channels?.telegram?.enabled === true, "telegram enabled flag changed");
126+assert(
127+config.channels?.telegram?.groups?.["-1001234567890"]?.requireMention === true,
128+"telegram group policy changed",
129+);
130+assert(config.channels?.whatsapp?.enabled === true, "whatsapp enabled flag changed");
131+assert(
132+config.channels?.whatsapp?.groups?.["120363000000000000@g.us"]?.systemPrompt ===
133+"Use the existing WhatsApp group prompt.",
134+"whatsapp group policy changed",
135+);
136+137+const pluginAllow = config.plugins?.allow ?? [];
138+assert(pluginAllow.includes("discord"), "discord plugin allow entry missing");
139+assert(pluginAllow.includes("telegram"), "telegram plugin allow entry missing");
140+assert(pluginAllow.includes("whatsapp"), "whatsapp plugin allow entry missing");
141+}
142+143+function assertStateSurvived() {
144+const stateDir = requireEnv("OPENCLAW_STATE_DIR");
145+const workspace = requireEnv("OPENCLAW_TEST_WORKSPACE_DIR");
146+assert(fs.existsSync(path.join(workspace, "IDENTITY.md")), "workspace identity file missing");
147+assert(
148+fs.existsSync(path.join(stateDir, "agents", "main", "sessions", "legacy-session.json")),
149+"legacy session file missing",
150+);
151+assert(
152+fs.existsSync(path.join(stateDir, "plugin-runtime-deps", "discord")),
153+"plugin runtime deps root missing",
154+);
155+}
156+157+function assertStatusJson([file]) {
158+const status = readJson(file);
159+assert(status && typeof status === "object", "gateway status JSON was not an object");
160+const text = JSON.stringify(status);
161+assert(/running|connected|ok|ready/u.test(text), "gateway status did not report a healthy state");
162+}
163+164+if (command === "seed") {
165+seedState();
166+} else if (command === "assert-config") {
167+assertConfigSurvived();
168+} else if (command === "assert-state") {
169+assertStateSurvived();
170+} else if (command === "assert-status-json") {
171+assertStatusJson(process.argv.slice(3));
172+} else {
173+throw new Error(`unknown upgrade-survivor assertion command: ${command ?? "<missing>"}`);
174+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。