























@@ -41,6 +41,7 @@ function makeContext(params: {
4141workspaceDir: string;
4242overwrite?: boolean;
4343verifyPluginApps?: boolean;
44+providerOptions?: MigrationProviderContext["providerOptions"];
4445reportDir?: string;
4546config?: MigrationProviderContext["config"];
4647runtime?: MigrationProviderContext["runtime"];
@@ -59,7 +60,8 @@ function makeContext(params: {
5960source: params.source,
6061stateDir: params.stateDir,
6162overwrite: params.overwrite,
62-providerOptions: params.verifyPluginApps ? { verifyPluginApps: true } : undefined,
63+providerOptions:
64+params.providerOptions ?? (params.verifyPluginApps ? { verifyPluginApps: true } : undefined),
6365reportDir: params.reportDir,
6466 logger,
6567};
@@ -210,9 +212,6 @@ describe("buildCodexMigrationProvider", () => {
210212status: "planned",
211213});
212214expect(plan.items.some((item) => item.id === "skill:system-skill")).toBe(false);
213-expect((plan.warnings ?? []).some((warning) => warning.includes("cached plugin bundles"))).toBe(
214-true,
215-);
216215});
217216218217it("plans source-installed curated plugins without installing during dry-run", async () => {
@@ -377,7 +376,6 @@ describe("buildCodexMigrationProvider", () => {
377376expect(plan.warnings).toEqual([
378377"Codex source-installed openai-curated plugins are planned for native activation; cached plugin bundles remain manual-review only.",
379378"Codex app-backed plugins were planned without source app accessibility verification. Re-run with --verify-plugin-apps to force a fresh source app/list check before planning native plugin activation.",
380-"Codex cached plugin bundles remain manual-review only.",
381379"Codex config and hook files are archive-only. They are preserved in the migration report, not loaded into OpenClaw automatically.",
382380]);
383381expect(appServerRequest.mock.calls.filter(([arg]) => arg.method === "app/list")).toHaveLength(
@@ -434,7 +432,6 @@ describe("buildCodexMigrationProvider", () => {
434432},
435433]);
436434expect(plan.warnings).toEqual([
437-"Codex cached plugin bundles remain manual-review only.",
438435"Codex app-backed plugin migration requires the Codex app-server source account to be logged in with a ChatGPT subscription account. Log in to the Codex app with subscription auth; OpenClaw auth or API-key auth does not satisfy Codex app connector access.",
439436"Codex config and hook files are archive-only. They are preserved in the migration report, not loaded into OpenClaw automatically.",
440437]);
@@ -1079,6 +1076,93 @@ describe("buildCodexMigrationProvider", () => {
10791076});
10801077});
108110781079+it("returns Codex plugin config patches without mutating config in return mode", async () => {
1080+const fixture = await createCodexFixture();
1081+const configState: MigrationProviderContext["config"] = {
1082+plugins: {
1083+entries: {
1084+codex: {
1085+enabled: true,
1086+config: {
1087+appServer: { sandbox: "workspace-write" },
1088+},
1089+},
1090+},
1091+},
1092+agents: { defaults: { workspace: fixture.workspaceDir } },
1093+} as MigrationProviderContext["config"];
1094+appServerRequest.mockImplementation(async ({ method }: { method: string }) => {
1095+if (method === "plugin/list") {
1096+return pluginList([pluginSummary("google-calendar", { installed: true, enabled: true })]);
1097+}
1098+if (method === "plugin/read") {
1099+return pluginRead("google-calendar");
1100+}
1101+if (method === "plugin/install") {
1102+return { authPolicy: "ON_USE", appsNeedingAuth: [] } satisfies v2.PluginInstallResponse;
1103+}
1104+if (method === "skills/list") {
1105+return { data: [] } satisfies v2.SkillsListResponse;
1106+}
1107+if (method === "hooks/list") {
1108+return { data: [] } satisfies v2.HooksListResponse;
1109+}
1110+if (method === "config/mcpServer/reload") {
1111+return {};
1112+}
1113+if (method === "app/list") {
1114+return appsList([]);
1115+}
1116+throw new Error(`unexpected request ${method}`);
1117+});
1118+const mutateConfigFile = vi.fn(async () => {
1119+throw new Error("mutateConfigFile should not be called in return mode");
1120+});
1121+const provider = buildCodexMigrationProvider({
1122+runtime: {
1123+config: {
1124+current: () => configState,
1125+ mutateConfigFile,
1126+},
1127+} as unknown as MigrationProviderContext["runtime"],
1128+});
1129+1130+const result = await provider.apply(
1131+makeContext({
1132+source: fixture.codexHome,
1133+stateDir: fixture.stateDir,
1134+workspaceDir: fixture.workspaceDir,
1135+config: configState,
1136+providerOptions: { configPatchMode: "return" },
1137+}),
1138+);
1139+1140+expect(mutateConfigFile).not.toHaveBeenCalled();
1141+expect(configState.plugins?.entries?.codex?.config?.codexPlugins).toBeUndefined();
1142+const configItem = findItem(result.items, "config:codex-plugins");
1143+expectRecordFields(configItem, { status: "migrated" });
1144+const configDetails = configItem.details as Record<string, unknown>;
1145+expectRecordFields(configDetails, {
1146+path: ["plugins", "entries", "codex"],
1147+});
1148+expect(configDetails.value).toEqual({
1149+enabled: true,
1150+config: {
1151+codexPlugins: {
1152+enabled: true,
1153+allow_destructive_actions: true,
1154+plugins: {
1155+"google-calendar": {
1156+enabled: true,
1157+marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
1158+pluginName: "google-calendar",
1159+},
1160+},
1161+},
1162+},
1163+});
1164+});
1165+10821166it("merges migrated plugin config with existing Codex plugins when entries do not conflict", async () => {
10831167const fixture = await createCodexFixture();
10841168const sourceKey = sourceAppCacheKey(fixture);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。