fix(cli): prepare plugin sdk before cli dev boot (#3343)

## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - The company import/export e2e exercises the local CLI startup path
that boots the dev server inside a workspace
> - That startup path loads server and plugin code which depends on
built workspace package artifacts such as `@paperclipai/shared` and
`@paperclipai/plugin-sdk`
> - In a clean worktree those `dist/*` artifacts may not exist yet even
though `paperclipai run` can still attempt to import the local server
entry
> - That mismatch caused the import/export e2e to fail before the actual
company package flow ran
> - This pull request adds a CLI preflight step that prepares the needed
workspace build dependencies before the local server import and fails
closed if that preflight is interrupted or stalls
> - The benefit is that clean worktrees can boot `paperclipai run`
reliably without silently continuing after incomplete dependency
preparation

## What Changed

- Updated `cli/src/commands/run.ts` to execute
`scripts/ensure-plugin-build-deps.mjs` before importing
`server/src/index.ts` for local dev startup.
- Ensured `paperclipai run` can materialize missing workspace artifacts
such as `packages/shared/dist` and `packages/plugins/sdk/dist`
automatically in clean worktrees.
- Made the preflight fail closed when the child process exits via signal
and bounded it with a 120-second timeout so the CLI does not hang
indefinitely.
- Kept the fix isolated to the CLI startup path; no API contract,
schema, or UI behavior changed.
- Reused the existing
`cli/src/__tests__/company-import-export-e2e.test.ts` coverage that
already exercises the failing boot path, so no additional test file was
needed.

## Verification

- `pnpm test:run cli/src/__tests__/company-import-export-e2e.test.ts`
- `pnpm --filter paperclipai typecheck`
- On the isolated branch, confirmed `packages/shared/dist/index.js` and
`packages/plugins/sdk/dist/index.js` were absent before the run, then
reran the targeted e2e and observed a passing result.

## Risks

- Low risk: the change only affects the local CLI dev startup path
before the server import.
- Residual risk: other entrypoints still rely on their own
preflight/build behavior, so this does not normalize every workspace
startup path.
- The 120-second timeout is intentionally generous, but unusually slow
machines could still hit it and surface a startup error instead of
waiting forever.

## Model Used

- OpenAI Codex, GPT-5-based coding agent in the Codex CLI environment,
with shell/tool execution enabled. The exact runtime revision and
context window are not exposed by this environment.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
Devin Foley
2026-04-10 17:01:06 -07:00
committed by GitHub
parent b00d52c5b6
commit f4a05dc35c

View File

@@ -1,5 +1,6 @@
import fs from "node:fs";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath, pathToFileURL } from "node:url";
import * as p from "@clack/prompts";
import pc from "picocolors";
@@ -146,11 +147,35 @@ function maybeEnableUiDevMiddleware(entrypoint: string): void {
}
}
function ensureDevWorkspaceBuildDeps(projectRoot: string): void {
const buildScript = path.resolve(projectRoot, "scripts/ensure-plugin-build-deps.mjs");
if (!fs.existsSync(buildScript)) return;
const result = spawnSync(process.execPath, [buildScript], {
cwd: projectRoot,
stdio: "inherit",
timeout: 120_000,
});
if (result.error) {
throw new Error(
`Failed to prepare workspace build artifacts before starting the Paperclip dev server.\n${formatError(result.error)}`,
);
}
if ((result.status ?? 1) !== 0) {
throw new Error(
"Failed to prepare workspace build artifacts before starting the Paperclip dev server.",
);
}
}
async function importServerEntry(): Promise<StartedServer> {
// Dev mode: try local workspace path (monorepo with tsx)
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
const devEntry = path.resolve(projectRoot, "server/src/index.ts");
if (fs.existsSync(devEntry)) {
ensureDevWorkspaceBuildDeps(projectRoot);
maybeEnableUiDevMiddleware(devEntry);
const mod = await import(pathToFileURL(devEntry).href);
return await startServerFromModule(mod, devEntry);