Migrate from single-bundle CLI publishing to publishing all @paperclipai/* packages individually via Changesets. This fixes the "Cannot find package @paperclipai/server" error when installing from npm. Changes: - Add @changesets/cli with fixed versioning (all packages share version) - Make 7 packages publishable (shared, adapter-utils, db, 3 adapters, server) - Add build scripts, publishConfig, and files fields to all packages - Mark @paperclipai/server as external in CLI esbuild config - Simplify CLI importServerEntry() to use string-literal dynamic import - Add generate-npm-package-json support for external workspace packages - Create scripts/release.sh for one-command releases - Remove old bump-and-publish.sh and version-bump.sh - All packages start at version 0.2.0 Usage: ./scripts/release.sh patch|minor|major [--dry-run] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
3.5 KiB
JavaScript
111 lines
3.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* generate-npm-package-json.mjs
|
|
*
|
|
* Reads the dev package.json (which has workspace:* refs) and produces
|
|
* a publishable package.json in cli/ with:
|
|
* - workspace:* dependencies removed
|
|
* - all external dependencies from workspace packages inlined
|
|
* - proper metadata for npm
|
|
*
|
|
* Reads from cli/package.dev.json if it exists (build already ran),
|
|
* otherwise from cli/package.json.
|
|
*/
|
|
|
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { resolve, dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const repoRoot = resolve(__dirname, "..");
|
|
|
|
function readPkg(relativePath) {
|
|
return JSON.parse(readFileSync(resolve(repoRoot, relativePath, "package.json"), "utf8"));
|
|
}
|
|
|
|
// Read all workspace packages that are BUNDLED into the CLI.
|
|
// Note: "server" is excluded — it's published separately as a dependency.
|
|
const workspacePaths = [
|
|
"cli",
|
|
"packages/db",
|
|
"packages/shared",
|
|
"packages/adapter-utils",
|
|
"packages/adapters/claude-local",
|
|
"packages/adapters/codex-local",
|
|
"packages/adapters/openclaw",
|
|
];
|
|
|
|
// Workspace packages that are NOT bundled and must stay as npm dependencies.
|
|
// These get published separately via Changesets and resolved at runtime.
|
|
const externalWorkspacePackages = new Set([
|
|
"@paperclipai/server",
|
|
]);
|
|
|
|
// Collect all external dependencies from all workspace packages
|
|
const allDeps = {};
|
|
const allOptionalDeps = {};
|
|
|
|
for (const pkgPath of workspacePaths) {
|
|
const pkg = readPkg(pkgPath);
|
|
const deps = pkg.dependencies || {};
|
|
const optDeps = pkg.optionalDependencies || {};
|
|
|
|
for (const [name, version] of Object.entries(deps)) {
|
|
if (name.startsWith("@paperclipai/") && !externalWorkspacePackages.has(name)) continue;
|
|
// For external workspace packages, read their version directly
|
|
if (externalWorkspacePackages.has(name)) {
|
|
const pkgDirMap = { "@paperclipai/server": "server" };
|
|
const wsPkg = readPkg(pkgDirMap[name]);
|
|
allDeps[name] = `^${wsPkg.version}`;
|
|
continue;
|
|
}
|
|
// Keep the more specific (pinned) version if conflict
|
|
if (!allDeps[name] || !version.startsWith("^")) {
|
|
allDeps[name] = version;
|
|
}
|
|
}
|
|
|
|
for (const [name, version] of Object.entries(optDeps)) {
|
|
allOptionalDeps[name] = version;
|
|
}
|
|
}
|
|
|
|
// Sort alphabetically
|
|
const sortedDeps = Object.fromEntries(Object.entries(allDeps).sort(([a], [b]) => a.localeCompare(b)));
|
|
const sortedOptDeps = Object.fromEntries(
|
|
Object.entries(allOptionalDeps).sort(([a], [b]) => a.localeCompare(b)),
|
|
);
|
|
|
|
// Read the CLI package metadata — prefer the dev backup if it exists
|
|
const devPkgPath = resolve(repoRoot, "cli/package.dev.json");
|
|
const cliPkg = existsSync(devPkgPath)
|
|
? JSON.parse(readFileSync(devPkgPath, "utf8"))
|
|
: readPkg("cli");
|
|
|
|
// Build the publishable package.json
|
|
const publishPkg = {
|
|
name: cliPkg.name,
|
|
version: cliPkg.version,
|
|
description: cliPkg.description,
|
|
type: cliPkg.type,
|
|
bin: cliPkg.bin,
|
|
keywords: cliPkg.keywords,
|
|
license: cliPkg.license,
|
|
repository: cliPkg.repository,
|
|
homepage: cliPkg.homepage,
|
|
files: cliPkg.files,
|
|
engines: { node: ">=20" },
|
|
dependencies: sortedDeps,
|
|
};
|
|
|
|
if (Object.keys(sortedOptDeps).length > 0) {
|
|
publishPkg.optionalDependencies = sortedOptDeps;
|
|
}
|
|
|
|
const output = JSON.stringify(publishPkg, null, 2) + "\n";
|
|
const outPath = resolve(repoRoot, "cli/package.json");
|
|
writeFileSync(outPath, output);
|
|
|
|
console.log(` ✓ Generated publishable package.json (${Object.keys(sortedDeps).length} deps)`);
|
|
console.log(` Version: ${cliPkg.version}`);
|