Fix OAuth logo: override .logo size constraint, use @import for VT323 font

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Advait Paliwal
2026-03-23 23:43:32 -07:00
parent b3263fb94c
commit 82790506d3
6 changed files with 62 additions and 143 deletions

View File

@@ -12,4 +12,4 @@ export const FEYNMAN_ASCII_LOGO = [
export const FEYNMAN_ASCII_LOGO_TEXT = FEYNMAN_ASCII_LOGO.join("\n"); export const FEYNMAN_ASCII_LOGO_TEXT = FEYNMAN_ASCII_LOGO.join("\n");
export const FEYNMAN_LOGO_HTML = `<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet"><span style="font-family:'VT323',monospace;font-size:64px;letter-spacing:-0.05em;color:#10b981">feynman</span>`; export const FEYNMAN_LOGO_HTML = `<style>@import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');.logo{width:auto!important;height:auto!important}</style><span style="font-family:'VT323',monospace;font-size:64px;letter-spacing:-0.05em;color:#10b981">feynman</span>`;

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@companion-ai/feynman", "name": "@companion-ai/feynman",
"version": "0.2.9", "version": "0.2.10",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@companion-ai/feynman", "name": "@companion-ai/feynman",
"version": "0.2.9", "version": "0.2.10",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@companion-ai/alpha-hub": "^0.1.2", "@companion-ai/alpha-hub": "^0.1.2",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@companion-ai/feynman", "name": "@companion-ai/feynman",
"version": "0.2.9", "version": "0.2.10",
"description": "Research-first CLI agent built on Pi and alphaXiv", "description": "Research-first CLI agent built on Pi and alphaXiv",
"type": "module", "type": "module",
"engines": { "engines": {

View File

@@ -19,6 +19,7 @@ import { launchPiChat } from "./pi/launch.js";
import { CORE_PACKAGE_SOURCES, getOptionalPackagePresetSources, listOptionalPackagePresets } 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 { normalizeFeynmanSettings, normalizeThinkingLevel, parseModelSpec } from "./pi/settings.js";
import { import {
getCurrentModelSpec,
loginModelProvider, loginModelProvider,
logoutModelProvider, logoutModelProvider,
printModelList, printModelList,
@@ -424,6 +425,22 @@ export async function main(): Promise<void> {
} }
} }
if (!explicitModelSpec && !getCurrentModelSpec(feynmanSettingsPath) && process.stdin.isTTY && process.stdout.isTTY) {
await runSetup({
settingsPath: feynmanSettingsPath,
bundledSettingsPath,
authPath: feynmanAuthPath,
workingDir,
sessionDir,
appRoot,
defaultThinkingLevel: thinkingLevel,
});
if (!getCurrentModelSpec(feynmanSettingsPath)) {
return;
}
normalizeFeynmanSettings(feynmanSettingsPath, bundledSettingsPath, thinkingLevel, feynmanAuthPath);
}
await launchPiChat({ await launchPiChat({
appRoot, appRoot,
workingDir, workingDir,

View File

@@ -191,33 +191,23 @@ export function setDefaultModelSpec(settingsPath: string, authPath: string, spec
} }
export async function runModelSetup(settingsPath: string, authPath: string): Promise<void> { export async function runModelSetup(settingsPath: string, authPath: string): Promise<void> {
const status = collectModelStatus(settingsPath, authPath); let status = collectModelStatus(settingsPath, authPath);
if (status.availableModels.length === 0) { if (status.availableModels.length === 0) {
printWarning("No Pi models are currently authenticated for Feynman."); await loginModelProvider(authPath, undefined, settingsPath);
for (const line of status.guidance) { status = collectModelStatus(settingsPath, authPath);
printInfo(line); if (status.availableModels.length === 0) {
return;
} }
printInfo("Tip: run `feynman model login <provider>` if your provider supports Pi OAuth login."); }
if (status.currentValid) {
printInfo(`Model: ${status.current}`);
return; return;
} }
const choices = status.availableModels.map((spec) => { const recommended = status.recommended ?? status.availableModels[0];
const markers = [ if (recommended) {
spec === status.recommended ? "recommended" : undefined, setDefaultModelSpec(settingsPath, authPath, recommended);
spec === status.current ? "current" : undefined,
].filter(Boolean);
return `${spec}${markers.length > 0 ? ` (${markers.join(", ")})` : ""}`;
});
choices.push(`Keep current (${status.current ?? "unset"})`);
const defaultIndex = status.current ? Math.max(0, status.availableModels.indexOf(status.current)) : 0;
const selection = await promptChoice("Select your default research model:", choices, defaultIndex >= 0 ? defaultIndex : 0);
if (selection >= status.availableModels.length) {
printInfo("Skipped (keeping current model)");
return;
} }
setDefaultModelSpec(settingsPath, authPath, status.availableModels[selection]!);
} }

View File

@@ -7,10 +7,9 @@ import type { ThinkingLevel } from "../pi/settings.js";
import { getCurrentModelSpec, runModelSetup } from "../model/commands.js"; import { getCurrentModelSpec, runModelSetup } from "../model/commands.js";
import { buildModelStatusSnapshotFromRecords, getAvailableModelRecords, getSupportedModelRecords } from "../model/catalog.js"; import { buildModelStatusSnapshotFromRecords, getAvailableModelRecords, getSupportedModelRecords } from "../model/catalog.js";
import { PANDOC_FALLBACK_PATHS, resolveExecutable } from "../system/executables.js"; import { PANDOC_FALLBACK_PATHS, resolveExecutable } from "../system/executables.js";
import { promptText } from "./prompts.js";
import { setupPreviewDependencies } from "./preview.js"; import { setupPreviewDependencies } from "./preview.js";
import { runDoctor } from "./doctor.js"; import { runDoctor } from "./doctor.js";
import { printInfo, printPanel, printSection, printSuccess } from "../ui/terminal.js"; import { printInfo, printSection, printSuccess } from "../ui/terminal.js";
type SetupOptions = { type SetupOptions = {
settingsPath: string; settingsPath: string;
@@ -22,129 +21,16 @@ type SetupOptions = {
defaultThinkingLevel?: ThinkingLevel; defaultThinkingLevel?: ThinkingLevel;
}; };
async function explainWebAccess(): Promise<void> {
const status = getPiWebAccessStatus();
printSection("Web Access");
printInfo("Feynman uses the bundled `pi-web-access` package directly.");
printInfo("Default v1 path: sign into gemini.google.com in a supported Chromium browser.");
printInfo(`Current search route: ${status.routeLabel}`);
printInfo(`Pi config path: ${status.configPath}`);
printInfo("Advanced users can edit the Pi config directly if they want API keys or a different route.");
}
function isPreviewConfigured() {
return Boolean(resolveExecutable("pandoc", PANDOC_FALLBACK_PATHS));
}
function isInteractiveTerminal(): boolean { function isInteractiveTerminal(): boolean {
return Boolean(process.stdin.isTTY && process.stdout.isTTY); return Boolean(process.stdin.isTTY && process.stdout.isTTY);
} }
function printNonInteractiveSetupGuidance(): void { function printNonInteractiveSetupGuidance(): void {
printPanel("Feynman Setup", [ printInfo("Non-interactive terminal. Use explicit commands:");
"Non-interactive terminal detected.",
]);
printInfo("Use the explicit commands instead of the interactive setup wizard:");
printInfo(" feynman status");
printInfo(" feynman model login <provider>"); printInfo(" feynman model login <provider>");
printInfo(" feynman model set <provider/model>"); printInfo(" feynman model set <provider/model>");
printInfo(" feynman search status");
printInfo(` edit ${getPiWebSearchConfigPath()} # optional advanced web config`);
printInfo(" feynman alpha login"); printInfo(" feynman alpha login");
printInfo(" feynman doctor"); printInfo(" feynman doctor");
printInfo(" feynman # Pi's /login flow still works inside chat if you prefer it");
}
async function runPreviewSetup(): Promise<void> {
const result = setupPreviewDependencies();
printSuccess(result.message);
}
function printConfigurationLocation(appRoot: string): void {
printSection("Configuration Location");
printInfo(`Data folder: ${getFeynmanHome()}`);
printInfo(`Sessions: ${getDefaultSessionDir()}`);
printInfo(`Install dir: ${appRoot}`);
}
function printSetupSummary(settingsPath: string, authPath: string): void {
const modelStatus = buildModelStatusSnapshotFromRecords(
getSupportedModelRecords(authPath),
getAvailableModelRecords(authPath),
getCurrentModelSpec(settingsPath),
);
printSection("Setup Summary");
printInfo(`Model: ${getCurrentModelSpec(settingsPath) ?? "not set"}`);
printInfo(`Model valid: ${modelStatus.currentValid ? "yes" : "no"}`);
printInfo(`Recommended model: ${modelStatus.recommended ?? "not available"}`);
printInfo(`alphaXiv: ${isAlphaLoggedIn() ? "configured" : "missing"}`);
printInfo(`Web access: pi-web-access (${getPiWebAccessStatus().routeLabel})`);
printInfo(`Preview: ${isPreviewConfigured() ? "configured" : "not configured"}`);
for (const line of modelStatus.guidance) {
printInfo(line);
}
}
async function runFullSetup(options: SetupOptions): Promise<void> {
printConfigurationLocation(options.appRoot);
await runModelSetup(options.settingsPath, options.authPath);
if (!isAlphaLoggedIn()) {
await loginAlpha();
printSuccess("alphaXiv login complete");
} else {
printInfo("alphaXiv login already configured");
}
await explainWebAccess();
await runPreviewSetup();
normalizeFeynmanSettings(
options.settingsPath,
options.bundledSettingsPath,
options.defaultThinkingLevel ?? "medium",
options.authPath,
);
runDoctor({
settingsPath: options.settingsPath,
authPath: options.authPath,
sessionDir: options.sessionDir,
workingDir: options.workingDir,
appRoot: options.appRoot,
});
printSetupSummary(options.settingsPath, options.authPath);
}
function hasExistingSetup(settingsPath: string, authPath: string): boolean {
const modelStatus = buildModelStatusSnapshotFromRecords(
getSupportedModelRecords(authPath),
getAvailableModelRecords(authPath),
getCurrentModelSpec(settingsPath),
);
return Boolean(
modelStatus.current ||
modelStatus.availableModels.length > 0 ||
isAlphaLoggedIn() ||
isPreviewConfigured(),
);
}
async function runDefaultInteractiveSetup(options: SetupOptions): Promise<void> {
const existing = hasExistingSetup(options.settingsPath, options.authPath);
printPanel("Feynman Setup Wizard", [
"Guided setup for the research-first Pi agent.",
"Press Ctrl+C at any time to exit.",
]);
if (existing) {
printSection("Full Setup");
printInfo("Existing configuration detected. Rerunning the full guided setup.");
} else {
printInfo("We'll walk you through:");
printInfo(" 1. Model Selection");
printInfo(" 2. alphaXiv Login");
printInfo(" 3. Preview Dependencies");
}
printInfo("Press Enter to begin, or Ctrl+C to exit.");
await promptText("Press Enter to start");
await runFullSetup(options);
} }
export async function runSetup(options: SetupOptions): Promise<void> { export async function runSetup(options: SetupOptions): Promise<void> {
@@ -153,5 +39,31 @@ export async function runSetup(options: SetupOptions): Promise<void> {
return; return;
} }
await runDefaultInteractiveSetup(options); await runModelSetup(options.settingsPath, options.authPath);
if (!isAlphaLoggedIn()) {
await loginAlpha();
printSuccess("alphaXiv login complete");
}
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}`);
} }