























@@ -1,6 +1,7 @@
11import fs from "node:fs/promises";
22import os from "node:os";
33import path from "node:path";
4+import { saveAuthProfileStore } from "openclaw/plugin-sdk/agent-runtime";
45import { afterEach, describe, expect, it } from "vitest";
56import { prepareAcpxCodexAuthConfig } from "./codex-auth-bridge.js";
67import { resolveAcpxPluginConfig } from "./config.js";
@@ -41,18 +42,28 @@ afterEach(async () => {
4142});
42434344describe("prepareAcpxCodexAuthConfig", () => {
44-it("wraps built-in Codex ACP with an isolated CODEX_HOME copy", async () => {
45+it("wraps built-in Codex ACP with an isolated CODEX_HOME from canonical OpenClaw OAuth", async () => {
4546const root = await makeTempDir();
46-const sourceCodexHome = path.join(root, "source-codex");
4747const agentDir = path.join(root, "agent");
4848const stateDir = path.join(root, "state");
49-await fs.mkdir(sourceCodexHome, { recursive: true });
50-await fs.writeFile(
51-path.join(sourceCodexHome, "auth.json"),
52-`${JSON.stringify({ auth_mode: "apikey", OPENAI_API_KEY: "test-api-key" }, null, 2)}\n`,
49+saveAuthProfileStore(
50+{
51+version: 1,
52+profiles: {
53+"openai-codex:default": {
54+type: "oauth",
55+provider: "openai-codex",
56+access: "access-token",
57+refresh: "refresh-token",
58+expires: Date.now() + 60_000,
59+accountId: "acct-123",
60+idToken: "id-token",
61+},
62+},
63+},
64+agentDir,
65+{ filterExternalAuthProfiles: false },
5366);
54-await fs.writeFile(path.join(sourceCodexHome, "config.toml"), 'model = "gpt-5.4"\n');
55-process.env.CODEX_HOME = sourceCodexHome;
5667process.env.OPENCLAW_AGENT_DIR = agentDir;
5768delete process.env.PI_CODING_AGENT_DIR;
5869@@ -69,21 +80,65 @@ describe("prepareAcpxCodexAuthConfig", () => {
6980expect(wrapperPath).toBe(path.join(stateDir, "acpx", "codex-acp-wrapper.mjs"));
7081await expect(fs.access(wrapperPath)).resolves.toBeUndefined();
718272-const isolatedAuthPath = path.join(agentDir, "acp-auth", "codex-source", "auth.json");
83+const bridgeRoot = path.join(agentDir, "acp-auth", "codex");
84+const bridgeDirs = await fs.readdir(bridgeRoot);
85+expect(bridgeDirs).toHaveLength(1);
86+const bridgeDir = bridgeDirs[0];
87+if (!bridgeDir) {
88+throw new Error("expected one Codex auth bridge directory");
89+}
90+const isolatedAuthPath = path.join(bridgeRoot, bridgeDir, "auth.json");
7391const copiedAuth = JSON.parse(await fs.readFile(isolatedAuthPath, "utf8")) as {
7492auth_mode?: string;
75-OPENAI_API_KEY?: string;
93+tokens?: Record<string, unknown>;
7694};
77-expect(copiedAuth).toEqual({ auth_mode: "apikey", OPENAI_API_KEY: "test-api-key" });
95+expect(copiedAuth).toEqual({
96+auth_mode: "chatgpt",
97+tokens: {
98+id_token: "id-token",
99+access_token: "access-token",
100+refresh_token: "refresh-token",
101+account_id: "acct-123",
102+},
103+last_refresh: expect.any(String),
104+});
78105expect((await fs.stat(isolatedAuthPath)).mode & 0o777).toBe(0o600);
79106await expect(
80-fs.readFile(path.join(agentDir, "acp-auth", "codex-source", "config.toml"), "utf8"),
81-).resolves.toBe('model = "gpt-5.4"\n');
107+fs.access(path.join(agentDir, "acp-auth", "codex-source", "auth.json")),
108+).rejects.toMatchObject({ code: "ENOENT" });
8210983110const wrapper = await fs.readFile(wrapperPath, "utf8");
84111expect(wrapper).toContain(`CODEX_HOME: ${JSON.stringify(path.dirname(isolatedAuthPath))}`);
85112expect(wrapper).toContain("for (const key of [])");
86-expect(wrapper).not.toContain("test-api-key");
113+expect(wrapper).not.toContain("access-token");
114+});
115+116+it("does not copy source Codex auth when canonical OpenClaw OAuth is unavailable", async () => {
117+const root = await makeTempDir();
118+const sourceCodexHome = path.join(root, "source-codex");
119+const agentDir = path.join(root, "agent");
120+await fs.mkdir(sourceCodexHome, { recursive: true });
121+await fs.writeFile(
122+path.join(sourceCodexHome, "auth.json"),
123+`${JSON.stringify({ auth_mode: "apikey", OPENAI_API_KEY: "test-api-key" }, null, 2)}\n`,
124+);
125+process.env.CODEX_HOME = sourceCodexHome;
126+process.env.OPENCLAW_AGENT_DIR = agentDir;
127+delete process.env.PI_CODING_AGENT_DIR;
128+129+const pluginConfig = resolveAcpxPluginConfig({
130+rawConfig: {},
131+workspaceDir: root,
132+});
133+const resolved = await prepareAcpxCodexAuthConfig({
134+ pluginConfig,
135+stateDir: path.join(root, "state"),
136+});
137+138+expect(resolved.agents.codex).toBeUndefined();
139+await expect(
140+fs.access(path.join(agentDir, "acp-auth", "codex-source", "auth.json")),
141+).rejects.toMatchObject({ code: "ENOENT" });
87142});
8814389144it("does not override an explicitly configured Codex agent command", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。