Commit guided setup and clack dependency updates
This commit is contained in:
53
package-lock.json
generated
53
package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^1.2.0",
|
||||
"@companion-ai/alpha-hub": "^0.1.3",
|
||||
"@mariozechner/pi-ai": "^0.66.1",
|
||||
"@mariozechner/pi-coding-agent": "^0.66.1",
|
||||
@@ -780,6 +781,28 @@
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/@clack/core": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@clack/core/-/core-1.2.0.tgz",
|
||||
"integrity": "sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-wrap-ansi": "^0.1.3",
|
||||
"sisteransi": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@clack/prompts": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.2.0.tgz",
|
||||
"integrity": "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@clack/core": "1.2.0",
|
||||
"fast-string-width": "^1.1.0",
|
||||
"fast-wrap-ansi": "^0.1.3",
|
||||
"sisteransi": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@companion-ai/alpha-hub": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@companion-ai/alpha-hub/-/alpha-hub-0.1.3.tgz",
|
||||
@@ -3206,6 +3229,21 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-string-truncated-width": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-1.2.1.tgz",
|
||||
"integrity": "sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-string-width": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-1.1.0.tgz",
|
||||
"integrity": "sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-string-truncated-width": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||
@@ -3222,6 +3260,15 @@
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fast-wrap-ansi": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.1.6.tgz",
|
||||
"integrity": "sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-string-width": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-xml-builder": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz",
|
||||
@@ -4611,6 +4658,12 @@
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/sisteransi": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
"node": ">=20.19.0 <25"
|
||||
},
|
||||
"bin": {
|
||||
"feynman": "bin/feynman.js"
|
||||
@@ -59,6 +59,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^1.2.0",
|
||||
"@companion-ai/alpha-hub": "^0.1.3",
|
||||
"@mariozechner/pi-ai": "^0.66.1",
|
||||
"@mariozechner/pi-coding-agent": "^0.66.1",
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
import { isLoggedIn as isAlphaLoggedIn, login as loginAlpha } from "@companion-ai/alpha-hub/lib";
|
||||
import { dirname } from "node:path";
|
||||
|
||||
import { getDefaultSessionDir, getFeynmanHome } from "../config/paths.js";
|
||||
import { getPiWebAccessStatus, getPiWebSearchConfigPath } from "../pi/web-access.js";
|
||||
import { getPiWebAccessStatus } from "../pi/web-access.js";
|
||||
import { normalizeFeynmanSettings } from "../pi/settings.js";
|
||||
import type { ThinkingLevel } from "../pi/settings.js";
|
||||
import { getMissingConfiguredPackages, installPackageSources } from "../pi/package-ops.js";
|
||||
import { listOptionalPackagePresets } from "../pi/package-presets.js";
|
||||
import { getCurrentModelSpec, runModelSetup } from "../model/commands.js";
|
||||
import { buildModelStatusSnapshotFromRecords, getAvailableModelRecords, getSupportedModelRecords } from "../model/catalog.js";
|
||||
import { PANDOC_FALLBACK_PATHS, resolveExecutable } from "../system/executables.js";
|
||||
import { setupPreviewDependencies } from "./preview.js";
|
||||
import { runDoctor } from "./doctor.js";
|
||||
import { printInfo, printSection, printSuccess } from "../ui/terminal.js";
|
||||
import {
|
||||
isInteractiveTerminal,
|
||||
promptConfirm,
|
||||
promptIntro,
|
||||
promptMultiSelect,
|
||||
promptOutro,
|
||||
SetupCancelledError,
|
||||
} from "./prompts.js";
|
||||
|
||||
type SetupOptions = {
|
||||
settingsPath: string;
|
||||
@@ -21,10 +30,6 @@ type SetupOptions = {
|
||||
defaultThinkingLevel?: ThinkingLevel;
|
||||
};
|
||||
|
||||
function isInteractiveTerminal(): boolean {
|
||||
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
||||
}
|
||||
|
||||
function printNonInteractiveSetupGuidance(): void {
|
||||
printInfo("Non-interactive terminal. Use explicit commands:");
|
||||
printInfo(" feynman model login <provider>");
|
||||
@@ -34,37 +39,181 @@ function printNonInteractiveSetupGuidance(): void {
|
||||
printInfo(" feynman doctor");
|
||||
}
|
||||
|
||||
function summarizePackageSources(sources: string[]): string {
|
||||
if (sources.length <= 3) {
|
||||
return sources.join(", ");
|
||||
}
|
||||
|
||||
return `${sources.slice(0, 3).join(", ")} +${sources.length - 3} more`;
|
||||
}
|
||||
|
||||
async function maybeInstallBundledPackages(options: SetupOptions): Promise<void> {
|
||||
const agentDir = dirname(options.authPath);
|
||||
const { missing, bundled } = getMissingConfiguredPackages(options.workingDir, agentDir, options.appRoot);
|
||||
const userMissing = missing.filter((entry) => entry.scope === "user").map((entry) => entry.source);
|
||||
const projectMissing = missing.filter((entry) => entry.scope === "project").map((entry) => entry.source);
|
||||
|
||||
printSection("Packages");
|
||||
if (bundled.length > 0) {
|
||||
printInfo(`Bundled research packages ready: ${summarizePackageSources(bundled.map((entry) => entry.source))}`);
|
||||
}
|
||||
|
||||
if (missing.length === 0) {
|
||||
printInfo("No additional package install required.");
|
||||
return;
|
||||
}
|
||||
|
||||
printInfo(`Missing packages: ${summarizePackageSources(missing.map((entry) => entry.source))}`);
|
||||
const shouldInstall = await promptConfirm("Install missing Feynman packages now?", true);
|
||||
if (!shouldInstall) {
|
||||
printInfo("Skipping package install. Feynman may install missing packages later if needed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (userMissing.length > 0) {
|
||||
try {
|
||||
await installPackageSources(options.workingDir, agentDir, userMissing);
|
||||
printSuccess(`Installed bundled packages: ${summarizePackageSources(userMissing)}`);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
printInfo(message.includes("No supported package manager found")
|
||||
? "No package manager available for additional installs. The standalone bundle can still run with its shipped packages."
|
||||
: `Package install skipped: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (projectMissing.length > 0) {
|
||||
try {
|
||||
await installPackageSources(options.workingDir, agentDir, projectMissing, { local: true });
|
||||
printSuccess(`Installed project packages: ${summarizePackageSources(projectMissing)}`);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
printInfo(`Project package install skipped: ${message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function maybeInstallOptionalPackages(options: SetupOptions): Promise<void> {
|
||||
const agentDir = dirname(options.authPath);
|
||||
const presets = listOptionalPackagePresets();
|
||||
if (presets.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedPresets = await promptMultiSelect(
|
||||
"Optional packages",
|
||||
presets.map((preset) => ({
|
||||
value: preset.name,
|
||||
label: preset.name,
|
||||
hint: preset.description,
|
||||
})),
|
||||
[],
|
||||
);
|
||||
|
||||
if (selectedPresets.length === 0) {
|
||||
printInfo("No optional packages selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const presetName of selectedPresets) {
|
||||
const preset = presets.find((entry) => entry.name === presetName);
|
||||
if (!preset) continue;
|
||||
try {
|
||||
await installPackageSources(options.workingDir, agentDir, preset.sources, {
|
||||
persist: true,
|
||||
});
|
||||
printSuccess(`Installed optional preset: ${preset.name}`);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
printInfo(message.includes("No supported package manager found")
|
||||
? `Skipped optional preset ${preset.name}: no package manager available.`
|
||||
: `Skipped optional preset ${preset.name}: ${message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function maybeLoginAlpha(): Promise<void> {
|
||||
if (isAlphaLoggedIn()) {
|
||||
printInfo("alphaXiv already configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldLogin = await promptConfirm("Connect alphaXiv now?", true);
|
||||
if (!shouldLogin) {
|
||||
printInfo("Skipping alphaXiv login for now.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await loginAlpha();
|
||||
printSuccess("alphaXiv login complete");
|
||||
} catch (error) {
|
||||
printInfo(`alphaXiv login skipped: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function maybeInstallPreviewDependencies(): Promise<void> {
|
||||
if (resolveExecutable("pandoc", PANDOC_FALLBACK_PATHS)) {
|
||||
printInfo("Preview support already configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldInstall = await promptConfirm("Install pandoc for preview/export support?", false);
|
||||
if (!shouldInstall) {
|
||||
printInfo("Skipping preview dependency install.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = setupPreviewDependencies();
|
||||
printSuccess(result.message);
|
||||
} catch (error) {
|
||||
printInfo(`Preview setup skipped: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function runSetup(options: SetupOptions): Promise<void> {
|
||||
if (!isInteractiveTerminal()) {
|
||||
printNonInteractiveSetupGuidance();
|
||||
return;
|
||||
}
|
||||
|
||||
await runModelSetup(options.settingsPath, options.authPath);
|
||||
try {
|
||||
await promptIntro("Feynman setup");
|
||||
await runModelSetup(options.settingsPath, options.authPath);
|
||||
await maybeInstallBundledPackages(options);
|
||||
await maybeInstallOptionalPackages(options);
|
||||
await maybeLoginAlpha();
|
||||
await maybeInstallPreviewDependencies();
|
||||
|
||||
if (!isAlphaLoggedIn()) {
|
||||
await loginAlpha();
|
||||
printSuccess("alphaXiv login complete");
|
||||
normalizeFeynmanSettings(
|
||||
options.settingsPath,
|
||||
options.bundledSettingsPath,
|
||||
options.defaultThinkingLevel ?? "medium",
|
||||
options.authPath,
|
||||
);
|
||||
|
||||
const modelStatus = buildModelStatusSnapshotFromRecords(
|
||||
getSupportedModelRecords(options.authPath),
|
||||
getAvailableModelRecords(options.authPath),
|
||||
getCurrentModelSpec(options.settingsPath),
|
||||
);
|
||||
printSection("Ready");
|
||||
printInfo(`Model: ${getCurrentModelSpec(options.settingsPath) ?? "not set"}`);
|
||||
printInfo(`alphaXiv: ${isAlphaLoggedIn() ? "configured" : "not configured"}`);
|
||||
printInfo(`Preview: ${resolveExecutable("pandoc", PANDOC_FALLBACK_PATHS) ? "configured" : "not configured"}`);
|
||||
printInfo(`Web: ${getPiWebAccessStatus().routeLabel}`);
|
||||
if (modelStatus.recommended && !modelStatus.currentValid) {
|
||||
printInfo(`Recommended model: ${modelStatus.recommended}`);
|
||||
}
|
||||
|
||||
await promptOutro("Feynman is ready");
|
||||
} catch (error) {
|
||||
if (error instanceof SetupCancelledError) {
|
||||
printInfo("Setup cancelled.");
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const result = setupPreviewDependencies();
|
||||
printSuccess(result.message);
|
||||
|
||||
normalizeFeynmanSettings(
|
||||
options.settingsPath,
|
||||
options.bundledSettingsPath,
|
||||
options.defaultThinkingLevel ?? "medium",
|
||||
options.authPath,
|
||||
);
|
||||
|
||||
const modelStatus = buildModelStatusSnapshotFromRecords(
|
||||
getSupportedModelRecords(options.authPath),
|
||||
getAvailableModelRecords(options.authPath),
|
||||
getCurrentModelSpec(options.settingsPath),
|
||||
);
|
||||
printSection("Ready");
|
||||
printInfo(`Model: ${getCurrentModelSpec(options.settingsPath) ?? "not set"}`);
|
||||
printInfo(`alphaXiv: ${isAlphaLoggedIn() ? "configured" : "not configured"}`);
|
||||
printInfo(`Preview: ${resolveExecutable("pandoc", PANDOC_FALLBACK_PATHS) ? "configured" : "not configured"}`);
|
||||
printInfo(`Web: ${getPiWebAccessStatus().routeLabel}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user