mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:40:44 +00:00
fix(acpx): tolerate wrapper chmod failures
This commit is contained in:
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
|
||||
- CLI/model probes: reject empty or whitespace-only `infer model run --prompt` values before calling local providers or the Gateway, so smoke checks do not spend provider calls on invalid turns. Fixes #73185. Thanks @iot2edge.
|
||||
- Gateway/media: route text-only `chat.send` image offloads through media-understanding fields so `agents.defaults.imageModel` can describe WebChat attachments instead of leaving only an opaque `media://inbound` marker. Fixes #72968. Thanks @vorajeeah.
|
||||
- Channels/Telegram: fail fast when Telegram rejects the startup `getMe` token probe with 401, so invalid or stale BotFather tokens are reported as token auth failures instead of misleading `deleteWebhook` cleanup failures. Fixes #47674. Thanks @samaedan-arch.
|
||||
- ACPX: keep generated Codex and Claude ACP wrapper startup paths working when remote or special state filesystems reject chmod, since OpenClaw invokes the wrappers through Node instead of executing them directly. Fixes #73333. Thanks @david-garcia-garcia.
|
||||
- CLI/onboarding: infer image input for common custom-provider vision model IDs, ask only for unknown models, and keep `--custom-image-input`/`--custom-text-input` overrides so vision-capable proxies do not get saved as text-only configs. Fixes #51869. Thanks @Antsoldier1974.
|
||||
- Models/OpenAI Codex: stop listing or resolving unsupported `openai-codex/gpt-5.4-mini` rows through Codex OAuth, keep stale discovery rows suppressed with a clear API-key-route hint, and leave direct `openai/gpt-5.4-mini` available. Fixes #73242. Thanks @0xCyda.
|
||||
- Plugin SDK: restore the root-alias bridge for `registerContextEngine` and expose missing legacy compat helpers `normalizeAccountId` and `resolvePreferredOpenClawTmpDir` so older external plugins such as `openclaw-weixin` can keep loading while migrating to focused SDK subpaths. Fixes #53497. Thanks @alanxchen85.
|
||||
|
||||
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { prepareAcpxCodexAuthConfig } from "./codex-auth-bridge.js";
|
||||
import { resolveAcpxPluginConfig } from "./config.js";
|
||||
|
||||
@@ -66,6 +66,7 @@ function expectClaudeWrapperCommand(command: string | undefined, wrapperPath: st
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
restoreEnv("CODEX_HOME");
|
||||
restoreEnv("OPENCLAW_AGENT_DIR");
|
||||
restoreEnv("PI_CODING_AGENT_DIR");
|
||||
@@ -114,6 +115,31 @@ describe("prepareAcpxCodexAuthConfig", () => {
|
||||
).rejects.toMatchObject({ code: "ENOENT" });
|
||||
});
|
||||
|
||||
it("keeps generated wrappers usable when chmod is rejected by the state filesystem", async () => {
|
||||
const root = await makeTempDir();
|
||||
const stateDir = path.join(root, "state");
|
||||
const generatedCodex = generatedCodexPaths(stateDir);
|
||||
const generatedClaude = generatedClaudePaths(stateDir);
|
||||
const chmodError = Object.assign(new Error("operation not permitted"), { code: "EPERM" });
|
||||
const chmodSpy = vi.spyOn(fs, "chmod").mockRejectedValue(chmodError);
|
||||
const pluginConfig = resolveAcpxPluginConfig({
|
||||
rawConfig: {},
|
||||
workspaceDir: root,
|
||||
});
|
||||
|
||||
const resolved = await prepareAcpxCodexAuthConfig({
|
||||
pluginConfig,
|
||||
stateDir,
|
||||
});
|
||||
|
||||
expect(chmodSpy).toHaveBeenCalledWith(generatedCodex.wrapperPath, 0o755);
|
||||
expect(chmodSpy).toHaveBeenCalledWith(generatedClaude.wrapperPath, 0o755);
|
||||
expectCodexWrapperCommand(resolved.agents.codex, generatedCodex.wrapperPath);
|
||||
expectClaudeWrapperCommand(resolved.agents.claude, generatedClaude.wrapperPath);
|
||||
await expect(fs.access(generatedCodex.wrapperPath)).resolves.toBeUndefined();
|
||||
await expect(fs.access(generatedClaude.wrapperPath)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("falls back to the current Codex ACP package range when the local adapter is unavailable", async () => {
|
||||
const root = await makeTempDir();
|
||||
const stateDir = path.join(root, "state");
|
||||
|
||||
@@ -240,13 +240,21 @@ async function prepareIsolatedCodexHome(baseDir: string): Promise<string> {
|
||||
return codexHome;
|
||||
}
|
||||
|
||||
async function makeGeneratedWrapperExecutableIfPossible(wrapperPath: string): Promise<void> {
|
||||
try {
|
||||
await fs.chmod(wrapperPath, 0o755);
|
||||
} catch {
|
||||
// The wrapper is invoked via `node wrapper.mjs`; executable mode is only a convenience.
|
||||
}
|
||||
}
|
||||
|
||||
async function writeCodexAcpWrapper(baseDir: string, installedBinPath?: string): Promise<string> {
|
||||
await fs.mkdir(baseDir, { recursive: true });
|
||||
const wrapperPath = path.join(baseDir, "codex-acp-wrapper.mjs");
|
||||
await fs.writeFile(wrapperPath, buildCodexAcpWrapperScript(installedBinPath), {
|
||||
encoding: "utf8",
|
||||
});
|
||||
await fs.chmod(wrapperPath, 0o755);
|
||||
await makeGeneratedWrapperExecutableIfPossible(wrapperPath);
|
||||
return wrapperPath;
|
||||
}
|
||||
|
||||
@@ -256,7 +264,7 @@ async function writeClaudeAcpWrapper(baseDir: string, installedBinPath?: string)
|
||||
await fs.writeFile(wrapperPath, buildClaudeAcpWrapperScript(installedBinPath), {
|
||||
encoding: "utf8",
|
||||
});
|
||||
await fs.chmod(wrapperPath, 0o755);
|
||||
await makeGeneratedWrapperExecutableIfPossible(wrapperPath);
|
||||
return wrapperPath;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user