






















@@ -1,11 +1,82 @@
1+import fsSync from "node:fs";
12import fs from "node:fs/promises";
23import path from "node:path";
3-import { afterEach, describe, expect, it } from "vitest";
4+import { afterEach, describe, expect, it, vi } from "vitest";
45import type { OpenClawConfig } from "../config/config.js";
56import { resolveChannelAllowFromPath } from "../pairing/pairing-store.js";
67import { createTrackedTempDirs } from "../test-utils/tracked-temp-dirs.js";
78import { detectLegacyStateMigrations, runLegacyStateMigrations } from "./state-migrations.js";
8910+vi.mock("../channels/plugins/bundled.js", () => {
11+function fileExists(filePath: string): boolean {
12+try {
13+return fsSync.statSync(filePath).isFile();
14+} catch {
15+return false;
16+}
17+}
18+19+function resolveChatAppAccountId(cfg: OpenClawConfig): string {
20+const channel = (cfg.channels as Record<string, { defaultAccount?: string }> | undefined)
21+?.chatapp;
22+return channel?.defaultAccount ?? "default";
23+}
24+25+return {
26+listBundledChannelLegacySessionSurfaces: vi.fn(() => [
27+{
28+isLegacyGroupSessionKey: (key: string) => /^group:mobile-/i.test(key.trim()),
29+canonicalizeLegacySessionKey: ({ key, agentId }: { key: string; agentId: string }) =>
30+/^group:mobile-/i.test(key.trim())
31+ ? `agent:${agentId}:mobileauth:${key.trim().toLowerCase()}`
32+ : null,
33+},
34+]),
35+listBundledChannelLegacyStateMigrationDetectors: vi.fn(() => [
36+({ oauthDir }: { oauthDir: string }) => {
37+let entries: fsSync.Dirent[] = [];
38+try {
39+entries = fsSync.readdirSync(oauthDir, { withFileTypes: true });
40+} catch {
41+return [];
42+}
43+return entries.flatMap((entry) => {
44+if (!entry.isFile() || !/^(creds|pre-key-1)\.json$/u.test(entry.name)) {
45+return [];
46+}
47+const sourcePath = path.join(oauthDir, entry.name);
48+const targetPath = path.join(oauthDir, "mobileauth", "default", entry.name);
49+return fileExists(targetPath)
50+ ? []
51+ : [
52+{
53+kind: "move" as const,
54+label: `MobileAuth auth ${entry.name}`,
55+ sourcePath,
56+ targetPath,
57+},
58+];
59+});
60+},
61+({ cfg, env }: { cfg: OpenClawConfig; env: NodeJS.ProcessEnv }) => {
62+const root = env.OPENCLAW_STATE_DIR;
63+if (!root) {
64+return [];
65+}
66+const sourcePath = path.join(root, "credentials", "chatapp-allowFrom.json");
67+const targetPath = path.join(
68+root,
69+"credentials",
70+`chatapp-${resolveChatAppAccountId(cfg)}-allowFrom.json`,
71+);
72+return fileExists(sourcePath) && !fileExists(targetPath)
73+ ? [{ kind: "copy" as const, label: "ChatApp pairing allowFrom", sourcePath, targetPath }]
74+ : [];
75+},
76+]),
77+};
78+});
79+980const tempDirs = createTrackedTempDirs();
1081const createTempDir = () => tempDirs.make("openclaw-state-migrations-test-");
1182@@ -18,7 +89,7 @@ function createConfig(): OpenClawConfig {
1889mainKey: "desk",
1990},
2091channels: {
21-telegram: {
92+chatapp: {
2293defaultAccount: "alpha",
2394accounts: {
2495beta: {},
@@ -57,7 +128,7 @@ async function createLegacyStateFixture(params?: { includePreKey?: boolean }) {
57128path.join(stateDir, "agents", "worker-1", "sessions", "sessions.json"),
58129`${JSON.stringify(
59130 {
60- "group:123@g.us": { sessionId: "group-session", updatedAt: 5 },
131+ "group:mobile-room": { sessionId: "group-session", updatedAt: 5 },
61132 "group:legacy-room": { sessionId: "generic-group-session", updatedAt: 4 },
62133 },
63134 null,
@@ -75,7 +146,7 @@ async function createLegacyStateFixture(params?: { includePreKey?: boolean }) {
75146);
76147}
77148await fs.writeFile(path.join(stateDir, "credentials", "oauth.json"), '{"oauth":true}\n', "utf8");
78-await fs.writeFile(resolveChannelAllowFromPath("telegram", env), '["123","456"]\n', "utf8");
149+await fs.writeFile(resolveChannelAllowFromPath("chatapp", env), '["123","456"]\n', "utf8");
7915080151return {
81152 root,
@@ -90,7 +161,7 @@ afterEach(async () => {
90161});
9116292163describe("state migrations", () => {
93-it("detects legacy sessions, agent files, whatsapp auth, and telegram allowFrom copies", async () => {
164+it("detects legacy sessions, agent files, channel auth, and allowFrom copies", async () => {
94165const { root, stateDir, env, cfg } = await createLegacyStateFixture();
9516696167const detected = await detectLegacyStateMigrations({
@@ -102,19 +173,19 @@ describe("state migrations", () => {
102173expect(detected.targetAgentId).toBe("worker-1");
103174expect(detected.targetMainKey).toBe("desk");
104175expect(detected.sessions.hasLegacy).toBe(true);
105-expect(detected.sessions.legacyKeys).toEqual(["group:123@g.us", "group:legacy-room"]);
176+expect(detected.sessions.legacyKeys).toEqual(["group:mobile-room", "group:legacy-room"]);
106177expect(detected.agentDir.hasLegacy).toBe(true);
107178expect(detected.channelPlans.hasLegacy).toBe(true);
108179expect(detected.channelPlans.plans.map((plan) => plan.targetPath)).toEqual([
109-resolveChannelAllowFromPath("telegram", env, "alpha"),
110-path.join(stateDir, "credentials", "whatsapp", "default", "creds.json"),
180+path.join(stateDir, "credentials", "mobileauth", "default", "creds.json"),
181+resolveChannelAllowFromPath("chatapp", env, "alpha"),
111182]);
112183expect(detected.preview).toEqual([
113184`- Sessions: ${path.join(stateDir, "sessions")} → ${path.join(stateDir, "agents", "worker-1", "sessions")}`,
114185`- Sessions: canonicalize legacy keys in ${path.join(stateDir, "agents", "worker-1", "sessions", "sessions.json")}`,
115186`- Agent dir: ${path.join(stateDir, "agent")} → ${path.join(stateDir, "agents", "worker-1", "agent")}`,
116-`- Telegram pairing allowFrom: ${resolveChannelAllowFromPath("telegram", env)} → ${resolveChannelAllowFromPath("telegram", env, "alpha")}`,
117-`- WhatsApp auth creds.json: ${path.join(stateDir, "credentials", "creds.json")} → ${path.join(stateDir, "credentials", "whatsapp", "default", "creds.json")}`,
187+`- MobileAuth auth creds.json: ${path.join(stateDir, "credentials", "creds.json")} → ${path.join(stateDir, "credentials", "mobileauth", "default", "creds.json")}`,
188+`- ChatApp pairing allowFrom: ${resolveChannelAllowFromPath("chatapp", env)} → ${resolveChannelAllowFromPath("chatapp", env, "alpha")}`,
118189]);
119190});
120191@@ -138,9 +209,9 @@ describe("state migrations", () => {
138209"Canonicalized 2 legacy session key(s)",
139210"Moved trace.jsonl → agents/worker-1/sessions",
140211"Moved agent file settings.json → agents/worker-1/agent",
141-`Copied Telegram pairing allowFrom → ${resolveChannelAllowFromPath("telegram", env, "alpha")}`,
142-`Moved WhatsApp auth creds.json → ${path.join(stateDir, "credentials", "whatsapp", "default", "creds.json")}`,
143-`Moved WhatsApp auth pre-key-1.json → ${path.join(stateDir, "credentials", "whatsapp", "default", "pre-key-1.json")}`,
212+`Moved MobileAuth auth creds.json → ${path.join(stateDir, "credentials", "mobileauth", "default", "creds.json")}`,
213+`Moved MobileAuth auth pre-key-1.json → ${path.join(stateDir, "credentials", "mobileauth", "default", "pre-key-1.json")}`,
214+`Copied ChatApp pairing allowFrom → ${resolveChannelAllowFromPath("chatapp", env, "alpha")}`,
144215]);
145216146217const mergedStore = JSON.parse(
@@ -150,7 +221,9 @@ describe("state migrations", () => {
150221),
151222) as Record<string, { sessionId: string }>;
152223expect(mergedStore["agent:worker-1:desk"]?.sessionId).toBe("legacy-direct");
153-expect(mergedStore["agent:worker-1:whatsapp:group:123@g.us"]?.sessionId).toBe("group-session");
224+expect(mergedStore["agent:worker-1:mobileauth:group:mobile-room"]?.sessionId).toBe(
225+"group-session",
226+);
154227expect(mergedStore["agent:worker-1:unknown:group:legacy-room"]?.sessionId).toBe(
155228"generic-group-session",
156229);
@@ -169,25 +242,28 @@ describe("state migrations", () => {
169242fs.readFile(path.join(stateDir, "agents", "worker-1", "agent", "settings.json"), "utf8"),
170243).resolves.toContain('"ok":true');
171244await expect(
172-fs.readFile(path.join(stateDir, "credentials", "whatsapp", "default", "creds.json"), "utf8"),
245+fs.readFile(
246+path.join(stateDir, "credentials", "mobileauth", "default", "creds.json"),
247+"utf8",
248+),
173249).resolves.toContain('"auth":true');
174250await expect(
175251fs.readFile(
176-path.join(stateDir, "credentials", "whatsapp", "default", "pre-key-1.json"),
252+path.join(stateDir, "credentials", "mobileauth", "default", "pre-key-1.json"),
177253"utf8",
178254),
179255).resolves.toContain('"preKey":true');
180256await expect(
181257fs.readFile(path.join(stateDir, "credentials", "oauth.json"), "utf8"),
182258).resolves.toContain('"oauth":true');
183259await expect(
184-fs.readFile(resolveChannelAllowFromPath("telegram", env, "alpha"), "utf8"),
260+fs.readFile(resolveChannelAllowFromPath("chatapp", env, "alpha"), "utf8"),
185261).resolves.toBe('["123","456"]\n');
186262await expect(
187-fs.stat(resolveChannelAllowFromPath("telegram", env, "default")),
263+fs.stat(resolveChannelAllowFromPath("chatapp", env, "default")),
188264).rejects.toMatchObject({ code: "ENOENT" });
189265await expect(
190-fs.stat(resolveChannelAllowFromPath("telegram", env, "beta")),
266+fs.stat(resolveChannelAllowFromPath("chatapp", env, "beta")),
191267).rejects.toMatchObject({ code: "ENOENT" });
192268});
193269});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。