diff --git a/package.json b/package.json index 7fe88af781ac..67f2bc085571 100644 --- a/package.json +++ b/package.json @@ -370,12 +370,6 @@ "!docs/releases/**", "!docs/**/*.jpg", "!docs/**/*.png", - "scripts/crabbox-routing-policy.mts", - "scripts/crabbox-wrapper-providers.mts", - "scripts/crabbox-wrapper.mjs", - "scripts/crabbox-wrapper.mts", - "scripts/testbox-lease-freshness.mts", - "scripts/lib/tsx-cli-shim.mjs", "patches/", "skills/", "custodian-skills/", diff --git a/scripts/package-manifest.mjs b/scripts/package-manifest.mjs index 4e2b4df0db01..a28815716dcc 100644 --- a/scripts/package-manifest.mjs +++ b/scripts/package-manifest.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -// Temporarily removes private workspace dependencies from the published manifest. +// Temporarily prepares source-only package metadata for publishing. import { existsSync } from "node:fs"; import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; import path from "node:path"; @@ -8,29 +8,45 @@ import { fileURLToPath } from "node:url"; const PACKAGE_JSON_PATH = "package.json"; const BACKUP_PATH = path.join(".artifacts", "package-manifest", "package.json.prepack-backup"); +// Source checkouts use TS tooling; production installs omit dev dependencies. +// Rewrite only during prepack so published commands load the bundled runtime. +const CRABBOX_SOURCE_LAUNCHER = "node scripts/crabbox-wrapper.mjs"; +const CRABBOX_PUBLISHED_LAUNCHER = "node dist/crabbox-wrapper.js"; function preparedPackageManifest(content) { const packageJson = JSON.parse(content); + let changed = false; + + for (const [name, command] of Object.entries(packageJson.scripts ?? {})) { + if ( + typeof command === "string" && + (command === CRABBOX_SOURCE_LAUNCHER || command.startsWith(`${CRABBOX_SOURCE_LAUNCHER} `)) + ) { + packageJson.scripts[name] = + `${CRABBOX_PUBLISHED_LAUNCHER}${command.slice(CRABBOX_SOURCE_LAUNCHER.length)}`; + changed = true; + } + } + const devDependencies = packageJson.devDependencies; - if (!devDependencies || typeof devDependencies !== "object" || Array.isArray(devDependencies)) { - return content; + if (devDependencies && typeof devDependencies === "object" && !Array.isArray(devDependencies)) { + const devDependencyEntries = Object.entries(devDependencies); + const publishedDevDependencyEntries = devDependencyEntries.filter( + ([, spec]) => typeof spec !== "string" || !spec.startsWith("workspace:"), + ); + if (publishedDevDependencyEntries.length !== devDependencyEntries.length) { + changed = true; + if (publishedDevDependencyEntries.length === 0) { + delete packageJson.devDependencies; + } else { + packageJson.devDependencies = Object.fromEntries(publishedDevDependencyEntries); + } + } } - const devDependencyEntries = Object.entries(devDependencies); - const publishedDevDependencyEntries = devDependencyEntries.filter( - ([, spec]) => typeof spec !== "string" || !spec.startsWith("workspace:"), - ); - if (publishedDevDependencyEntries.length === devDependencyEntries.length) { - return content; - } - if (publishedDevDependencyEntries.length === 0) { - delete packageJson.devDependencies; - } else { - packageJson.devDependencies = Object.fromEntries(publishedDevDependencyEntries); - } - return `${JSON.stringify(packageJson, null, 2)}\n`; + return changed ? `${JSON.stringify(packageJson, null, 2)}\n` : content; } -/** Restore package.json after prepack removed private workspace dependencies. */ +/** Restore package.json after prepack prepared it for publishing. */ export async function restorePackageManifest(cwd = process.cwd()) { const backupPath = path.join(cwd, BACKUP_PATH); if (!existsSync(backupPath)) { @@ -51,7 +67,7 @@ export async function restorePackageManifest(cwd = process.cwd()) { return true; } -/** Remove private workspace dependencies while recording restorable source bytes. */ +/** Prepare published package metadata while recording restorable source bytes. */ export async function preparePackageManifest(cwd = process.cwd()) { const packageJsonPath = path.join(cwd, PACKAGE_JSON_PATH); const backupPath = path.join(cwd, BACKUP_PATH); diff --git a/test/openclaw-prepack.test.ts b/test/openclaw-prepack.test.ts index 55b87669cf5c..aaf33877d8f9 100644 --- a/test/openclaw-prepack.test.ts +++ b/test/openclaw-prepack.test.ts @@ -224,6 +224,7 @@ describe("collectSourcePackWorkspaceDependencyErrors", () => { scripts: { prepack: "node scripts/package-manifest.mjs prepare", postpack: "node scripts/package-manifest.mjs restore", + "crabbox:run": "node scripts/crabbox-wrapper.mjs run", }, devDependencies: { "@openclaw/session-url-contract": "workspace:*", @@ -258,8 +259,9 @@ describe("collectSourcePackWorkspaceDependencyErrors", () => { const packedPackageJson = JSON.parse( readFileSync(path.join(extractDir, "package", "package.json"), "utf8"), - ) as { devDependencies?: Record }; + ) as { devDependencies?: Record; scripts?: Record }; expect(packedPackageJson.devDependencies).toEqual({ vitest: "4.1.10" }); + expect(packedPackageJson.scripts?.["crabbox:run"]).toBe("node dist/crabbox-wrapper.js run"); expect(readFileSync(path.join(rootDir, "package.json"), "utf8")).toBe(originalPackageJson); expect( existsSync( diff --git a/test/package-manager-config.test.ts b/test/package-manager-config.test.ts index a5f82d6b5fa9..e66d4a0d9f95 100644 --- a/test/package-manager-config.test.ts +++ b/test/package-manager-config.test.ts @@ -64,19 +64,19 @@ describe("package manager build policy", () => { expect(packageJson.files).toContain("THIRD_PARTY_NOTICES.md"); }); - it("includes the Crabbox wrapper runtime modules in the published root package", () => { + it("omits source-only Crabbox wrapper modules from the published root package", () => { const packageJson = readJson("package.json") as RootPackageJson; - expect(packageJson.files).toEqual( - expect.arrayContaining([ - "scripts/crabbox-wrapper.mjs", - "scripts/crabbox-wrapper.mts", - "scripts/crabbox-wrapper-providers.mts", - "scripts/crabbox-routing-policy.mts", - "scripts/testbox-lease-freshness.mts", - "scripts/lib/tsx-cli-shim.mjs", - ]), - ); + for (const sourcePath of [ + "scripts/crabbox-wrapper.mjs", + "scripts/crabbox-wrapper.mts", + "scripts/crabbox-wrapper-providers.mts", + "scripts/crabbox-routing-policy.mts", + "scripts/testbox-lease-freshness.mts", + "scripts/lib/tsx-cli-shim.mjs", + ]) { + expect(packageJson.files).not.toContain(sourcePath); + } }); it("pins forked transitive dependencies with parent-scoped npm-lock overrides", () => { diff --git a/tsdown.config.ts b/tsdown.config.ts index ab2a855324ee..4fbbe0c0a8af 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -346,6 +346,7 @@ function buildCoreDistEntries(): Record { return { index: "src/index.ts", entry: "src/entry.ts", + "crabbox-wrapper": "scripts/crabbox-wrapper.mts", "docker-healthcheck": "src/docker-healthcheck.ts", // Ensure this module is bundled as an entry so legacy CLI shims can resolve its exports. "cli/daemon-cli": "src/cli/daemon-cli.ts",