FN-6410: fix standalone pi runtime deps

Keep published CLI standalone installs from omitting the pi runtime packages.\n\n- Remove pi runtime packages from optional peer declarations so npm and pnpm install them as required dependencies.\n- Add a package manifest regression test for source and prepack-transformed publish manifests.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/fn-6410-standalone-pi-dependency.md    |  5 +++\n packages/cli/package.json                         |  8 ----\n packages/cli/src/__tests__/package-config.test.ts | 47 +++++++++++++++++++++++\n 3 files changed, 52 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-6410

Fusion-Task-Lineage: 579c8a91-a021-4b36-94d7-e4651ce241cb
This commit is contained in:
gsxdsm
2026-06-13 17:03:59 -07:00
parent 91e7da2906
commit 96773dda0c
3 changed files with 52 additions and 8 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Fix standalone installs of the published CLI crashing with `ERR_MODULE_NOT_FOUND` for `@earendil-works/pi-coding-agent`. `@earendil-works/pi-coding-agent` and `@earendil-works/pi-ai` are now plain required dependencies instead of also being optional peers, so clean npm and pnpm installs resolve the pi runtime packages.

View File

@@ -74,17 +74,9 @@
"ws": "^8.18.0"
},
"peerDependencies": {
"@earendil-works/pi-ai": "*",
"@earendil-works/pi-coding-agent": "*",
"typebox": "*"
},
"peerDependenciesMeta": {
"@earendil-works/pi-ai": {
"optional": true
},
"@earendil-works/pi-coding-agent": {
"optional": true
},
"typebox": {
"optional": true
}

View File

@@ -3,6 +3,7 @@ import { readFileSync } from "node:fs";
import { join } from "node:path";
import { builtinModules } from "node:module";
import { parse } from "yaml";
import { applyPrepackTransform } from "../../scripts/prepare-publish-manifest.mjs";
const workspaceRoot = join(__dirname, "..", "..", "..", "..");
@@ -32,6 +33,43 @@ function hasProjectArg(script: string | undefined, project: string): boolean {
return parts.some((part, index) => part === "--project" && parts[index + 1] === project);
}
function assertRuntimeDepsAreNotOptionalPeers(pkg: any, label: string): void {
const dependencies = pkg.dependencies ?? {};
const peerDependencies = pkg.peerDependencies ?? {};
const peerDependenciesMeta = pkg.peerDependenciesMeta ?? {};
for (const dependencyName of Object.keys(dependencies)) {
expect(
peerDependenciesMeta[dependencyName]?.optional,
`${label}: runtime dependency "${dependencyName}" must not also be an optional peer; npm/pnpm may omit it from clean standalone installs.`,
).not.toBe(true);
}
for (const dependencyName of ["@earendil-works/pi-coding-agent", "@earendil-works/pi-ai"]) {
expect(dependencies, `${label}: ${dependencyName} must remain a required runtime dependency`).toHaveProperty(
dependencyName,
"^0.79.1",
);
expect(peerDependencies, `${label}: ${dependencyName} must not be a peer dependency`).not.toHaveProperty(
dependencyName,
);
expect(peerDependenciesMeta, `${label}: ${dependencyName} must not have peer metadata`).not.toHaveProperty(
dependencyName,
);
}
expect(dependencies, `${label}: typebox must not be promoted into runtime dependencies`).not.toHaveProperty(
"typebox",
);
expect(peerDependencies, `${label}: typebox remains the optional peer control`).toHaveProperty(
"typebox",
"*",
);
expect(peerDependenciesMeta.typebox, `${label}: typebox remains optional peer metadata`).toEqual({
optional: true,
});
}
describe("CLI package.json publishing config", () => {
const pkg = loadPackageJson("cli");
const prepackScript = loadCliPrepackScript();
@@ -93,6 +131,15 @@ describe("CLI package.json publishing config", () => {
expect(deps).toContain("ioredis");
});
/**
* FNXC:Packaging 2026-06-13-16:36:
* Standalone npm/pnpm installs may omit a package when the published manifest declares it as both a runtime dependency and an optional peer. Keep the pi runtime packages as plain dependencies so dist/bin.js and dist/extension.js can resolve their static imports outside the monorepo, while leaving typebox as the optional-peer control because Fusion does not import it at runtime.
*/
it("does not declare runtime dependencies as optional peers in source or published manifests", () => {
assertRuntimeDepsAreNotOptionalPeers(pkg, "source manifest");
assertRuntimeDepsAreNotOptionalPeers(applyPrepackTransform(pkg), "published manifest");
});
it("defines test:docs-index as a single-file docs README index lane", () => {
const script = pkg.scripts?.["test:docs-index"];
const parts = script?.trim().split(/\s+/) ?? [];