@phi-code-admin/phi-code 0.75.4 → 0.75.5
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/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +3 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/extensions/phi/README.md +21 -1
- package/extensions/phi/init.ts +366 -372
- package/extensions/phi/models.ts +236 -0
- package/extensions/phi/providers/live-models.ts +493 -0
- package/extensions/phi/providers/opencode-go.ts +15 -10
- package/extensions/phi/setup.ts +84 -32
- package/package.json +1 -1
package/extensions/phi/setup.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
pingOpenCodeGo,
|
|
41
41
|
validateOpenCodeGoApiKey,
|
|
42
42
|
} from "./providers/opencode-go.js";
|
|
43
|
+
import { fetchLiveModels, pingProvider, toPersistedModel } from "./providers/live-models.js";
|
|
43
44
|
|
|
44
45
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
45
46
|
|
|
@@ -176,22 +177,9 @@ function getProviderCatalog(): ProviderEntry[] {
|
|
|
176
177
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
177
178
|
|
|
178
179
|
async function probeLocalProvider(provider: ProviderEntry): Promise<string[]> {
|
|
179
|
-
if (!provider.
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const res = await fetch(provider.probeUrl, {
|
|
184
|
-
signal: controller.signal,
|
|
185
|
-
headers: { Authorization: `Bearer ${provider.id === "ollama" ? "ollama" : "lm-studio"}` },
|
|
186
|
-
});
|
|
187
|
-
clearTimeout(timeout);
|
|
188
|
-
if (!res.ok) return [];
|
|
189
|
-
const data = (await res.json()) as { data?: Array<{ id?: string; name?: string }> };
|
|
190
|
-
return (data.data ?? []).map((m) => m.id ?? m.name ?? "").filter(Boolean);
|
|
191
|
-
} catch {
|
|
192
|
-
clearTimeout(timeout);
|
|
193
|
-
return [];
|
|
194
|
-
}
|
|
180
|
+
if (!provider.local) return [];
|
|
181
|
+
const result = await fetchLiveModels(provider.id, { forceRefresh: true, timeoutMs: 2_500 });
|
|
182
|
+
return result.source === "live" ? result.models.map((m) => m.id) : [];
|
|
195
183
|
}
|
|
196
184
|
|
|
197
185
|
function buildStatusWidget(
|
|
@@ -442,21 +430,53 @@ async function configureGenericCloud(
|
|
|
442
430
|
if (!proceed) return undefined;
|
|
443
431
|
}
|
|
444
432
|
|
|
433
|
+
// Persist the key immediately so a downstream fetch failure cannot lose user input.
|
|
445
434
|
store.setKey(provider.id, trimmed, {
|
|
446
435
|
baseUrl: provider.baseUrl,
|
|
447
436
|
api: provider.api,
|
|
448
|
-
models: provider.staticModels.map((id) => ({
|
|
449
|
-
id,
|
|
450
|
-
name: id,
|
|
451
|
-
reasoning: true,
|
|
452
|
-
input: ["text"] as const,
|
|
453
|
-
})),
|
|
454
437
|
});
|
|
438
|
+
|
|
439
|
+
// Optional ping for early auth diagnostics.
|
|
440
|
+
ui.setStatus("setup-ping", `Pinging ${provider.displayName}...`);
|
|
441
|
+
const ping = await pingProvider(provider.id, trimmed, 5_000).catch((err) => ({
|
|
442
|
+
ok: false,
|
|
443
|
+
error: err instanceof Error ? err.message : String(err),
|
|
444
|
+
}));
|
|
445
|
+
ui.setStatus("setup-ping", undefined);
|
|
446
|
+
if (ping.ok) {
|
|
447
|
+
ui.notify(`${provider.displayName} ping OK (200).`, "info");
|
|
448
|
+
} else {
|
|
449
|
+
ui.notify(
|
|
450
|
+
`${provider.displayName} ping failed: ${ping.error ?? "unknown"}. Key saved; you can retry with \`/keys test ${provider.id}\`.`,
|
|
451
|
+
"warning",
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Live-fetch the model catalog (falls back to the static list when offline).
|
|
456
|
+
ui.setStatus("setup-fetch", `Fetching ${provider.displayName} model list...`);
|
|
457
|
+
const live = await fetchLiveModels(provider.id, {
|
|
458
|
+
apiKey: trimmed,
|
|
459
|
+
forceRefresh: true,
|
|
460
|
+
timeoutMs: 6_000,
|
|
461
|
+
});
|
|
462
|
+
ui.setStatus("setup-fetch", undefined);
|
|
463
|
+
|
|
464
|
+
const models = (live.models.length > 0
|
|
465
|
+
? live.models
|
|
466
|
+
: provider.staticModels.map((id) => ({ id, name: id, reasoning: true }))
|
|
467
|
+
).map(toPersistedModel);
|
|
468
|
+
|
|
469
|
+
store.setKey(provider.id, trimmed, {
|
|
470
|
+
baseUrl: provider.baseUrl,
|
|
471
|
+
api: provider.api,
|
|
472
|
+
models,
|
|
473
|
+
});
|
|
474
|
+
|
|
455
475
|
ui.notify(
|
|
456
|
-
`${provider.displayName} configured: \`${maskKeyForDisplay(trimmed)}\` (${
|
|
476
|
+
`${provider.displayName} configured: \`${maskKeyForDisplay(trimmed)}\` (${models.length} models, source: ${live.source}${live.error ? `, ${live.error}` : ""})`,
|
|
457
477
|
"info",
|
|
458
478
|
);
|
|
459
|
-
return { providerId: provider.id, modelCount:
|
|
479
|
+
return { providerId: provider.id, modelCount: models.length };
|
|
460
480
|
}
|
|
461
481
|
|
|
462
482
|
async function configureLocal(
|
|
@@ -539,7 +559,23 @@ async function configureAssignments(
|
|
|
539
559
|
|
|
540
560
|
// ─── Extension ───────────────────────────────────────────────────────────
|
|
541
561
|
|
|
562
|
+
// One-time global guard so a stray async rejection inside the wizard never kills the TUI.
|
|
563
|
+
let setupUnhandledGuard = false;
|
|
564
|
+
function installSetupUnhandledRejectionGuard(): void {
|
|
565
|
+
if (setupUnhandledGuard) return;
|
|
566
|
+
setupUnhandledGuard = true;
|
|
567
|
+
process.on("unhandledRejection", (reason) => {
|
|
568
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
569
|
+
try {
|
|
570
|
+
process.stderr.write(`[phi-setup] swallowed unhandledRejection: ${message}\n`);
|
|
571
|
+
} catch {
|
|
572
|
+
// no-op
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
542
577
|
export default function setupExtension(pi: ExtensionAPI) {
|
|
578
|
+
installSetupUnhandledRejectionGuard();
|
|
543
579
|
pi.registerCommand("setup", {
|
|
544
580
|
description: "Phi Code setup wizard (refonte UX, replaces /phi-init)",
|
|
545
581
|
handler: async (_args, ctx) => {
|
|
@@ -551,6 +587,7 @@ export default function setupExtension(pi: ExtensionAPI) {
|
|
|
551
587
|
// empty file or missing, fine
|
|
552
588
|
}
|
|
553
589
|
|
|
590
|
+
try {
|
|
554
591
|
ui.notify(
|
|
555
592
|
"**φ Phi Code Setup Wizard**\n\n" +
|
|
556
593
|
"This wizard configures providers and assigns models to agent roles.\n" +
|
|
@@ -652,14 +689,21 @@ export default function setupExtension(pi: ExtensionAPI) {
|
|
|
652
689
|
const provider = catalog[providerIndex];
|
|
653
690
|
|
|
654
691
|
let result: { providerId: string; modelCount: number } | undefined;
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
692
|
+
try {
|
|
693
|
+
if (provider.id === "alibaba-codingplan") {
|
|
694
|
+
result = await configureAlibaba(ui, store);
|
|
695
|
+
} else if (provider.id === "opencode-go") {
|
|
696
|
+
result = await configureOpenCodeGo(ui, store);
|
|
697
|
+
} else if (provider.local) {
|
|
698
|
+
result = await configureLocal(ui, store, provider);
|
|
699
|
+
} else {
|
|
700
|
+
result = await configureGenericCloud(ui, store, provider);
|
|
701
|
+
}
|
|
702
|
+
} catch (err) {
|
|
703
|
+
ui.notify(
|
|
704
|
+
`Provider configuration failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
705
|
+
"error",
|
|
706
|
+
);
|
|
663
707
|
}
|
|
664
708
|
|
|
665
709
|
if (result) refreshAvailable();
|
|
@@ -681,12 +725,20 @@ export default function setupExtension(pi: ExtensionAPI) {
|
|
|
681
725
|
"**Setup complete.**\n\n" +
|
|
682
726
|
"Next steps:\n" +
|
|
683
727
|
" - `/keys` to list/manage saved keys\n" +
|
|
728
|
+
" - `/models refresh` to re-fetch the catalog from each provider's API\n" +
|
|
684
729
|
" - `/routing` to inspect routing\n" +
|
|
685
730
|
" - `/agents` to list sub-agents\n" +
|
|
686
731
|
" - `/skills` to list skills\n" +
|
|
687
732
|
" - Edit `~/.phi/agent/models.json` or `routing.json` directly: hot-reload kicks in",
|
|
688
733
|
"info",
|
|
689
734
|
);
|
|
735
|
+
} catch (err) {
|
|
736
|
+
ui.setWidget("setup-status", undefined);
|
|
737
|
+
ui.notify(
|
|
738
|
+
`Setup wizard error: ${err instanceof Error ? err.message : String(err)}`,
|
|
739
|
+
"error",
|
|
740
|
+
);
|
|
741
|
+
}
|
|
690
742
|
},
|
|
691
743
|
});
|
|
692
744
|
}
|