mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
refactor(doctor): model legacy file copies as plans
This commit is contained in:
@@ -296,6 +296,9 @@ describe("doctor legacy state migrations", () => {
|
||||
env: { OPENCLAW_STATE_DIR: root } as NodeJS.ProcessEnv,
|
||||
});
|
||||
expect(detected.pairingAllowFrom.hasLegacyTelegram).toBe(true);
|
||||
expect(
|
||||
detected.pairingAllowFrom.copyPlans.map((plan) => path.basename(plan.targetPath)),
|
||||
).toEqual(["telegram-default-allowFrom.json"]);
|
||||
|
||||
const result = await runLegacyStateMigrations({ detected, now: () => 123 });
|
||||
expect(result.warnings).toEqual([]);
|
||||
@@ -308,6 +311,59 @@ describe("doctor legacy state migrations", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("fans out legacy Telegram pairing allowFrom store to configured named accounts", async () => {
|
||||
const root = await makeTempRoot();
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: {
|
||||
telegram: {
|
||||
accounts: {
|
||||
bot1: {},
|
||||
bot2: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const oauthDir = ensureCredentialsDir(root);
|
||||
fs.writeFileSync(
|
||||
path.join(oauthDir, "telegram-allowFrom.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
allowFrom: ["123456"],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + "\n",
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const detected = await detectLegacyStateMigrations({
|
||||
cfg,
|
||||
env: { OPENCLAW_STATE_DIR: root } as NodeJS.ProcessEnv,
|
||||
});
|
||||
expect(detected.pairingAllowFrom.hasLegacyTelegram).toBe(true);
|
||||
expect(
|
||||
detected.pairingAllowFrom.copyPlans.map((plan) => path.basename(plan.targetPath)).toSorted(),
|
||||
).toEqual(["telegram-bot1-allowFrom.json", "telegram-bot2-allowFrom.json"]);
|
||||
|
||||
const result = await runLegacyStateMigrations({ detected, now: () => 123 });
|
||||
expect(result.warnings).toEqual([]);
|
||||
|
||||
const bot1Target = path.join(oauthDir, "telegram-bot1-allowFrom.json");
|
||||
const bot2Target = path.join(oauthDir, "telegram-bot2-allowFrom.json");
|
||||
expect(fs.existsSync(bot1Target)).toBe(true);
|
||||
expect(fs.existsSync(bot2Target)).toBe(true);
|
||||
expect(fs.existsSync(path.join(oauthDir, "telegram-default-allowFrom.json"))).toBe(false);
|
||||
expect(JSON.parse(fs.readFileSync(bot1Target, "utf-8"))).toEqual({
|
||||
version: 1,
|
||||
allowFrom: ["123456"],
|
||||
});
|
||||
expect(JSON.parse(fs.readFileSync(bot2Target, "utf-8"))).toEqual({
|
||||
version: 1,
|
||||
allowFrom: ["123456"],
|
||||
});
|
||||
});
|
||||
|
||||
it("no-ops when nothing detected", async () => {
|
||||
const root = await makeTempRoot();
|
||||
const cfg: OpenClawConfig = {};
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
DEFAULT_MAIN_KEY,
|
||||
normalizeAgentId,
|
||||
} from "../routing/session-key.js";
|
||||
import { listTelegramAccountIds } from "../telegram/accounts.js";
|
||||
import { isWithinDir } from "./path-safety.js";
|
||||
import {
|
||||
ensureDir,
|
||||
@@ -57,13 +58,18 @@ export type LegacyStateDetection = {
|
||||
hasLegacy: boolean;
|
||||
};
|
||||
pairingAllowFrom: {
|
||||
legacyTelegramPath: string;
|
||||
targetTelegramPath: string;
|
||||
hasLegacyTelegram: boolean;
|
||||
copyPlans: FileCopyPlan[];
|
||||
};
|
||||
preview: string[];
|
||||
};
|
||||
|
||||
type FileCopyPlan = {
|
||||
label: string;
|
||||
sourcePath: string;
|
||||
targetPath: string;
|
||||
};
|
||||
|
||||
type MigrationLogger = {
|
||||
info: (message: string) => void;
|
||||
warn: (message: string) => void;
|
||||
@@ -98,6 +104,30 @@ function isLegacyGroupKey(key: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
function buildFileCopyPreview(plan: FileCopyPlan): string {
|
||||
return `- ${plan.label}: ${plan.sourcePath} → ${plan.targetPath}`;
|
||||
}
|
||||
|
||||
async function runFileCopyPlans(
|
||||
plans: FileCopyPlan[],
|
||||
): Promise<{ changes: string[]; warnings: string[] }> {
|
||||
const changes: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
for (const plan of plans) {
|
||||
if (fileExists(plan.targetPath)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
ensureDir(path.dirname(plan.targetPath));
|
||||
fs.copyFileSync(plan.sourcePath, plan.targetPath);
|
||||
changes.push(`Copied ${plan.label} → ${plan.targetPath}`);
|
||||
} catch (err) {
|
||||
warnings.push(`Failed migrating ${plan.label} (${plan.sourcePath}): ${String(err)}`);
|
||||
}
|
||||
}
|
||||
return { changes, warnings };
|
||||
}
|
||||
|
||||
function canonicalizeSessionKeyForAgent(params: {
|
||||
key: string;
|
||||
agentId: string;
|
||||
@@ -619,13 +649,24 @@ export async function detectLegacyStateMigrations(params: {
|
||||
fileExists(path.join(oauthDir, "creds.json")) &&
|
||||
!fileExists(path.join(targetWhatsAppAuthDir, "creds.json"));
|
||||
const legacyTelegramAllowFromPath = resolveChannelAllowFromPath("telegram", env);
|
||||
const targetTelegramAllowFromPath = resolveChannelAllowFromPath(
|
||||
"telegram",
|
||||
env,
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
);
|
||||
const hasLegacyTelegramAllowFrom =
|
||||
fileExists(legacyTelegramAllowFromPath) && !fileExists(targetTelegramAllowFromPath);
|
||||
const telegramPairingAllowFromPlans = fileExists(legacyTelegramAllowFromPath)
|
||||
? Array.from(
|
||||
new Set(
|
||||
listTelegramAccountIds(params.cfg).map((accountId) =>
|
||||
resolveChannelAllowFromPath("telegram", env, accountId),
|
||||
),
|
||||
),
|
||||
)
|
||||
.filter((targetPath) => !fileExists(targetPath))
|
||||
.map(
|
||||
(targetPath): FileCopyPlan => ({
|
||||
label: "Telegram pairing allowFrom",
|
||||
sourcePath: legacyTelegramAllowFromPath,
|
||||
targetPath,
|
||||
}),
|
||||
)
|
||||
: [];
|
||||
const hasLegacyTelegramAllowFrom = telegramPairingAllowFromPlans.length > 0;
|
||||
|
||||
const preview: string[] = [];
|
||||
if (hasLegacySessions) {
|
||||
@@ -641,9 +682,7 @@ export async function detectLegacyStateMigrations(params: {
|
||||
preview.push(`- WhatsApp auth: ${oauthDir} → ${targetWhatsAppAuthDir} (keep oauth.json)`);
|
||||
}
|
||||
if (hasLegacyTelegramAllowFrom) {
|
||||
preview.push(
|
||||
`- Telegram pairing allowFrom: ${legacyTelegramAllowFromPath} → ${targetTelegramAllowFromPath}`,
|
||||
);
|
||||
preview.push(...telegramPairingAllowFromPlans.map(buildFileCopyPreview));
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -671,9 +710,8 @@ export async function detectLegacyStateMigrations(params: {
|
||||
hasLegacy: hasLegacyWhatsAppAuth,
|
||||
},
|
||||
pairingAllowFrom: {
|
||||
legacyTelegramPath: legacyTelegramAllowFromPath,
|
||||
targetTelegramPath: targetTelegramAllowFromPath,
|
||||
hasLegacyTelegram: hasLegacyTelegramAllowFrom,
|
||||
copyPlans: telegramPairingAllowFromPlans,
|
||||
},
|
||||
preview,
|
||||
};
|
||||
@@ -899,18 +937,7 @@ async function migrateLegacyTelegramPairingAllowFrom(
|
||||
if (!detected.pairingAllowFrom.hasLegacyTelegram) {
|
||||
return { changes, warnings };
|
||||
}
|
||||
|
||||
const legacyPath = detected.pairingAllowFrom.legacyTelegramPath;
|
||||
const targetPath = detected.pairingAllowFrom.targetTelegramPath;
|
||||
try {
|
||||
ensureDir(path.dirname(targetPath));
|
||||
fs.copyFileSync(legacyPath, targetPath);
|
||||
changes.push(`Copied Telegram pairing allowFrom → ${targetPath}`);
|
||||
} catch (err) {
|
||||
warnings.push(`Failed migrating Telegram pairing allowFrom (${legacyPath}): ${String(err)}`);
|
||||
}
|
||||
|
||||
return { changes, warnings };
|
||||
return await runFileCopyPlans(detected.pairingAllowFrom.copyPlans);
|
||||
}
|
||||
|
||||
export async function runLegacyStateMigrations(params: {
|
||||
|
||||
Reference in New Issue
Block a user