fix(runner): resolve exported ACP package manifests
This commit is contained in:
parent
d8ab1f0f3b
commit
40830f61e8
|
|
@ -82,8 +82,13 @@ describe("ACPX installation integrity", () => {
|
|||
await mkdir(nestedDependencyDirectory, { recursive: true });
|
||||
await writeFile(
|
||||
nestedDependencyPackageJson,
|
||||
JSON.stringify({ name: "qualified-dependency", version: "1.0.0" }),
|
||||
JSON.stringify({
|
||||
name: "qualified-dependency",
|
||||
version: "1.0.0",
|
||||
exports: "./index.js",
|
||||
}),
|
||||
);
|
||||
await writeFile(join(nestedDependencyDirectory, "index.js"), "export {};");
|
||||
expect(
|
||||
createAcpxPackageJsonResolver(root)(
|
||||
"qualified-dependency",
|
||||
|
|
@ -549,6 +554,18 @@ describe("ACPX installation integrity", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it.runIf(process.platform === "linux" && process.arch === "x64")(
|
||||
"resolves and pins the installed Claude ACP dependency graph",
|
||||
async () => {
|
||||
const profile = resolveQualifiedAcpxProfile("claude", "claude-sonnet-5");
|
||||
const installation = await verifyQualifiedAcpxInstallation(profile);
|
||||
expect(installation.agentServerPackageJsonPath).toContain(
|
||||
"/@agentclientprotocol/claude-agent-acp/package.json",
|
||||
);
|
||||
await (await installation.openCommand()).close();
|
||||
},
|
||||
);
|
||||
|
||||
it.runIf(process.platform === "linux" && process.arch === "x64")(
|
||||
"resolves and pins the qualified Codex native runtime through its transitive packages",
|
||||
async () => {
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ export function createAcpxPackageJsonResolver(
|
|||
);
|
||||
}
|
||||
const packageJsonPath = realpathSync(
|
||||
createRequire(canonicalIssuer).resolve(`${packageName}/package.json`),
|
||||
resolvePackageJsonFromIssuer(packageName, canonicalIssuer),
|
||||
);
|
||||
if (!pathIsInside(canonicalNodeModules, packageJsonPath)) {
|
||||
throw new Error(
|
||||
|
|
@ -306,6 +306,44 @@ export function createAcpxPackageJsonResolver(
|
|||
};
|
||||
}
|
||||
|
||||
function resolvePackageJsonFromIssuer(
|
||||
packageName: string,
|
||||
issuerPackageJsonPath: string,
|
||||
): string {
|
||||
const issuerRequire = createRequire(issuerPackageJsonPath);
|
||||
try {
|
||||
return issuerRequire.resolve(`${packageName}/package.json`);
|
||||
} catch (error) {
|
||||
if (
|
||||
(error as NodeJS.ErrnoException).code !== "ERR_PACKAGE_PATH_NOT_EXPORTED"
|
||||
)
|
||||
throw error;
|
||||
}
|
||||
|
||||
const packageSegments = packageName.split("/");
|
||||
if (
|
||||
packageSegments.length < 1 ||
|
||||
packageSegments.length > 2 ||
|
||||
packageSegments.some((segment) => segment.length === 0)
|
||||
) {
|
||||
throw new Error(`ACPX provider package name is invalid: ${packageName}`);
|
||||
}
|
||||
let directory = dirname(realpathSync(issuerRequire.resolve(packageName)));
|
||||
for (let count = 0; count < MAX_DEPENDENCY_ANCESTORS; count += 1) {
|
||||
const matchesPackage =
|
||||
basename(directory) === packageSegments.at(-1) &&
|
||||
(packageSegments.length === 1 ||
|
||||
basename(dirname(directory)) === packageSegments[0]);
|
||||
if (matchesPackage) return resolve(directory, "package.json");
|
||||
const parent = dirname(directory);
|
||||
if (parent === directory) break;
|
||||
directory = parent;
|
||||
}
|
||||
throw new Error(
|
||||
`ACPX provider package manifest could not be located for ${packageName}`,
|
||||
);
|
||||
}
|
||||
|
||||
function pathIsInside(root: string, candidate: string): boolean {
|
||||
const candidateRelativePath = relative(root, candidate);
|
||||
return (
|
||||
|
|
|
|||
Loading…
Reference in New Issue