fix(runner): remove provider pack install hook
This commit is contained in:
parent
43558e7783
commit
fb40d8a307
|
|
@ -103,8 +103,7 @@
|
|||
"acpx@0.13.1": "patches/acpx@0.13.1.patch",
|
||||
"@agentclientprotocol/claude-agent-acp@0.70.0": "patches/@agentclientprotocol__claude-agent-acp@0.70.0.patch",
|
||||
"@agentclientprotocol/claude-agent-acp@0.73.0": "patches/@agentclientprotocol__claude-agent-acp@0.73.0.patch",
|
||||
"@agentclientprotocol/codex-acp@1.6.2": "patches/@agentclientprotocol__codex-acp@1.6.2.patch",
|
||||
"node@24.11.0": "patches/node@24.11.0.patch"
|
||||
"@agentclientprotocol/codex-acp@1.6.2": "patches/@agentclientprotocol__codex-acp@1.6.2.patch"
|
||||
},
|
||||
"overrides": {
|
||||
"rollup": ">=4.59.0",
|
||||
|
|
|
|||
|
|
@ -158,13 +158,13 @@
|
|||
"acpx": "0.13.1",
|
||||
"ajv": "^8.20.0",
|
||||
"json-schema-to-ts": "^3.1.1",
|
||||
"node": "24.11.0",
|
||||
"opencode-ai": "1.18.17",
|
||||
"react-markdown": "^10.1.0",
|
||||
"remark-gfm": "^4.0.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@openai/codex-linux-x64": "npm:@openai/codex@0.148.0-linux-x64"
|
||||
"@openai/codex-linux-x64": "npm:@openai/codex@0.148.0-linux-x64",
|
||||
"node-linux-x64": "24.11.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18",
|
||||
|
|
|
|||
|
|
@ -24,16 +24,14 @@ const outputRoot = resolve(
|
|||
outputArgument ?? join(packageRoot, "provider-pack"),
|
||||
);
|
||||
if (
|
||||
outputRoot === workspaceRoot
|
||||
|| outputRoot === packageRoot
|
||||
|| outputRoot === "/"
|
||||
outputRoot === workspaceRoot ||
|
||||
outputRoot === packageRoot ||
|
||||
outputRoot === "/"
|
||||
) {
|
||||
throw new Error(`Refusing unsafe provider-pack output path: ${outputRoot}`);
|
||||
}
|
||||
|
||||
const temporaryParent = mkdtempSync(
|
||||
join(tmpdir(), "paperclip-provider-pack-"),
|
||||
);
|
||||
const temporaryParent = mkdtempSync(join(tmpdir(), "paperclip-provider-pack-"));
|
||||
const temporaryRoot = join(temporaryParent, "pack");
|
||||
|
||||
function canonicalJson(value) {
|
||||
|
|
@ -56,8 +54,9 @@ function sha256File(path) {
|
|||
function sha256Tree(root) {
|
||||
const hash = createHash("sha256");
|
||||
const visit = (directory, prefix = "") => {
|
||||
const entries = readdirSync(directory, { withFileTypes: true })
|
||||
.sort((left, right) => left.name.localeCompare(right.name));
|
||||
const entries = readdirSync(directory, { withFileTypes: true }).sort(
|
||||
(left, right) => left.name.localeCompare(right.name),
|
||||
);
|
||||
for (const entry of entries) {
|
||||
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
||||
const absolutePath = join(directory, entry.name);
|
||||
|
|
@ -67,9 +66,13 @@ function sha256Tree(root) {
|
|||
} else if (entry.isFile()) {
|
||||
hash.update(`file\0${relativePath}\0${sha256File(absolutePath)}\n`);
|
||||
} else if (entry.isSymbolicLink()) {
|
||||
hash.update(`symlink\0${relativePath}\0${readlinkSync(absolutePath)}\n`);
|
||||
hash.update(
|
||||
`symlink\0${relativePath}\0${readlinkSync(absolutePath)}\n`,
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Provider pack tree contains unsupported entry ${relativePath}`);
|
||||
throw new Error(
|
||||
`Provider pack tree contains unsupported entry ${relativePath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -123,6 +126,22 @@ try {
|
|||
throw new Error(`pnpm deploy failed with exit code ${deployed.status}`);
|
||||
}
|
||||
|
||||
// The generic `node` npm package runs an install-time downloader. Depend on
|
||||
// the immutable Linux x64 artifact directly, then expose it at the stable
|
||||
// pack-owned path consumed by the verified launch contract.
|
||||
const packagedNodeRoot = join(
|
||||
temporaryRoot,
|
||||
"node_modules",
|
||||
"node-linux-x64",
|
||||
);
|
||||
const stableNodeRoot = join(temporaryRoot, "node_modules", "node");
|
||||
if (!existsSync(packagedNodeRoot) || existsSync(stableNodeRoot)) {
|
||||
throw new Error(
|
||||
"Provider pack requires the pinned node-linux-x64 artifact and an unclaimed stable Node path",
|
||||
);
|
||||
}
|
||||
renameSync(packagedNodeRoot, stableNodeRoot);
|
||||
|
||||
// pnpm's generated .bin shims embed the temporary deployment directory in
|
||||
// NODE_PATH. That makes an otherwise identical provider pack hash differ on
|
||||
// every build and leaks a nonexistent host path after relocation. Replace
|
||||
|
|
@ -193,7 +212,10 @@ try {
|
|||
const opencodeExecutable = "node_modules/opencode-ai/bin/opencode.exe";
|
||||
const nodeCommand = "node_modules/node/bin/node";
|
||||
const productionLock = "pnpm-lock.yaml";
|
||||
copyFileSync(join(workspaceRoot, "pnpm-lock.yaml"), join(temporaryRoot, productionLock));
|
||||
copyFileSync(
|
||||
join(workspaceRoot, "pnpm-lock.yaml"),
|
||||
join(temporaryRoot, productionLock),
|
||||
);
|
||||
for (const relativePath of [
|
||||
nodeCommand,
|
||||
productionLock,
|
||||
|
|
@ -207,16 +229,14 @@ try {
|
|||
}
|
||||
}
|
||||
|
||||
const opencodeProxySha = sha256File(
|
||||
join(temporaryRoot, opencodeProxyPath),
|
||||
);
|
||||
const opencodeProxySha = sha256File(join(temporaryRoot, opencodeProxyPath));
|
||||
const acpxSidecarSha = sha256File(join(temporaryRoot, acpxSidecarPath));
|
||||
const distDigest = sha256Tree(join(temporaryRoot, "dist"));
|
||||
const configuredRevision =
|
||||
process.env.PAPERCLIP_RUNNER_SOURCE_REVISION?.trim();
|
||||
const revision =
|
||||
configuredRevision
|
||||
?? execFileSync("git", ["rev-parse", "HEAD"], {
|
||||
configuredRevision ??
|
||||
execFileSync("git", ["rev-parse", "HEAD"], {
|
||||
cwd: workspaceRoot,
|
||||
encoding: "utf8",
|
||||
}).trim();
|
||||
|
|
@ -225,11 +245,9 @@ try {
|
|||
}
|
||||
const dirty = configuredRevision
|
||||
? false
|
||||
: spawnSync(
|
||||
"git",
|
||||
["diff", "--quiet", "--", "packages/paperclip-runner"],
|
||||
{ cwd: workspaceRoot },
|
||||
).status !== 0;
|
||||
: spawnSync("git", ["diff", "--quiet", "--", "packages/paperclip-runner"], {
|
||||
cwd: workspaceRoot,
|
||||
}).status !== 0;
|
||||
const payload = {
|
||||
pins: {
|
||||
nodeMinimum: "24.11.0",
|
||||
|
|
|
|||
|
|
@ -30,10 +30,6 @@ const claudePatch = await readFile(
|
|||
),
|
||||
"utf8",
|
||||
);
|
||||
const nodePatch = await readFile(
|
||||
new URL("../../../patches/node@24.11.0.patch", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const qualifiedProfiles = await readFile(
|
||||
new URL("../src/drivers/acpx/qualified-profiles.ts", import.meta.url),
|
||||
"utf8",
|
||||
|
|
@ -58,12 +54,13 @@ const nativeSessionExecutor = await readFile(
|
|||
);
|
||||
|
||||
test("the runner pins every qualified ACPX production dependency", () => {
|
||||
assert.equal(runnerPackage.dependencies.node, "24.11.0");
|
||||
assert.equal(runnerPackage.dependencies["@openai/codex"], "0.148.0");
|
||||
assert.equal(
|
||||
runnerPackage.optionalDependencies["@openai/codex-linux-x64"],
|
||||
"npm:@openai/codex@0.148.0-linux-x64",
|
||||
);
|
||||
assert.equal(runnerPackage.optionalDependencies["node-linux-x64"], "24.11.0");
|
||||
assert.equal(runnerPackage.dependencies.node, undefined);
|
||||
assert.equal(runnerPackage.dependencies.acpx, "0.13.1");
|
||||
assert.equal(
|
||||
runnerPackage.dependencies["@agentclientprotocol/codex-acp"],
|
||||
|
|
@ -76,31 +73,30 @@ test("the runner pins every qualified ACPX production dependency", () => {
|
|||
});
|
||||
|
||||
test("the patched Codex ACP command digest stays aligned across launch boundaries", () => {
|
||||
const profileMatch = /agent: "codex"[\s\S]*?commandDigest:\s*"(sha256:[a-f0-9]{64})"/.exec(
|
||||
qualifiedProfiles,
|
||||
);
|
||||
const profileMatch =
|
||||
/agent: "codex"[\s\S]*?commandDigest:\s*"(sha256:[a-f0-9]{64})"/.exec(
|
||||
qualifiedProfiles,
|
||||
);
|
||||
assert.ok(profileMatch, "qualified Codex ACPX profile digest");
|
||||
const digest = profileMatch[1];
|
||||
|
||||
assert.match(
|
||||
runnerdAcpxBackend,
|
||||
new RegExp(`"codex"[\\s\\S]*?${digest}`),
|
||||
);
|
||||
assert.match(runnerdAcpxBackend, new RegExp(`"codex"[\\s\\S]*?${digest}`));
|
||||
assert.match(
|
||||
providerPackBuilder,
|
||||
new RegExp(`acpxProfileDigests:[\\s\\S]*?codex:[\\s\\S]*?${digest}`),
|
||||
);
|
||||
assert.match(
|
||||
nativeSessionExecutor,
|
||||
new RegExp(`REMOTE_PROVIDER_PACK_PROFILE_DIGESTS[\\s\\S]*?codex:[\\s\\S]*?${digest}`),
|
||||
new RegExp(
|
||||
`REMOTE_PROVIDER_PACK_PROFILE_DIGESTS[\\s\\S]*?codex:[\\s\\S]*?${digest}`,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test("the package exposes only the reviewed runner CLI binaries", () => {
|
||||
assert.deepEqual(runnerPackage.bin, {
|
||||
"paperclip-runner-eval-session": "./dist/cli/eval-session.js",
|
||||
"paperclip-runner-codex-proxy":
|
||||
"./dist/cli/codex-app-server-unix-proxy.js",
|
||||
"paperclip-runner-codex-proxy": "./dist/cli/codex-app-server-unix-proxy.js",
|
||||
"paperclip-runner-acpx-sidecar": "./dist/cli/acpx-runtime-sidecar.js",
|
||||
"paperclip-runner-opencode-proxy":
|
||||
"./dist/cli/opencode-app-server-proxy.js",
|
||||
|
|
@ -133,13 +129,12 @@ test("old and new pnpm configuration both apply the exact runtime patches", () =
|
|||
workspace,
|
||||
/claude-agent-acp@0\.70\.0': patches\/@agentclientprotocol__claude-agent-acp@0\.70\.0\.patch/,
|
||||
);
|
||||
assert.equal(
|
||||
rootPackage.pnpm.patchedDependencies["node@24.11.0"],
|
||||
"patches/node@24.11.0.patch",
|
||||
assert.equal(rootPackage.pnpm.patchedDependencies["node@24.11.0"], undefined);
|
||||
assert.doesNotMatch(workspace, /node@24\.11\.0:/);
|
||||
assert.match(
|
||||
providerPackBuilder,
|
||||
/renameSync\(packagedNodeRoot, stableNodeRoot\)/,
|
||||
);
|
||||
assert.match(workspace, /node@24\.11\.0: patches\/node@24\.11\.0\.patch/);
|
||||
assert.match(nodePatch, /- "bin": \{/);
|
||||
assert.match(nodePatch, /- "node": "bin\/node"/);
|
||||
});
|
||||
|
||||
test("the ACPX patch preserves launch-only state and verified spawning", () => {
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
diff --git a/package.json b/package.json
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -14,9 +14,6 @@
|
||||
"scripts": {
|
||||
"preinstall": "node installArchSpecificPackage"
|
||||
},
|
||||
- "bin": {
|
||||
- "node": "bin/node"
|
||||
- },
|
||||
"dependencies": {
|
||||
"node-bin-setup": "^1.0.0"
|
||||
},
|
||||
|
|
@ -19,7 +19,6 @@ patchedDependencies:
|
|||
embedded-postgres@18.1.0-beta.16: patches/embedded-postgres@18.1.0-beta.16.patch
|
||||
acpx@0.12.0: patches/acpx@0.12.0.patch
|
||||
acpx@0.13.1: patches/acpx@0.13.1.patch
|
||||
'@agentclientprotocol/claude-agent-acp@0.70.0': patches/@agentclientprotocol__claude-agent-acp@0.70.0.patch
|
||||
'@agentclientprotocol/claude-agent-acp@0.73.0': patches/@agentclientprotocol__claude-agent-acp@0.73.0.patch
|
||||
'@agentclientprotocol/codex-acp@1.6.2': patches/@agentclientprotocol__codex-acp@1.6.2.patch
|
||||
node@24.11.0: patches/node@24.11.0.patch
|
||||
"@agentclientprotocol/claude-agent-acp@0.70.0": patches/@agentclientprotocol__claude-agent-acp@0.70.0.patch
|
||||
"@agentclientprotocol/claude-agent-acp@0.73.0": patches/@agentclientprotocol__claude-agent-acp@0.73.0.patch
|
||||
"@agentclientprotocol/codex-acp@1.6.2": patches/@agentclientprotocol__codex-acp@1.6.2.patch
|
||||
|
|
|
|||
Loading…
Reference in New Issue