diff options
| author | Uttkarsh <38376852+uttkarsh-26@users.noreply.github.com> | 2026-10-11 23:40:29 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-10-11 23:40:29 +0530 |
| commit | 79526b01f4d3cf16c3cfb46313cf1179f0db40ac (patch) | |
| tree | 31b2b9f53d6eb16ea018da5533c887ba1e4c5d35 | |
| parent | 0fe64593bcc3379032840b35833ea39f23aa9322 (diff) | |
test: keep host-provided packages out of dependencies (#36)
Adds a manifest guard (scripts/check-manifest.ts + test/util/manifest-test.ts) so npm run check
fails if a host-provided package such as typebox lands in dependencies, plus a daemon-startup
troubleshooting section in the skill.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | scripts/check-manifest.ts | 40 | ||||
| -rw-r--r-- | skills/pi-browser-harness/SKILL.md | 18 | ||||
| -rw-r--r-- | test/util/manifest-test.ts | 49 |
4 files changed, 108 insertions, 0 deletions
@@ -9,6 +9,7 @@ docs/ scripts/* !scripts/check-boundaries.ts !scripts/boundary-allowlist.ts +!scripts/check-manifest.ts data/ .worktrees/ .harness/
\ No newline at end of file diff --git a/scripts/check-manifest.ts b/scripts/check-manifest.ts new file mode 100644 index 0000000..437cd90 --- /dev/null +++ b/scripts/check-manifest.ts @@ -0,0 +1,40 @@ +// Mirrors HOST_PROVIDED_EXTENSION_PACKAGES in pi-coding-agent's +// resource-loader.js and the list in its docs/packages.md. Pi provides these +// packages to extensions at runtime; declaring them in dependencies makes pi +// emit an extension warning and risks duplicate runtime modules. +export const HOST_PROVIDED_EXTENSION_PACKAGES: ReadonlySet<string> = new Set([ + "@earendil-works/pi-agent-core", + "@earendil-works/pi-ai", + "@earendil-works/pi-coding-agent", + "@earendil-works/pi-tui", + "@mariozechner/pi-agent-core", + "@mariozechner/pi-ai", + "@mariozechner/pi-coding-agent", + "@mariozechner/pi-tui", + "@sinclair/typebox", + "typebox", +]); + +export type Manifest = { + readonly dependencies?: unknown; + readonly peerDependencies?: unknown; + readonly devDependencies?: unknown; +}; + +// A host-provided package belongs in peerDependencies with a "*" range only. +export function findHostProvidedDependencies( + manifest: Manifest +): ReadonlyArray<string> { + const dependencies = manifest.dependencies; + if ( + typeof dependencies !== "object" || + dependencies === null || + Array.isArray(dependencies) + ) { + return []; + } + const names = Object.keys(dependencies).filter((name) => + HOST_PROVIDED_EXTENSION_PACKAGES.has(name) + ); + return names.sort(); +} diff --git a/skills/pi-browser-harness/SKILL.md b/skills/pi-browser-harness/SKILL.md index c1dcef7..f0a3324 100644 --- a/skills/pi-browser-harness/SKILL.md +++ b/skills/pi-browser-harness/SKILL.md @@ -100,3 +100,21 @@ When a workflow repeats 3+ times or needs Node.js APIs, write a script to disk a **Don't:** - Use scripts for one-off actions — call `browser_*` tools directly. - Call `browser_*` tools from inside a script — sequence them as separate tool calls outside. + +## Troubleshooting: "Could not start the browser daemon" + +`browser_setup` spawns the daemon as `node <tsx cli> <pkg>/src/daemon/index.ts` with `stdio: "ignore"` (tsx is resolved from the package directory upward, so in pi's hoisted install it sits beside the package rather than inside it). A crash before the socket binds is completely silent and all you see is `Could not start the browser daemon. Check /tmp/pi-browser-daemon.sock.` + +Run it in the foreground to see the real error (it prints whether it reached Chrome): + + cd <pkg> && node "$(node -p "require.resolve('tsx/cli')")" src/daemon/index.ts + +Known cause (package 0.11.1): `Error: Cannot find module 'typebox'` at `src/cdp/discovery.ts`. The package declares `typebox` in `peerDependencies` and `devDependencies`, and pi installs packages with dev and peer dependencies omitted, so the tree has no physical copy. It still resolves inside the pi extension host, which maps `typebox` to pi's bundled copy, but not in the spawned daemon process, which resolves modules from the package directory upward. The daemon dies, no socket appears, and every browser tool reports `not_connected`. + +Fix it without touching the manifest, which must keep `typebox` out of `dependencies` (pi warns on it and `npm run check` rejects it): + + cd <pkg> && npm install --no-save typebox --no-audit --no-fund + +or restore the full tree with `npm ci`, which installs the `devDependencies` copy. Then confirm with `node -e "console.log(require.resolve('typebox'))"` inside the package directory, and start the daemon with `NODE_PATH` unset to prove no environment workaround is in play. A fresh clone or update reinstalls the tree, so the missing copy can come back; the manifest itself is correct upstream. + +Do not work around it with `npm install typebox --save` (that writes `typebox` into `dependencies`, tripping pi's extension warning and the manifest check), a global `NODE_PATH`, or a hand-made symlink: the spawn inherits the extension host environment, so a shell-level export may not reach it, and the underlying problem is an incomplete install, not an undeclared dependency. diff --git a/test/util/manifest-test.ts b/test/util/manifest-test.ts new file mode 100644 index 0000000..6fcf136 --- /dev/null +++ b/test/util/manifest-test.ts @@ -0,0 +1,49 @@ +import { test, describe } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { findHostProvidedDependencies } from "../../scripts/check-manifest"; + +describe("host-provided manifest contract", () => { + test("flags host-provided packages listed in dependencies", () => { + const manifest = { dependencies: { ws: "^8.20.0", typebox: "^1.3.34" } }; + assert.deepEqual(findHostProvidedDependencies(manifest), ["typebox"]); + }); + + test("recognises every pi-provided package", () => { + const manifest = { + dependencies: { + typebox: "*", + "@sinclair/typebox": "*", + "@mariozechner/pi-tui": "*", + "@earendil-works/pi-coding-agent": "*", + }, + }; + assert.deepEqual(findHostProvidedDependencies(manifest), [ + "@earendil-works/pi-coding-agent", + "@mariozechner/pi-tui", + "@sinclair/typebox", + "typebox", + ]); + }); + + test("ignores peer and devDependencies", () => { + const manifest = { + peerDependencies: { typebox: "*" }, + devDependencies: { typebox: "*" }, + }; + assert.deepEqual(findHostProvidedDependencies(manifest), []); + }); + + test("tolerates a missing or malformed dependencies field", () => { + assert.deepEqual(findHostProvidedDependencies({}), []); + assert.deepEqual(findHostProvidedDependencies({ dependencies: null }), []); + assert.deepEqual(findHostProvidedDependencies({ dependencies: [] }), []); + }); + + test("this package declares no host-provided dependencies", () => { + const manifest = JSON.parse( + readFileSync(new URL("../../package.json", import.meta.url), "utf8") + ); + assert.deepEqual(findHostProvidedDependencies(manifest), []); + }); +}); |
