fix(packages): load bundled Pi runtime for local installs

This commit is contained in:
2026-09-02 22:46:58 +08:00
parent 28e1690038
commit 5a2f0eb678
9 changed files with 356 additions and 19 deletions

View File

@@ -1,6 +1,7 @@
import { execFile } from 'node:child_process';
import { randomUUID } from 'node:crypto';
import { cp, mkdir, readFile, rename, rm, stat } from 'node:fs/promises';
import { createRequire } from 'node:module';
import path from 'node:path';
import { atomicWriteJson } from '../coding-projects/atomic-json';
import {
@@ -53,6 +54,17 @@ export interface DevicePackageInstallInput {
export type DevicePackageInstallRunner = (input: DevicePackageInstallInput) => Promise<void>;
interface PiPackageManagerModule {
DefaultPackageManager: new (input: {
cwd: string;
agentDir: string;
settingsManager: unknown;
}) => { getInstalledPath(source: string, scope: 'user'): string | undefined };
SettingsManager: {
inMemory(user: Record<string, unknown>, project: { projectTrusted: boolean }): unknown;
};
}
export interface DevicePackageManagerOptions {
rootDir: string;
executablePath?: string;
@@ -208,6 +220,23 @@ async function gitHead(directory: string): Promise<string> {
});
}
async function loadBundledPiPackageManager(cliPath: string): Promise<PiPackageManagerModule> {
const entryPath = path.join(path.dirname(path.resolve(cliPath)), 'index.js');
try {
const loaded = createRequire(entryPath)(entryPath) as Partial<PiPackageManagerModule>;
if (typeof loaded.DefaultPackageManager !== 'function'
|| typeof loaded.SettingsManager?.inMemory !== 'function') {
throw new Error('Pi package manager exports are unavailable');
}
return loaded as PiPackageManagerModule;
} catch {
throw new DevicePackageError(
'local_package_dependency_failed',
'Bundled Pi package manager is unavailable',
);
}
}
export class DevicePackageManager {
private readonly rootDir: string;
private readonly packagesDir: string;
@@ -270,18 +299,23 @@ export class DevicePackageManager {
}
const agentDir = path.join(planRoot, 'pi-agent');
const cwd = path.join(planRoot, 'project');
await this.runInstall({
source: source.spec,
executablePath,
cliPath,
...(this.options.npmCliPath ? { npmCliPath: this.options.npmCliPath } : {}),
agentDir,
cwd,
env: installEnvironment(agentDir),
});
const { DefaultPackageManager, SettingsManager } = await import(
'@earendil-works/pi-coding-agent'
);
try {
await this.runInstall({
source: source.spec,
executablePath,
cliPath,
...(this.options.npmCliPath ? { npmCliPath: this.options.npmCliPath } : {}),
agentDir,
cwd,
env: installEnvironment(agentDir),
});
} catch {
throw new DevicePackageError(
'local_package_dependency_failed',
'Pi could not install this package source',
);
}
const { DefaultPackageManager, SettingsManager } = await loadBundledPiPackageManager(cliPath);
const manager = new DefaultPackageManager({
cwd,
agentDir,