@companion-ai/feynman 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +19 -4
- package/dist/model/commands.js +26 -21
- package/dist/setup/setup.js +15 -96
- package/dist/ui/terminal.js +11 -0
- package/extensions/research-tools/header.ts +3 -1
- package/logo.d.mts +1 -1
- package/logo.mjs +10 -7
- package/package.json +1 -1
- package/scripts/patch-embedded-pi.mjs +10 -10
package/dist/cli.js
CHANGED
|
@@ -10,12 +10,12 @@ import { ensureFeynmanHome, getDefaultSessionDir, getFeynmanAgentDir, getFeynman
|
|
|
10
10
|
import { launchPiChat } from "./pi/launch.js";
|
|
11
11
|
import { CORE_PACKAGE_SOURCES, getOptionalPackagePresetSources, listOptionalPackagePresets } from "./pi/package-presets.js";
|
|
12
12
|
import { normalizeFeynmanSettings, normalizeThinkingLevel, parseModelSpec } from "./pi/settings.js";
|
|
13
|
-
import { loginModelProvider, logoutModelProvider, printModelList, setDefaultModelSpec, } from "./model/commands.js";
|
|
13
|
+
import { getCurrentModelSpec, loginModelProvider, logoutModelProvider, printModelList, setDefaultModelSpec, } from "./model/commands.js";
|
|
14
14
|
import { printSearchStatus } from "./search/commands.js";
|
|
15
15
|
import { runDoctor, runStatus } from "./setup/doctor.js";
|
|
16
16
|
import { setupPreviewDependencies } from "./setup/preview.js";
|
|
17
17
|
import { runSetup } from "./setup/setup.js";
|
|
18
|
-
import { printInfo, printPanel, printSection } from "./ui/terminal.js";
|
|
18
|
+
import { printAsciiHeader, printInfo, printPanel, printSection } from "./ui/terminal.js";
|
|
19
19
|
import { cliCommandSections, formatCliWorkflowUsage, legacyFlags, readPromptSpecs, topLevelCommandNames, } from "../metadata/commands.mjs";
|
|
20
20
|
const TOP_LEVEL_COMMANDS = new Set(topLevelCommandNames);
|
|
21
21
|
function printHelpLine(usage, description) {
|
|
@@ -25,7 +25,7 @@ function printHelpLine(usage, description) {
|
|
|
25
25
|
}
|
|
26
26
|
function printHelp(appRoot) {
|
|
27
27
|
const workflowCommands = readPromptSpecs(appRoot).filter((command) => command.section === "Research Workflows" && command.topLevelCli);
|
|
28
|
-
|
|
28
|
+
printAsciiHeader([
|
|
29
29
|
"Research-first agent shell built on Pi.",
|
|
30
30
|
"Use `feynman setup` first if this is a new machine.",
|
|
31
31
|
]);
|
|
@@ -87,7 +87,7 @@ async function handleModelCommand(subcommand, args, feynmanSettingsPath, feynman
|
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
if (subcommand === "login") {
|
|
90
|
-
await loginModelProvider(feynmanAuthPath, args[0]);
|
|
90
|
+
await loginModelProvider(feynmanAuthPath, args[0], feynmanSettingsPath);
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
if (subcommand === "logout") {
|
|
@@ -347,6 +347,21 @@ export async function main() {
|
|
|
347
347
|
throw new Error(`Unknown model: ${explicitModelSpec}`);
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
|
+
if (!explicitModelSpec && !getCurrentModelSpec(feynmanSettingsPath) && process.stdin.isTTY && process.stdout.isTTY) {
|
|
351
|
+
await runSetup({
|
|
352
|
+
settingsPath: feynmanSettingsPath,
|
|
353
|
+
bundledSettingsPath,
|
|
354
|
+
authPath: feynmanAuthPath,
|
|
355
|
+
workingDir,
|
|
356
|
+
sessionDir,
|
|
357
|
+
appRoot,
|
|
358
|
+
defaultThinkingLevel: thinkingLevel,
|
|
359
|
+
});
|
|
360
|
+
if (!getCurrentModelSpec(feynmanSettingsPath)) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
normalizeFeynmanSettings(feynmanSettingsPath, bundledSettingsPath, thinkingLevel, feynmanAuthPath);
|
|
364
|
+
}
|
|
350
365
|
await launchPiChat({
|
|
351
366
|
appRoot,
|
|
352
367
|
workingDir,
|
package/dist/model/commands.js
CHANGED
|
@@ -3,7 +3,7 @@ import { writeFileSync } from "node:fs";
|
|
|
3
3
|
import { readJson } from "../pi/settings.js";
|
|
4
4
|
import { promptChoice, promptText } from "../setup/prompts.js";
|
|
5
5
|
import { printInfo, printSection, printSuccess, printWarning } from "../ui/terminal.js";
|
|
6
|
-
import { buildModelStatusSnapshotFromRecords, getAvailableModelRecords, getSupportedModelRecords, } from "./catalog.js";
|
|
6
|
+
import { buildModelStatusSnapshotFromRecords, chooseRecommendedModel, getAvailableModelRecords, getSupportedModelRecords, } from "./catalog.js";
|
|
7
7
|
function collectModelStatus(settingsPath, authPath) {
|
|
8
8
|
return buildModelStatusSnapshotFromRecords(getSupportedModelRecords(authPath), getAvailableModelRecords(authPath), getCurrentModelSpec(settingsPath));
|
|
9
9
|
}
|
|
@@ -80,7 +80,7 @@ export function printModelList(settingsPath, authPath) {
|
|
|
80
80
|
printInfo(`${spec}${markers.length > 0 ? ` (${markers.join(", ")})` : ""}`);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
export async function loginModelProvider(authPath, providerId) {
|
|
83
|
+
export async function loginModelProvider(authPath, providerId, settingsPath) {
|
|
84
84
|
const provider = providerId ? resolveOAuthProvider(authPath, providerId) : await selectOAuthProvider(authPath, "login");
|
|
85
85
|
if (!provider) {
|
|
86
86
|
if (providerId) {
|
|
@@ -111,6 +111,19 @@ export async function loginModelProvider(authPath, providerId) {
|
|
|
111
111
|
signal: abortController.signal,
|
|
112
112
|
});
|
|
113
113
|
printSuccess(`Model provider login complete: ${provider.id}`);
|
|
114
|
+
if (settingsPath) {
|
|
115
|
+
const currentSpec = getCurrentModelSpec(settingsPath);
|
|
116
|
+
const available = getAvailableModelRecords(authPath);
|
|
117
|
+
const currentValid = currentSpec
|
|
118
|
+
? available.some((m) => `${m.provider}/${m.id}` === currentSpec)
|
|
119
|
+
: false;
|
|
120
|
+
if ((!currentSpec || !currentValid) && available.length > 0) {
|
|
121
|
+
const recommended = chooseRecommendedModel(authPath);
|
|
122
|
+
if (recommended) {
|
|
123
|
+
setDefaultModelSpec(settingsPath, authPath, recommended.spec);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
114
127
|
}
|
|
115
128
|
export async function logoutModelProvider(authPath, providerId) {
|
|
116
129
|
const provider = providerId ? resolveOAuthProvider(authPath, providerId) : await selectOAuthProvider(authPath, "logout");
|
|
@@ -138,28 +151,20 @@ export function setDefaultModelSpec(settingsPath, authPath, spec) {
|
|
|
138
151
|
printSuccess(`Default model set to ${resolvedSpec}`);
|
|
139
152
|
}
|
|
140
153
|
export async function runModelSetup(settingsPath, authPath) {
|
|
141
|
-
|
|
154
|
+
let status = collectModelStatus(settingsPath, authPath);
|
|
142
155
|
if (status.availableModels.length === 0) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
156
|
+
await loginModelProvider(authPath, undefined, settingsPath);
|
|
157
|
+
status = collectModelStatus(settingsPath, authPath);
|
|
158
|
+
if (status.availableModels.length === 0) {
|
|
159
|
+
return;
|
|
146
160
|
}
|
|
147
|
-
printInfo("Tip: run `feynman model login <provider>` if your provider supports Pi OAuth login.");
|
|
148
|
-
return;
|
|
149
161
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
spec === status.recommended ? "recommended" : undefined,
|
|
153
|
-
spec === status.current ? "current" : undefined,
|
|
154
|
-
].filter(Boolean);
|
|
155
|
-
return `${spec}${markers.length > 0 ? ` (${markers.join(", ")})` : ""}`;
|
|
156
|
-
});
|
|
157
|
-
choices.push(`Keep current (${status.current ?? "unset"})`);
|
|
158
|
-
const defaultIndex = status.current ? Math.max(0, status.availableModels.indexOf(status.current)) : 0;
|
|
159
|
-
const selection = await promptChoice("Select your default research model:", choices, defaultIndex >= 0 ? defaultIndex : 0);
|
|
160
|
-
if (selection >= status.availableModels.length) {
|
|
161
|
-
printInfo("Skipped (keeping current model)");
|
|
162
|
+
if (status.currentValid) {
|
|
163
|
+
printInfo(`Model: ${status.current}`);
|
|
162
164
|
return;
|
|
163
165
|
}
|
|
164
|
-
|
|
166
|
+
const recommended = status.recommended ?? status.availableModels[0];
|
|
167
|
+
if (recommended) {
|
|
168
|
+
setDefaultModelSpec(settingsPath, authPath, recommended);
|
|
169
|
+
}
|
|
165
170
|
}
|
package/dist/setup/setup.js
CHANGED
|
@@ -1,119 +1,38 @@
|
|
|
1
1
|
import { isLoggedIn as isAlphaLoggedIn, login as loginAlpha } from "@companion-ai/alpha-hub/lib";
|
|
2
|
-
import {
|
|
3
|
-
import { getPiWebAccessStatus, getPiWebSearchConfigPath } from "../pi/web-access.js";
|
|
2
|
+
import { getPiWebAccessStatus } from "../pi/web-access.js";
|
|
4
3
|
import { normalizeFeynmanSettings } from "../pi/settings.js";
|
|
5
4
|
import { getCurrentModelSpec, runModelSetup } from "../model/commands.js";
|
|
6
5
|
import { buildModelStatusSnapshotFromRecords, getAvailableModelRecords, getSupportedModelRecords } from "../model/catalog.js";
|
|
7
6
|
import { PANDOC_FALLBACK_PATHS, resolveExecutable } from "../system/executables.js";
|
|
8
|
-
import { promptText } from "./prompts.js";
|
|
9
7
|
import { setupPreviewDependencies } from "./preview.js";
|
|
10
|
-
import {
|
|
11
|
-
import { printInfo, printPanel, printSection, printSuccess } from "../ui/terminal.js";
|
|
12
|
-
async function explainWebAccess() {
|
|
13
|
-
const status = getPiWebAccessStatus();
|
|
14
|
-
printSection("Web Access");
|
|
15
|
-
printInfo("Feynman uses the bundled `pi-web-access` package directly.");
|
|
16
|
-
printInfo("Default v1 path: sign into gemini.google.com in a supported Chromium browser.");
|
|
17
|
-
printInfo(`Current search route: ${status.routeLabel}`);
|
|
18
|
-
printInfo(`Pi config path: ${status.configPath}`);
|
|
19
|
-
printInfo("Advanced users can edit the Pi config directly if they want API keys or a different route.");
|
|
20
|
-
}
|
|
21
|
-
function isPreviewConfigured() {
|
|
22
|
-
return Boolean(resolveExecutable("pandoc", PANDOC_FALLBACK_PATHS));
|
|
23
|
-
}
|
|
8
|
+
import { printInfo, printSection, printSuccess } from "../ui/terminal.js";
|
|
24
9
|
function isInteractiveTerminal() {
|
|
25
10
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
26
11
|
}
|
|
27
12
|
function printNonInteractiveSetupGuidance() {
|
|
28
|
-
|
|
29
|
-
"Non-interactive terminal detected.",
|
|
30
|
-
]);
|
|
31
|
-
printInfo("Use the explicit commands instead of the interactive setup wizard:");
|
|
32
|
-
printInfo(" feynman status");
|
|
13
|
+
printInfo("Non-interactive terminal. Use explicit commands:");
|
|
33
14
|
printInfo(" feynman model login <provider>");
|
|
34
15
|
printInfo(" feynman model set <provider/model>");
|
|
35
|
-
printInfo(" feynman search status");
|
|
36
|
-
printInfo(` edit ${getPiWebSearchConfigPath()} # optional advanced web config`);
|
|
37
16
|
printInfo(" feynman alpha login");
|
|
38
17
|
printInfo(" feynman doctor");
|
|
39
|
-
printInfo(" feynman # Pi's /login flow still works inside chat if you prefer it");
|
|
40
|
-
}
|
|
41
|
-
async function runPreviewSetup() {
|
|
42
|
-
const result = setupPreviewDependencies();
|
|
43
|
-
printSuccess(result.message);
|
|
44
|
-
}
|
|
45
|
-
function printConfigurationLocation(appRoot) {
|
|
46
|
-
printSection("Configuration Location");
|
|
47
|
-
printInfo(`Data folder: ${getFeynmanHome()}`);
|
|
48
|
-
printInfo(`Sessions: ${getDefaultSessionDir()}`);
|
|
49
|
-
printInfo(`Install dir: ${appRoot}`);
|
|
50
18
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
printInfo(`Model valid: ${modelStatus.currentValid ? "yes" : "no"}`);
|
|
56
|
-
printInfo(`Recommended model: ${modelStatus.recommended ?? "not available"}`);
|
|
57
|
-
printInfo(`alphaXiv: ${isAlphaLoggedIn() ? "configured" : "missing"}`);
|
|
58
|
-
printInfo(`Web access: pi-web-access (${getPiWebAccessStatus().routeLabel})`);
|
|
59
|
-
printInfo(`Preview: ${isPreviewConfigured() ? "configured" : "not configured"}`);
|
|
60
|
-
for (const line of modelStatus.guidance) {
|
|
61
|
-
printInfo(line);
|
|
19
|
+
export async function runSetup(options) {
|
|
20
|
+
if (!isInteractiveTerminal()) {
|
|
21
|
+
printNonInteractiveSetupGuidance();
|
|
22
|
+
return;
|
|
62
23
|
}
|
|
63
|
-
}
|
|
64
|
-
async function runFullSetup(options) {
|
|
65
|
-
printConfigurationLocation(options.appRoot);
|
|
66
24
|
await runModelSetup(options.settingsPath, options.authPath);
|
|
67
25
|
if (!isAlphaLoggedIn()) {
|
|
68
26
|
await loginAlpha();
|
|
69
27
|
printSuccess("alphaXiv login complete");
|
|
70
28
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
await explainWebAccess();
|
|
75
|
-
await runPreviewSetup();
|
|
29
|
+
const result = setupPreviewDependencies();
|
|
30
|
+
printSuccess(result.message);
|
|
76
31
|
normalizeFeynmanSettings(options.settingsPath, options.bundledSettingsPath, options.defaultThinkingLevel ?? "medium", options.authPath);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
});
|
|
84
|
-
printSetupSummary(options.settingsPath, options.authPath);
|
|
85
|
-
}
|
|
86
|
-
function hasExistingSetup(settingsPath, authPath) {
|
|
87
|
-
const modelStatus = buildModelStatusSnapshotFromRecords(getSupportedModelRecords(authPath), getAvailableModelRecords(authPath), getCurrentModelSpec(settingsPath));
|
|
88
|
-
return Boolean(modelStatus.current ||
|
|
89
|
-
modelStatus.availableModels.length > 0 ||
|
|
90
|
-
isAlphaLoggedIn() ||
|
|
91
|
-
isPreviewConfigured());
|
|
92
|
-
}
|
|
93
|
-
async function runDefaultInteractiveSetup(options) {
|
|
94
|
-
const existing = hasExistingSetup(options.settingsPath, options.authPath);
|
|
95
|
-
printPanel("Feynman Setup Wizard", [
|
|
96
|
-
"Guided setup for the research-first Pi agent.",
|
|
97
|
-
"Press Ctrl+C at any time to exit.",
|
|
98
|
-
]);
|
|
99
|
-
if (existing) {
|
|
100
|
-
printSection("Full Setup");
|
|
101
|
-
printInfo("Existing configuration detected. Rerunning the full guided setup.");
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
printInfo("We'll walk you through:");
|
|
105
|
-
printInfo(" 1. Model Selection");
|
|
106
|
-
printInfo(" 2. alphaXiv Login");
|
|
107
|
-
printInfo(" 3. Preview Dependencies");
|
|
108
|
-
}
|
|
109
|
-
printInfo("Press Enter to begin, or Ctrl+C to exit.");
|
|
110
|
-
await promptText("Press Enter to start");
|
|
111
|
-
await runFullSetup(options);
|
|
112
|
-
}
|
|
113
|
-
export async function runSetup(options) {
|
|
114
|
-
if (!isInteractiveTerminal()) {
|
|
115
|
-
printNonInteractiveSetupGuidance();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
await runDefaultInteractiveSetup(options);
|
|
32
|
+
const modelStatus = buildModelStatusSnapshotFromRecords(getSupportedModelRecords(options.authPath), getAvailableModelRecords(options.authPath), getCurrentModelSpec(options.settingsPath));
|
|
33
|
+
printSection("Ready");
|
|
34
|
+
printInfo(`Model: ${getCurrentModelSpec(options.settingsPath) ?? "not set"}`);
|
|
35
|
+
printInfo(`alphaXiv: ${isAlphaLoggedIn() ? "configured" : "not configured"}`);
|
|
36
|
+
printInfo(`Preview: ${resolveExecutable("pandoc", PANDOC_FALLBACK_PATHS) ? "configured" : "not configured"}`);
|
|
37
|
+
printInfo(`Web: ${getPiWebAccessStatus().routeLabel}`);
|
|
119
38
|
}
|
package/dist/ui/terminal.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FEYNMAN_ASCII_LOGO } from "../../logo.mjs";
|
|
1
2
|
const RESET = "\x1b[0m";
|
|
2
3
|
const BOLD = "\x1b[1m";
|
|
3
4
|
const DIM = "\x1b[2m";
|
|
@@ -31,6 +32,16 @@ export function printSection(title) {
|
|
|
31
32
|
console.log("");
|
|
32
33
|
console.log(paint(`◆ ${title}`, TEAL, BOLD));
|
|
33
34
|
}
|
|
35
|
+
export function printAsciiHeader(subtitleLines = []) {
|
|
36
|
+
console.log("");
|
|
37
|
+
for (const line of FEYNMAN_ASCII_LOGO) {
|
|
38
|
+
console.log(paint(` ${line}`, TEAL, BOLD));
|
|
39
|
+
}
|
|
40
|
+
for (const line of subtitleLines) {
|
|
41
|
+
console.log(paint(` ${line}`, ASH));
|
|
42
|
+
}
|
|
43
|
+
console.log("");
|
|
44
|
+
}
|
|
34
45
|
export function printPanel(title, subtitleLines = []) {
|
|
35
46
|
const inner = 53;
|
|
36
47
|
const border = "─".repeat(inner + 2);
|
|
@@ -230,8 +230,10 @@ export function installFeynmanHeader(
|
|
|
230
230
|
|
|
231
231
|
push("");
|
|
232
232
|
if (cardW >= 70) {
|
|
233
|
+
const maxLogoW = Math.max(...FEYNMAN_AGENT_LOGO.map((l) => l.length));
|
|
234
|
+
const logoOffset = " ".repeat(Math.max(0, Math.floor((cardW - maxLogoW) / 2)));
|
|
233
235
|
for (const logoLine of FEYNMAN_AGENT_LOGO) {
|
|
234
|
-
push(theme.fg("accent", theme.bold(
|
|
236
|
+
push(theme.fg("accent", theme.bold(`${logoOffset}${truncateVisible(logoLine, cardW)}`)));
|
|
235
237
|
}
|
|
236
238
|
push("");
|
|
237
239
|
}
|
package/logo.d.mts
CHANGED
package/logo.mjs
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export const FEYNMAN_ASCII_LOGO = [
|
|
2
|
-
"
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
2
|
+
" ██████",
|
|
3
|
+
" ███",
|
|
4
|
+
"█████████ ████████ ███ ███ ███ ██████ ██ ███ ████ ███████ ███ ██████",
|
|
5
|
+
" ███ ███ ███ ███ ███ ████ ███ ███ ██ ███ ███ ████ ███",
|
|
6
|
+
" ███ ████████████ ███ ███ ███ ███ ███ ██ ███ █████████ ███ ███",
|
|
7
|
+
" ███ ███ ██ ███ ███ ███ ███ ██ ███ ███ ███ ███ ███",
|
|
8
|
+
"███████ ████████ ████ ███ ███ ███ ██ ███ ██████ ███ ███ ███",
|
|
9
|
+
" ███",
|
|
10
|
+
" █████",
|
|
8
11
|
];
|
|
9
12
|
|
|
10
13
|
export const FEYNMAN_ASCII_LOGO_TEXT = FEYNMAN_ASCII_LOGO.join("\n");
|
|
11
14
|
|
|
12
|
-
export const FEYNMAN_LOGO_HTML = `<
|
|
15
|
+
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>`;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
5
|
+
import { FEYNMAN_LOGO_HTML } from "../logo.mjs";
|
|
6
6
|
|
|
7
7
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
const appRoot = resolve(here, "..");
|
|
@@ -365,7 +365,7 @@ if (oauthPagePath && existsSync(oauthPagePath)) {
|
|
|
365
365
|
let source = readFileSync(oauthPagePath, "utf8");
|
|
366
366
|
const piLogo = 'const LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>`;';
|
|
367
367
|
if (source.includes(piLogo)) {
|
|
368
|
-
const feynmanLogo = `const LOGO_SVG = \`${
|
|
368
|
+
const feynmanLogo = `const LOGO_SVG = \`${FEYNMAN_LOGO_HTML}\`;`;
|
|
369
369
|
source = source.replace(piLogo, feynmanLogo);
|
|
370
370
|
writeFileSync(oauthPagePath, source, "utf8");
|
|
371
371
|
}
|
|
@@ -377,17 +377,17 @@ const alphaHubAuthPath = findPackageRoot("@companion-ai/alpha-hub")
|
|
|
377
377
|
|
|
378
378
|
if (alphaHubAuthPath && existsSync(alphaHubAuthPath)) {
|
|
379
379
|
let source = readFileSync(alphaHubAuthPath, "utf8");
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
const
|
|
385
|
-
const
|
|
380
|
+
const oldSuccess = "'<html><body><h2>Logged in to Alpha Hub</h2><p>You can close this tab.</p></body></html>'";
|
|
381
|
+
const oldError = "'<html><body><h2>Login failed</h2><p>You can close this tab.</p></body></html>'";
|
|
382
|
+
const bodyAttr = `style="font-family:system-ui,sans-serif;text-align:center;padding-top:20vh;background:#050a08;color:#f0f5f2"`;
|
|
383
|
+
const logo = `<h1 style="font-family:monospace;font-size:48px;color:#34d399;margin:0">feynman</h1>`;
|
|
384
|
+
const newSuccess = `'<html><body ${bodyAttr}>${logo}<h2 style="color:#34d399;margin-top:16px">Logged in</h2><p style="color:#8aaa9a">You can close this tab.</p></body></html>'`;
|
|
385
|
+
const newError = `'<html><body ${bodyAttr}>${logo}<h2 style="color:#ef4444;margin-top:16px">Login failed</h2><p style="color:#8aaa9a">You can close this tab.</p></body></html>'`;
|
|
386
386
|
if (source.includes(oldSuccess)) {
|
|
387
|
-
source = source.replace(oldSuccess,
|
|
387
|
+
source = source.replace(oldSuccess, newSuccess);
|
|
388
388
|
}
|
|
389
389
|
if (source.includes(oldError)) {
|
|
390
|
-
source = source.replace(oldError,
|
|
390
|
+
source = source.replace(oldError, newError);
|
|
391
391
|
}
|
|
392
392
|
writeFileSync(alphaHubAuthPath, source, "utf8");
|
|
393
393
|
}
|