fix(pi-ext): stop importing @earendil-works/pi-ai at runtime
Some checks failed
CI/CD / typecheck (pull_request) Successful in 1m19s
CI/CD / test (pull_request) Successful in 1m23s
CI/CD / lint (pull_request) Successful in 3m10s
CI/CD / smoke (pull_request) Failing after 10m42s
CI/CD / build (pull_request) Failing after 13m32s
CI/CD / publish (pull_request) Has been skipped
Some checks failed
CI/CD / typecheck (pull_request) Successful in 1m19s
CI/CD / test (pull_request) Successful in 1m23s
CI/CD / lint (pull_request) Successful in 3m10s
CI/CD / smoke (pull_request) Failing after 10m42s
CI/CD / build (pull_request) Failing after 13m32s
CI/CD / publish (pull_request) Has been skipped
The extension failed to load outright on older pi installs: Failed to load extension ".../mcpctl-pi.ts": Cannot find module '@earendil-works/pi-ai' pi doesn't resolve an extension's bare specifiers the ordinary way — it hands jiti a hard-coded alias table built from its own dependencies, and that table differs between pi distributions. `@earendil-works/pi-coding- agent` (0.84.1) aliases both the `@earendil-works/*` and legacy `@mariozechner/*` names; `@mariozechner/pi-coding-agent` (0.73.1) aliases only the old ones. Neither resolves the other's namespace, so a single import outside the intersection takes the whole extension down: every tool, the /mcpctl command, and the status line, all gone. The only thing we used from pi-ai was `StringEnum`, a six-line wrapper over `Type.Unsafe`. Inlined as a local `stringEnum` with byte-identical output, so `typebox` — aliased by every published pi — is now the sole bare runtime import. The call site also passes `description` through, which the pi-ai version was silently dropping. Guarded in tests/config/pi-extension-embed.test.ts: any runtime import in the embedded sources that isn't `node:`, relative, or typebox now fails. Verified against both installs with the same active project: 0.73.1 reproduced the error verbatim before the change and loads cleanly after, and 0.84.1 keeps registering the gate tool exactly as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014tsRTqhEC7YYYYaP3cBqo8
This commit is contained in:
@@ -29,6 +29,34 @@ describe('embedded pi extension', () => {
|
||||
expect(PI_EXTENSION_FILES['mcpctl-pi.ts']).toContain('./mcp-http.js');
|
||||
});
|
||||
|
||||
/**
|
||||
* pi resolves an extension's bare specifiers through a hard-coded alias table
|
||||
* in its own loader, and that table is not the same across pi distributions:
|
||||
* `@earendil-works/*` exists only in the newer packages, `@mariozechner/*`
|
||||
* installs alias only the old names, and neither resolves the other. An
|
||||
* import of a package outside the intersection makes the whole extension fail
|
||||
* to load with `Cannot find module` — every tool gone, on someone else's pi.
|
||||
*
|
||||
* `typebox` is aliased by every published pi, so it is the only safe bare
|
||||
* runtime import. Type-only imports are erased before jiti resolves anything,
|
||||
* so they may name whatever they like.
|
||||
*/
|
||||
it('imports nothing at runtime that some pi build cannot resolve', () => {
|
||||
// `import x from "s"` / `import {..} from "s"` (but not `import type`),
|
||||
// plus the side-effect form `import "s"`.
|
||||
const runtimeImport =
|
||||
/^\s*import\s+(?!type\s)[^;]*?from\s*["']([^"']+)["']|^\s*import\s*["']([^"']+)["']/gm;
|
||||
const allowed = /^(node:|\.\/|\.\.\/|typebox$|typebox\/)/;
|
||||
|
||||
for (const name of PI_EXTENSION_FILENAMES) {
|
||||
const src = PI_EXTENSION_FILES[name] ?? '';
|
||||
for (const match of src.matchAll(runtimeImport)) {
|
||||
const specifier = match[1] ?? match[2] ?? '';
|
||||
expect(specifier, `${name} runtime-imports ${specifier}`).toMatch(allowed);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('carries the fixes the pi API requires', () => {
|
||||
const main = PI_EXTENSION_FILES['mcpctl-pi.ts'] ?? '';
|
||||
// ctx.ui.select takes string[] and returns the chosen string.
|
||||
|
||||
Reference in New Issue
Block a user