Fix Feynman onboarding and local install reliability
This commit is contained in:
124
src/cli.ts
124
src/cli.ts
@@ -1,6 +1,6 @@
|
||||
import "dotenv/config";
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { parseArgs } from "node:util";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -11,11 +11,13 @@ import {
|
||||
login as loginAlpha,
|
||||
logout as logoutAlpha,
|
||||
} from "@companion-ai/alpha-hub/lib";
|
||||
import { DefaultPackageManager, SettingsManager } from "@mariozechner/pi-coding-agent";
|
||||
import { SettingsManager } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
import { syncBundledAssets } from "./bootstrap/sync.js";
|
||||
import { ensureFeynmanHome, getDefaultSessionDir, getFeynmanAgentDir, getFeynmanHome } from "./config/paths.js";
|
||||
import { launchPiChat } from "./pi/launch.js";
|
||||
import { installPackageSources, updateConfiguredPackages } from "./pi/package-ops.js";
|
||||
import { MAX_NATIVE_PACKAGE_NODE_MAJOR } from "./pi/package-presets.js";
|
||||
import { CORE_PACKAGE_SOURCES, getOptionalPackagePresetSources, listOptionalPackagePresets } from "./pi/package-presets.js";
|
||||
import { normalizeFeynmanSettings, normalizeThinkingLevel, parseModelSpec } from "./pi/settings.js";
|
||||
import { applyFeynmanPackageManagerEnv } from "./pi/runtime.js";
|
||||
@@ -28,6 +30,7 @@ import {
|
||||
printModelList,
|
||||
setDefaultModelSpec,
|
||||
} from "./model/commands.js";
|
||||
import { buildModelStatusSnapshotFromRecords, getAvailableModelRecords, getSupportedModelRecords } from "./model/catalog.js";
|
||||
import { clearSearchConfig, printSearchStatus, setSearchProvider } from "./search/commands.js";
|
||||
import type { PiWebSearchProvider } from "./pi/web-access.js";
|
||||
import { runDoctor, runStatus } from "./setup/doctor.js";
|
||||
@@ -180,27 +183,30 @@ async function handleModelCommand(subcommand: string | undefined, args: string[]
|
||||
}
|
||||
|
||||
async function handleUpdateCommand(workingDir: string, feynmanAgentDir: string, source?: string): Promise<void> {
|
||||
applyFeynmanPackageManagerEnv(feynmanAgentDir);
|
||||
const settingsManager = SettingsManager.create(workingDir, feynmanAgentDir);
|
||||
const packageManager = new DefaultPackageManager({
|
||||
cwd: workingDir,
|
||||
agentDir: feynmanAgentDir,
|
||||
settingsManager,
|
||||
});
|
||||
|
||||
packageManager.setProgressCallback((event) => {
|
||||
if (event.type === "start") {
|
||||
console.log(`Updating ${event.source}...`);
|
||||
} else if (event.type === "complete") {
|
||||
console.log(`Updated ${event.source}`);
|
||||
} else if (event.type === "error") {
|
||||
console.error(`Failed to update ${event.source}: ${event.message ?? "unknown error"}`);
|
||||
try {
|
||||
const result = await updateConfiguredPackages(workingDir, feynmanAgentDir, source);
|
||||
if (result.updated.length === 0) {
|
||||
console.log("All packages up to date.");
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
await packageManager.update(source);
|
||||
await settingsManager.flush();
|
||||
console.log("All packages up to date.");
|
||||
for (const updatedSource of result.updated) {
|
||||
console.log(`Updated ${updatedSource}`);
|
||||
}
|
||||
for (const skippedSource of result.skipped) {
|
||||
console.log(`Skipped ${skippedSource} on Node ${process.versions.node} (native packages are only supported through Node ${MAX_NATIVE_PACKAGE_NODE_MAJOR}.x).`);
|
||||
}
|
||||
console.log("All packages up to date.");
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (message.includes("No supported package manager found")) {
|
||||
console.log("No package manager is available for live package updates.");
|
||||
console.log("If you installed the standalone app, rerun the installer to get newer bundled packages.");
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePackagesCommand(subcommand: string | undefined, args: string[], workingDir: string, feynmanAgentDir: string): Promise<void> {
|
||||
@@ -244,30 +250,44 @@ async function handlePackagesCommand(subcommand: string | undefined, args: strin
|
||||
throw new Error(`Unknown package preset: ${target}`);
|
||||
}
|
||||
|
||||
const packageManager = new DefaultPackageManager({
|
||||
cwd: workingDir,
|
||||
agentDir: feynmanAgentDir,
|
||||
settingsManager,
|
||||
});
|
||||
packageManager.setProgressCallback((event) => {
|
||||
if (event.type === "start") {
|
||||
console.log(`Installing ${event.source}...`);
|
||||
} else if (event.type === "complete") {
|
||||
console.log(`Installed ${event.source}`);
|
||||
} else if (event.type === "error") {
|
||||
console.error(`Failed to install ${event.source}: ${event.message ?? "unknown error"}`);
|
||||
}
|
||||
});
|
||||
const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const isStandaloneBundle = !existsSync(resolve(appRoot, ".feynman", "runtime-workspace.tgz")) && existsSync(resolve(appRoot, ".feynman", "npm"));
|
||||
if (target === "generative-ui" && process.platform === "darwin" && isStandaloneBundle) {
|
||||
console.log("The generative-ui preset is currently unavailable in the standalone macOS bundle.");
|
||||
console.log("Its native glimpseui dependency fails to compile reliably in that environment.");
|
||||
console.log("If you need generative-ui, install Feynman through npm instead of the standalone bundle.");
|
||||
return;
|
||||
}
|
||||
|
||||
const pendingSources = sources.filter((source) => !configuredSources.has(source));
|
||||
for (const source of sources) {
|
||||
if (configuredSources.has(source)) {
|
||||
console.log(`${source} already installed`);
|
||||
continue;
|
||||
}
|
||||
await packageManager.install(source);
|
||||
}
|
||||
await settingsManager.flush();
|
||||
console.log("Optional packages installed.");
|
||||
|
||||
if (pendingSources.length === 0) {
|
||||
console.log("Optional packages installed.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await installPackageSources(workingDir, feynmanAgentDir, pendingSources, { persist: true });
|
||||
for (const skippedSource of result.skipped) {
|
||||
console.log(`Skipped ${skippedSource} on Node ${process.versions.node} (native packages are only supported through Node ${MAX_NATIVE_PACKAGE_NODE_MAJOR}.x).`);
|
||||
}
|
||||
await settingsManager.flush();
|
||||
console.log("Optional packages installed.");
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (message.includes("No supported package manager found")) {
|
||||
console.log("No package manager is available for optional package installs.");
|
||||
console.log("Install npm, pnpm, or bun, or rerun the standalone installer for bundled package updates.");
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearchCommand(subcommand: string | undefined, args: string[]): void {
|
||||
@@ -326,6 +346,24 @@ export function resolveInitialPrompt(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function shouldRunInteractiveSetup(
|
||||
explicitModelSpec: string | undefined,
|
||||
currentModelSpec: string | undefined,
|
||||
isInteractiveTerminal: boolean,
|
||||
authPath: string,
|
||||
): boolean {
|
||||
if (explicitModelSpec || !isInteractiveTerminal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const status = buildModelStatusSnapshotFromRecords(
|
||||
getSupportedModelRecords(authPath),
|
||||
getAvailableModelRecords(authPath),
|
||||
currentModelSpec,
|
||||
);
|
||||
return !status.currentValid;
|
||||
}
|
||||
|
||||
export async function main(): Promise<void> {
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const appRoot = resolve(here, "..");
|
||||
@@ -498,7 +536,13 @@ export async function main(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
if (!explicitModelSpec && !getCurrentModelSpec(feynmanSettingsPath) && process.stdin.isTTY && process.stdout.isTTY) {
|
||||
const currentModelSpec = getCurrentModelSpec(feynmanSettingsPath);
|
||||
if (shouldRunInteractiveSetup(
|
||||
explicitModelSpec,
|
||||
currentModelSpec,
|
||||
Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
||||
feynmanAuthPath,
|
||||
)) {
|
||||
await runSetup({
|
||||
settingsPath: feynmanSettingsPath,
|
||||
bundledSettingsPath,
|
||||
|
||||
Reference in New Issue
Block a user