fix: harden pi package resolution
This commit is contained in:
@@ -1,26 +1,73 @@
|
||||
import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
// Resolve the bundled pi/tau binary path from the installed dependency.
|
||||
export function resolveBundledPiBinary(): string | null {
|
||||
const candidatePkgDirs: string[] = [];
|
||||
|
||||
// Preferred: ESM resolution to the package entry, then walk up to package.json.
|
||||
try {
|
||||
const require = createRequire(import.meta.url);
|
||||
const pkgPath = require.resolve(
|
||||
"@mariozechner/pi-coding-agent/package.json",
|
||||
);
|
||||
const pkgDir = path.dirname(pkgPath);
|
||||
// Prefer compiled binary if present, else fall back to dist/cli.js (has shebang).
|
||||
const binCandidates = [
|
||||
path.join(pkgDir, "dist", "pi"),
|
||||
path.join(pkgDir, "dist", "cli.js"),
|
||||
path.join(pkgDir, "bin", "tau-dev.mjs"),
|
||||
];
|
||||
for (const candidate of binCandidates) {
|
||||
if (fs.existsSync(candidate)) return candidate;
|
||||
const resolved = (import.meta as { resolve?: (s: string) => string })
|
||||
.resolve;
|
||||
const entryUrl = resolved?.("@mariozechner/pi-coding-agent");
|
||||
if (typeof entryUrl === "string" && entryUrl.startsWith("file:")) {
|
||||
const entryPath = fileURLToPath(entryUrl);
|
||||
let dir = path.dirname(entryPath);
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
const pkgJson = path.join(dir, "package.json");
|
||||
if (fs.existsSync(pkgJson)) {
|
||||
candidatePkgDirs.push(dir);
|
||||
break;
|
||||
}
|
||||
const parent = path.dirname(dir);
|
||||
if (parent === dir) break;
|
||||
dir = parent;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Dependency missing or resolution failed.
|
||||
// ignore; we'll try filesystem fallbacks below
|
||||
}
|
||||
|
||||
// Fallback: walk up from this module's directory to find node_modules.
|
||||
try {
|
||||
let dir = path.dirname(fileURLToPath(import.meta.url));
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
candidatePkgDirs.push(
|
||||
path.join(dir, "node_modules", "@mariozechner", "pi-coding-agent"),
|
||||
);
|
||||
const parent = path.dirname(dir);
|
||||
if (parent === dir) break;
|
||||
dir = parent;
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Fallback: assume CWD is project root.
|
||||
candidatePkgDirs.push(
|
||||
path.resolve(
|
||||
process.cwd(),
|
||||
"node_modules",
|
||||
"@mariozechner",
|
||||
"pi-coding-agent",
|
||||
),
|
||||
);
|
||||
|
||||
for (const pkgDir of candidatePkgDirs) {
|
||||
try {
|
||||
if (!fs.existsSync(pkgDir)) continue;
|
||||
const binCandidates = [
|
||||
path.join(pkgDir, "dist", "pi"),
|
||||
path.join(pkgDir, "dist", "cli.js"),
|
||||
path.join(pkgDir, "bin", "tau-dev.mjs"),
|
||||
];
|
||||
for (const candidate of binCandidates) {
|
||||
if (fs.existsSync(candidate)) return candidate;
|
||||
}
|
||||
} catch {
|
||||
// ignore this candidate
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,8 @@ function safeReadJson(filePath: string): Record<string, unknown> | null {
|
||||
if (!exists(filePath)) return null;
|
||||
const raw = fs.readFileSync(filePath, "utf-8");
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (typeof parsed !== "object" || parsed === null) return null;
|
||||
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
||||
return null;
|
||||
return parsed as Record<string, unknown>;
|
||||
} catch {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user