
























@@ -1,10 +1,13 @@
1+import { execFile } from "node:child_process";
12import fs from "node:fs/promises";
23import os from "node:os";
34import path from "node:path";
5+import { promisify } from "node:util";
46import { afterEach, describe, expect, it } from "vitest";
57import { prepareAcpxCodexAuthConfig } from "./codex-auth-bridge.js";
68import { resolveAcpxPluginConfig } from "./config.js";
7910+const execFileAsync = promisify(execFile);
811const tempDirs: string[] = [];
912const previousEnv = {
1013CODEX_HOME: process.env.CODEX_HOME,
@@ -59,6 +62,14 @@ describe("prepareAcpxCodexAuthConfig", () => {
5962const agentDir = path.join(root, "agent");
6063const stateDir = path.join(root, "state");
6164const generated = generatedCodexPaths(stateDir);
65+const installedBinPath = path.join(
66+root,
67+"node_modules",
68+"@zed-industries",
69+"codex-acp",
70+"bin",
71+"codex-acp.js",
72+);
6273process.env.OPENCLAW_AGENT_DIR = agentDir;
6374delete process.env.PI_CODING_AGENT_DIR;
6475@@ -69,17 +80,90 @@ describe("prepareAcpxCodexAuthConfig", () => {
6980const resolved = await prepareAcpxCodexAuthConfig({
7081 pluginConfig,
7182 stateDir,
83+resolveInstalledCodexAcpBinPath: async () => installedBinPath,
7284});
73857486expectCodexWrapperCommand(resolved.agents.codex, generated.wrapperPath);
7587await expect(fs.access(generated.wrapperPath)).resolves.toBeUndefined();
7688const wrapper = await fs.readFile(generated.wrapperPath, "utf8");
77-expect(wrapper).toContain('"--", "codex-acp"');
89+expect(wrapper).toContain(JSON.stringify(installedBinPath));
90+expect(wrapper).toContain("defaultArgs = [installedBinPath]");
7891await expect(
7992fs.access(path.join(agentDir, "acp-auth", "codex", "auth.json")),
8093).rejects.toMatchObject({ code: "ENOENT" });
8194});
829596+it("falls back to the current Codex ACP package range when the local adapter is unavailable", async () => {
97+const root = await makeTempDir();
98+const stateDir = path.join(root, "state");
99+const generated = generatedCodexPaths(stateDir);
100+const pluginConfig = resolveAcpxPluginConfig({
101+rawConfig: {},
102+workspaceDir: root,
103+});
104+105+await prepareAcpxCodexAuthConfig({
106+ pluginConfig,
107+ stateDir,
108+resolveInstalledCodexAcpBinPath: async () => undefined,
109+});
110+111+const wrapper = await fs.readFile(generated.wrapperPath, "utf8");
112+expect(wrapper).toContain('"@zed-industries/codex-acp@^0.12.0"');
113+expect(wrapper).toContain('"--", "codex-acp"');
114+expect(wrapper).not.toContain("@zed-industries/codex-acp@^0.11.1");
115+});
116+117+it("uses the bundled Codex ACP dependency by default when it is installed", async () => {
118+const root = await makeTempDir();
119+const stateDir = path.join(root, "state");
120+const generated = generatedCodexPaths(stateDir);
121+const pluginConfig = resolveAcpxPluginConfig({
122+rawConfig: {},
123+workspaceDir: root,
124+});
125+126+await prepareAcpxCodexAuthConfig({
127+ pluginConfig,
128+ stateDir,
129+});
130+131+const wrapper = await fs.readFile(generated.wrapperPath, "utf8");
132+expect(wrapper).toContain("@zed-industries/codex-acp");
133+expect(wrapper).toContain("bin/codex-acp.js");
134+expect(wrapper).toContain("defaultArgs = [installedBinPath]");
135+});
136+137+it("launches the locally installed Codex ACP bin with isolated CODEX_HOME", async () => {
138+const root = await makeTempDir();
139+const stateDir = path.join(root, "state");
140+const generated = generatedCodexPaths(stateDir);
141+const installedBinPath = path.join(root, "codex-acp-bin.js");
142+await fs.writeFile(
143+installedBinPath,
144+"console.log(JSON.stringify({ argv: process.argv.slice(2), codexHome: process.env.CODEX_HOME }));\n",
145+"utf8",
146+);
147+const pluginConfig = resolveAcpxPluginConfig({
148+rawConfig: {},
149+workspaceDir: root,
150+});
151+152+await prepareAcpxCodexAuthConfig({
153+ pluginConfig,
154+ stateDir,
155+resolveInstalledCodexAcpBinPath: async () => installedBinPath,
156+});
157+158+const { stdout } = await execFileAsync(process.execPath, [generated.wrapperPath], {
159+cwd: root,
160+});
161+const launched = JSON.parse(stdout.trim()) as { argv?: unknown; codexHome?: unknown };
162+expect(launched.argv).toEqual([]);
163+const expectedCodexHome = await fs.realpath(path.join(stateDir, "acpx", "codex-home"));
164+expect(path.resolve(String(launched.codexHome))).toBe(expectedCodexHome);
165+});
166+83167it("does not copy source Codex auth", async () => {
84168const root = await makeTempDir();
85169const sourceCodexHome = path.join(root, "source-codex");
@@ -106,6 +190,7 @@ describe("prepareAcpxCodexAuthConfig", () => {
106190const resolved = await prepareAcpxCodexAuthConfig({
107191 pluginConfig,
108192 stateDir,
193+resolveInstalledCodexAcpBinPath: async () => undefined,
109194});
110195111196expectCodexWrapperCommand(resolved.agents.codex, generated.wrapperPath);
@@ -138,7 +223,7 @@ describe("prepareAcpxCodexAuthConfig", () => {
138223rawConfig: {
139224agents: {
140225codex: {
141-command: "npx @zed-industries/codex-acp@^0.11.1 -c 'model=\"gpt-5.4\"'",
226+command: "npx @zed-industries/codex-acp@0.12.0 -c 'model=\"gpt-5.4\"'",
142227},
143228},
144229},
@@ -148,10 +233,11 @@ describe("prepareAcpxCodexAuthConfig", () => {
148233const resolved = await prepareAcpxCodexAuthConfig({
149234 pluginConfig,
150235 stateDir,
236+resolveInstalledCodexAcpBinPath: async () => path.join(root, "codex-acp.js"),
151237});
152238153239expectCodexWrapperCommand(resolved.agents.codex, generated.wrapperPath);
154-expect(resolved.agents.codex).toContain("npx @zed-industries/codex-acp@^0.11.1");
240+expect(resolved.agents.codex).toContain("npx @zed-industries/codex-acp@0.12.0");
155241expect(resolved.agents.codex).toContain("-c 'model=\"gpt-5.4\"'");
156242const isolatedConfig = await fs.readFile(generated.configPath, "utf8");
157243expect(isolatedConfig).not.toContain("notify");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。