@phi-code-admin/phi-code 0.96.1 → 0.97.1
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/CHANGELOG.md +61 -0
- package/dist/core/slash-commands.d.ts +13 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +38 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +8 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +67 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/extensions/phi/benchmark.ts +12 -56
- package/extensions/phi/init.ts +86 -673
- package/extensions/phi/providers/catalog.ts +133 -0
- package/extensions/phi/setup.ts +180 -280
- package/extensions/phi/smart-router.ts +0 -17
- package/package.json +3 -3
package/extensions/phi/setup.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Setup Wizard Extension (/setup) - Refonte UX du wizard de configuration.
|
|
3
3
|
*
|
|
4
|
-
* Per Q8 strategy B: nouvelle commande /setup qui remplace /phi-init
|
|
5
|
-
*
|
|
4
|
+
* Per Q8 strategy B: nouvelle commande /setup qui remplace /phi-init.
|
|
5
|
+
* Depuis 0.97.0 c'est LE seul wizard : /phi-init (init.ts) importe
|
|
6
|
+
* runSetupWizard d'ici au lieu d'entretenir sa propre copie, et le catalogue
|
|
7
|
+
* de providers vit dans providers/catalog.ts (partagé avec /benchmark). UX:
|
|
6
8
|
* - Panneau de statut persistant via setWidget (providers + assignments)
|
|
7
9
|
* - Fuzzy search via ctx.ui.select (matching builtin Phi/upstream)
|
|
8
10
|
* - Ping API immédiat à la saisie de clé (validation 401/200)
|
|
@@ -25,39 +27,25 @@ import { join } from "node:path";
|
|
|
25
27
|
import { ApiKeyStore, type ExtensionAPI, type ExtensionUIContext, getApiKeyStore, getConfigWatcher } from "phi-code";
|
|
26
28
|
import {
|
|
27
29
|
ALIBABA_ENV_VAR,
|
|
28
|
-
ALIBABA_MODELS,
|
|
29
30
|
ALIBABA_PROVIDERS,
|
|
30
31
|
ALIBABA_RATE_LIMITS,
|
|
31
32
|
buildAlibabaProviderConfig,
|
|
32
33
|
pingAlibaba,
|
|
33
34
|
validateAlibabaApiKey,
|
|
34
35
|
} from "./providers/alibaba.js";
|
|
36
|
+
import { getProviderCatalog, type ProviderCatalogEntry } from "./providers/catalog.js";
|
|
35
37
|
import { fetchLiveModels, pingProvider, toPersistedModel } from "./providers/live-models.js";
|
|
36
38
|
import {
|
|
37
39
|
buildOpenCodeGoAnthropicProviderConfig,
|
|
38
40
|
buildOpenCodeGoProviderConfig,
|
|
39
41
|
getOpenCodeGoModels,
|
|
40
42
|
OPENCODE_GO_AUTH_URL,
|
|
41
|
-
OPENCODE_GO_ENV_VAR,
|
|
42
43
|
pingOpenCodeGo,
|
|
43
44
|
validateOpenCodeGoApiKey,
|
|
44
45
|
} from "./providers/opencode-go.js";
|
|
45
46
|
|
|
46
47
|
// ─── Types ───────────────────────────────────────────────────────────────
|
|
47
48
|
|
|
48
|
-
interface ProviderEntry {
|
|
49
|
-
id: string;
|
|
50
|
-
displayName: string;
|
|
51
|
-
envVar: string;
|
|
52
|
-
baseUrl: string;
|
|
53
|
-
api: string;
|
|
54
|
-
staticModels: string[];
|
|
55
|
-
supportsOAuth?: boolean;
|
|
56
|
-
local?: boolean;
|
|
57
|
-
probeUrl?: string;
|
|
58
|
-
docUrl?: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
49
|
interface RouteAssignment {
|
|
62
50
|
preferred: string;
|
|
63
51
|
fallback: string;
|
|
@@ -77,7 +65,7 @@ interface RoutingConfigOut {
|
|
|
77
65
|
default: { model: string; agent: string | null };
|
|
78
66
|
}
|
|
79
67
|
|
|
80
|
-
const ORCHESTRATION_ROLES = [
|
|
68
|
+
export const ORCHESTRATION_ROLES = [
|
|
81
69
|
{
|
|
82
70
|
key: "explore",
|
|
83
71
|
label: "Explore",
|
|
@@ -117,106 +105,11 @@ const ORCHESTRATION_ROLES = [
|
|
|
117
105
|
|
|
118
106
|
const DEBUG_KEYWORDS = ["fix", "bug", "error", "debug", "crash", "broken", "failing", "issue", "troubleshoot"];
|
|
119
107
|
|
|
120
|
-
// ─── Provider catalog ────────────────────────────────────────────────────
|
|
121
|
-
|
|
122
|
-
function getProviderCatalog(): ProviderEntry[] {
|
|
123
|
-
return [
|
|
124
|
-
{
|
|
125
|
-
id: "alibaba-codingplan",
|
|
126
|
-
displayName: "Alibaba Coding Plan",
|
|
127
|
-
envVar: ALIBABA_ENV_VAR,
|
|
128
|
-
baseUrl: ALIBABA_PROVIDERS.openai.baseUrl,
|
|
129
|
-
api: "openai-completions",
|
|
130
|
-
staticModels: ALIBABA_MODELS.map((m) => m.id),
|
|
131
|
-
docUrl: "https://www.alibabacloud.com/help/en/model-studio/coding-plan",
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
id: "opencode-go",
|
|
135
|
-
displayName: "OpenCode Go (zen)",
|
|
136
|
-
envVar: OPENCODE_GO_ENV_VAR,
|
|
137
|
-
baseUrl: "https://opencode.ai/zen/go/v1",
|
|
138
|
-
api: "openai-completions",
|
|
139
|
-
staticModels: [],
|
|
140
|
-
docUrl: OPENCODE_GO_AUTH_URL,
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
id: "opencode",
|
|
144
|
-
displayName: "OpenCode Zen",
|
|
145
|
-
envVar: "OPENCODE_API_KEY",
|
|
146
|
-
baseUrl: "https://opencode.ai/zen/v1",
|
|
147
|
-
api: "openai-completions",
|
|
148
|
-
staticModels: [],
|
|
149
|
-
docUrl: OPENCODE_GO_AUTH_URL,
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
id: "openai",
|
|
153
|
-
displayName: "OpenAI",
|
|
154
|
-
envVar: "OPENAI_API_KEY",
|
|
155
|
-
baseUrl: "https://api.openai.com/v1",
|
|
156
|
-
api: "openai-completions",
|
|
157
|
-
staticModels: ["gpt-4o", "gpt-4o-mini", "o1", "o3-mini", "gpt-5"],
|
|
158
|
-
supportsOAuth: true,
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
id: "anthropic",
|
|
162
|
-
displayName: "Anthropic",
|
|
163
|
-
envVar: "ANTHROPIC_API_KEY",
|
|
164
|
-
baseUrl: "https://api.anthropic.com/v1",
|
|
165
|
-
api: "anthropic-messages",
|
|
166
|
-
staticModels: ["claude-opus-4-6", "claude-sonnet-4-6", "claude-3-5-haiku-20241022"],
|
|
167
|
-
supportsOAuth: true,
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
id: "google",
|
|
171
|
-
displayName: "Google Gemini",
|
|
172
|
-
envVar: "GOOGLE_API_KEY",
|
|
173
|
-
baseUrl: "https://generativelanguage.googleapis.com/v1beta",
|
|
174
|
-
api: "google",
|
|
175
|
-
staticModels: ["gemini-2.5-pro", "gemini-2.5-flash"],
|
|
176
|
-
supportsOAuth: true,
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
id: "openrouter",
|
|
180
|
-
displayName: "OpenRouter",
|
|
181
|
-
envVar: "OPENROUTER_API_KEY",
|
|
182
|
-
baseUrl: "https://openrouter.ai/api/v1",
|
|
183
|
-
api: "openai-completions",
|
|
184
|
-
staticModels: [],
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
id: "groq",
|
|
188
|
-
displayName: "Groq",
|
|
189
|
-
envVar: "GROQ_API_KEY",
|
|
190
|
-
baseUrl: "https://api.groq.com/openai/v1",
|
|
191
|
-
api: "openai-completions",
|
|
192
|
-
staticModels: ["llama-3.3-70b-versatile", "openai/gpt-oss-120b"],
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
id: "ollama",
|
|
196
|
-
displayName: "Ollama (local)",
|
|
197
|
-
envVar: "OLLAMA",
|
|
198
|
-
baseUrl: "http://localhost:11434/v1",
|
|
199
|
-
api: "openai-completions",
|
|
200
|
-
staticModels: [],
|
|
201
|
-
local: true,
|
|
202
|
-
probeUrl: "http://localhost:11434/v1/models",
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
id: "lm-studio",
|
|
206
|
-
displayName: "LM Studio (local)",
|
|
207
|
-
envVar: "LM_STUDIO",
|
|
208
|
-
baseUrl: "http://localhost:1234/v1",
|
|
209
|
-
api: "openai-completions",
|
|
210
|
-
staticModels: [],
|
|
211
|
-
local: true,
|
|
212
|
-
probeUrl: "http://localhost:1234/v1/models",
|
|
213
|
-
},
|
|
214
|
-
];
|
|
215
|
-
}
|
|
216
|
-
|
|
217
108
|
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
109
|
+
// The provider catalog itself lives in providers/catalog.ts (shared with
|
|
110
|
+
// /phi-init and /benchmark).
|
|
218
111
|
|
|
219
|
-
async function probeLocalProvider(provider:
|
|
112
|
+
async function probeLocalProvider(provider: ProviderCatalogEntry): Promise<string[]> {
|
|
220
113
|
if (!provider.local) return [];
|
|
221
114
|
const result = await fetchLiveModels(provider.id, { forceRefresh: true, timeoutMs: 2_500 });
|
|
222
115
|
return result.source === "live" ? result.models.map((m) => m.id) : [];
|
|
@@ -256,7 +149,10 @@ function maskKeyForDisplay(key: string | undefined): string {
|
|
|
256
149
|
return ApiKeyStore.maskKey(key);
|
|
257
150
|
}
|
|
258
151
|
|
|
259
|
-
function buildRoutingConfig(
|
|
152
|
+
export function buildRoutingConfig(
|
|
153
|
+
defaultModel: string,
|
|
154
|
+
orchestration: Record<string, RouteAssignment>,
|
|
155
|
+
): RoutingConfigOut {
|
|
260
156
|
const routes: RoutingConfigOut["routes"] = {};
|
|
261
157
|
for (const role of ORCHESTRATION_ROLES) {
|
|
262
158
|
const a = orchestration[role.key] ?? { preferred: defaultModel, fallback: defaultModel };
|
|
@@ -281,7 +177,7 @@ function buildRoutingConfig(defaultModel: string, orchestration: Record<string,
|
|
|
281
177
|
};
|
|
282
178
|
}
|
|
283
179
|
|
|
284
|
-
async function writeRoutingConfig(routing: RoutingConfigOut): Promise<string> {
|
|
180
|
+
export async function writeRoutingConfig(routing: RoutingConfigOut): Promise<string> {
|
|
285
181
|
const dir = join(homedir(), ".phi", "agent");
|
|
286
182
|
await mkdir(dir, { recursive: true });
|
|
287
183
|
const path = join(dir, "routing.json");
|
|
@@ -448,7 +344,7 @@ async function configureOpenCodeGo(
|
|
|
448
344
|
async function configureGenericCloud(
|
|
449
345
|
ui: ExtensionUIContext,
|
|
450
346
|
store: ApiKeyStore,
|
|
451
|
-
provider:
|
|
347
|
+
provider: ProviderCatalogEntry,
|
|
452
348
|
): Promise<{ providerId: string; modelCount: number } | undefined> {
|
|
453
349
|
if (provider.supportsOAuth) {
|
|
454
350
|
const authChoice = await ui.select(`Auth method for ${provider.displayName}`, [
|
|
@@ -538,7 +434,7 @@ async function configureGenericCloud(
|
|
|
538
434
|
async function configureLocal(
|
|
539
435
|
ui: ExtensionUIContext,
|
|
540
436
|
store: ApiKeyStore,
|
|
541
|
-
provider:
|
|
437
|
+
provider: ProviderCatalogEntry,
|
|
542
438
|
): Promise<{ providerId: string; modelCount: number } | undefined> {
|
|
543
439
|
ui.setStatus("setup-probe", `Probing ${provider.displayName}...`);
|
|
544
440
|
const models = await probeLocalProvider(provider);
|
|
@@ -586,7 +482,7 @@ async function pickModelFromCatalog(
|
|
|
586
482
|
return models.find((m) => m.display === choice)?.ref;
|
|
587
483
|
}
|
|
588
484
|
|
|
589
|
-
async function configureAssignments(
|
|
485
|
+
export async function configureAssignments(
|
|
590
486
|
ui: ExtensionUIContext,
|
|
591
487
|
models: Array<{ ref: string; display: string }>,
|
|
592
488
|
): Promise<{ defaultModel: string; orchestration: Record<string, RouteAssignment> }> {
|
|
@@ -639,180 +535,184 @@ function installSetupUnhandledRejectionGuard(): void {
|
|
|
639
535
|
});
|
|
640
536
|
}
|
|
641
537
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
538
|
+
/**
|
|
539
|
+
* The full setup wizard flow. Exported so /phi-init (init.ts) delegates to the
|
|
540
|
+
* exact same wizard instead of maintaining a drifting second copy.
|
|
541
|
+
*/
|
|
542
|
+
export async function runSetupWizard(ui: ExtensionUIContext): Promise<void> {
|
|
543
|
+
const store = getApiKeyStore();
|
|
544
|
+
try {
|
|
545
|
+
store.load();
|
|
546
|
+
} catch {
|
|
547
|
+
// empty file or missing, fine
|
|
548
|
+
}
|
|
654
549
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
550
|
+
try {
|
|
551
|
+
ui.notify(
|
|
552
|
+
"**φ Phi Code Setup Wizard**\n\n" +
|
|
553
|
+
"This wizard configures providers and assigns models to **orchestration roles** " +
|
|
554
|
+
"(used by `/plan`).\n" +
|
|
555
|
+
"The **chat default model is controlled via `/model`** and stays sticky across " +
|
|
556
|
+
"prompts — this wizard will never change it.\n\n" +
|
|
557
|
+
"Keys are stored in `~/.phi/agent/models.json` (chmod 0600 on Unix). " +
|
|
558
|
+
"Edit that file directly later to hot-reload (no restart needed).",
|
|
559
|
+
"info",
|
|
560
|
+
);
|
|
666
561
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
const refreshAvailable = (): void => {
|
|
673
|
-
const catalog = getProviderCatalog();
|
|
674
|
-
available.clear();
|
|
675
|
-
for (const p of catalog) {
|
|
676
|
-
const stored = store.getProvider(p.id);
|
|
677
|
-
if (stored?.apiKey) {
|
|
678
|
-
const source = stored.apiKey === "$OAUTH" ? "key" : stored.apiKey === "local" ? "local" : "key";
|
|
679
|
-
const modelCount = Array.isArray(stored.models) ? stored.models.length : p.staticModels.length;
|
|
680
|
-
available.set(p.id, { source, modelCount });
|
|
681
|
-
continue;
|
|
682
|
-
}
|
|
683
|
-
if (!p.local && process.env[p.envVar]) {
|
|
684
|
-
available.set(p.id, { source: "env", modelCount: p.staticModels.length });
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
ui.setWidget("setup-status", buildStatusWidget(store, available, assignments));
|
|
688
|
-
};
|
|
562
|
+
const available = new Map<string, { source: "key" | "env" | "local"; modelCount: number }>();
|
|
563
|
+
const assignments: { default?: string; orchestration: Record<string, RouteAssignment> } = {
|
|
564
|
+
orchestration: {},
|
|
565
|
+
};
|
|
689
566
|
|
|
690
|
-
|
|
567
|
+
const refreshAvailable = (): void => {
|
|
568
|
+
const catalog = getProviderCatalog();
|
|
569
|
+
available.clear();
|
|
570
|
+
for (const p of catalog) {
|
|
571
|
+
const stored = store.getProvider(p.id);
|
|
572
|
+
if (stored?.apiKey) {
|
|
573
|
+
const source = stored.apiKey === "$OAUTH" ? "key" : stored.apiKey === "local" ? "local" : "key";
|
|
574
|
+
const modelCount = Array.isArray(stored.models) ? stored.models.length : p.staticModels.length;
|
|
575
|
+
available.set(p.id, { source, modelCount });
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (!p.local && process.env[p.envVar]) {
|
|
579
|
+
available.set(p.id, { source: "env", modelCount: p.staticModels.length });
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
ui.setWidget("setup-status", buildStatusWidget(store, available, assignments));
|
|
583
|
+
};
|
|
691
584
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
585
|
+
refreshAvailable();
|
|
586
|
+
|
|
587
|
+
let done = false;
|
|
588
|
+
while (!done) {
|
|
589
|
+
const catalog = getProviderCatalog();
|
|
590
|
+
const choices: string[] = [];
|
|
591
|
+
for (const p of catalog) {
|
|
592
|
+
const state = available.get(p.id);
|
|
593
|
+
const tag = state ? "[ok]" : "[--]";
|
|
594
|
+
const modelTag = state ? ` (${state.modelCount} models)` : "";
|
|
595
|
+
choices.push(`${tag} ${p.displayName}${modelTag}`);
|
|
596
|
+
}
|
|
597
|
+
choices.push("---");
|
|
598
|
+
choices.push("Assign models to agent roles");
|
|
599
|
+
choices.push("Finish and save");
|
|
600
|
+
choices.push("Quit without saving");
|
|
601
|
+
|
|
602
|
+
const action = await ui.select("Phi Code Setup - choose an action", choices);
|
|
603
|
+
if (!action) {
|
|
604
|
+
done = true;
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
712
607
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
608
|
+
if (action === "Quit without saving") {
|
|
609
|
+
const confirm = await ui.confirm(
|
|
610
|
+
"Quit",
|
|
611
|
+
"Discard all in-memory assignments? (provider keys already saved are kept)",
|
|
612
|
+
);
|
|
613
|
+
if (confirm) {
|
|
614
|
+
ui.setWidget("setup-status", undefined);
|
|
615
|
+
ui.notify("Setup cancelled (saved provider keys are unchanged).", "warning");
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
725
620
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
621
|
+
if (action === "Finish and save") {
|
|
622
|
+
if (!assignments.default || Object.keys(assignments.orchestration).length === 0) {
|
|
623
|
+
const proceed = await ui.confirm(
|
|
624
|
+
"Assignments incomplete",
|
|
625
|
+
"You have not assigned all roles. Save current state anyway?",
|
|
626
|
+
);
|
|
627
|
+
if (!proceed) continue;
|
|
628
|
+
}
|
|
629
|
+
done = true;
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
737
632
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
const { defaultModel, orchestration } = await configureAssignments(ui, allModels);
|
|
756
|
-
assignments.default = defaultModel;
|
|
757
|
-
assignments.orchestration = orchestration;
|
|
758
|
-
refreshAvailable();
|
|
759
|
-
continue;
|
|
633
|
+
if (action === "Assign models to agent roles") {
|
|
634
|
+
const allModels: Array<{ ref: string; display: string }> = [];
|
|
635
|
+
const seenRefs = new Set<string>();
|
|
636
|
+
for (const p of catalog) {
|
|
637
|
+
const stored = store.getProvider(p.id);
|
|
638
|
+
if (!stored) continue;
|
|
639
|
+
const models = Array.isArray(stored.models) ? stored.models : [];
|
|
640
|
+
for (const m of models) {
|
|
641
|
+
const id = (m as { id?: string }).id;
|
|
642
|
+
if (typeof id !== "string") continue;
|
|
643
|
+
// Provider-qualified ref so the same id from different providers stays distinct.
|
|
644
|
+
const ref = `${p.id}/${id}`;
|
|
645
|
+
if (seenRefs.has(ref)) continue;
|
|
646
|
+
seenRefs.add(ref);
|
|
647
|
+
allModels.push({ ref, display: `${id} [${p.id}]` });
|
|
760
648
|
}
|
|
649
|
+
}
|
|
650
|
+
const { defaultModel, orchestration } = await configureAssignments(ui, allModels);
|
|
651
|
+
assignments.default = defaultModel;
|
|
652
|
+
assignments.orchestration = orchestration;
|
|
653
|
+
refreshAvailable();
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
761
656
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
const providerIndex = choices.indexOf(action);
|
|
765
|
-
if (providerIndex < 0 || providerIndex >= catalog.length) continue;
|
|
766
|
-
const provider = catalog[providerIndex];
|
|
767
|
-
|
|
768
|
-
let result: { providerId: string; modelCount: number } | undefined;
|
|
769
|
-
try {
|
|
770
|
-
if (provider.id === "alibaba-codingplan") {
|
|
771
|
-
result = await configureAlibaba(ui, store);
|
|
772
|
-
} else if (provider.id === "opencode-go") {
|
|
773
|
-
result = await configureOpenCodeGo(ui, store);
|
|
774
|
-
} else if (provider.local) {
|
|
775
|
-
result = await configureLocal(ui, store, provider);
|
|
776
|
-
} else {
|
|
777
|
-
result = await configureGenericCloud(ui, store, provider);
|
|
778
|
-
}
|
|
779
|
-
} catch (err) {
|
|
780
|
-
ui.notify(
|
|
781
|
-
`Provider configuration failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
782
|
-
"error",
|
|
783
|
-
);
|
|
784
|
-
}
|
|
657
|
+
if (action === "---") continue;
|
|
785
658
|
|
|
786
|
-
|
|
787
|
-
|
|
659
|
+
const providerIndex = choices.indexOf(action);
|
|
660
|
+
if (providerIndex < 0 || providerIndex >= catalog.length) continue;
|
|
661
|
+
const provider = catalog[providerIndex];
|
|
788
662
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
663
|
+
let result: { providerId: string; modelCount: number } | undefined;
|
|
664
|
+
try {
|
|
665
|
+
if (provider.id === "alibaba-codingplan") {
|
|
666
|
+
result = await configureAlibaba(ui, store);
|
|
667
|
+
} else if (provider.id === "opencode-go") {
|
|
668
|
+
result = await configureOpenCodeGo(ui, store);
|
|
669
|
+
} else if (provider.local) {
|
|
670
|
+
result = await configureLocal(ui, store, provider);
|
|
671
|
+
} else {
|
|
672
|
+
result = await configureGenericCloud(ui, store, provider);
|
|
798
673
|
}
|
|
674
|
+
} catch (err) {
|
|
675
|
+
ui.notify(`Provider configuration failed: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
676
|
+
}
|
|
799
677
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
" - Edit `~/.phi/agent/models.json` or `routing.json` directly: hot-reload kicks in",
|
|
810
|
-
"info",
|
|
811
|
-
);
|
|
678
|
+
if (result) refreshAvailable();
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Save routing if assignments were made
|
|
682
|
+
if (assignments.default) {
|
|
683
|
+
try {
|
|
684
|
+
const routing = buildRoutingConfig(assignments.default, assignments.orchestration);
|
|
685
|
+
const routingPath = await writeRoutingConfig(routing);
|
|
686
|
+
ui.notify(`Routing config written to \`${routingPath}\`.`, "info");
|
|
812
687
|
} catch (err) {
|
|
813
|
-
ui.
|
|
814
|
-
ui.notify(`Setup wizard error: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
688
|
+
ui.notify(`Failed to write routing.json: ${err}`, "error");
|
|
815
689
|
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
ui.setWidget("setup-status", undefined);
|
|
693
|
+
ui.notify(
|
|
694
|
+
"**Setup complete.**\n\n" +
|
|
695
|
+
"Next steps:\n" +
|
|
696
|
+
" - `/keys` to list/manage saved keys\n" +
|
|
697
|
+
" - `/models refresh` to re-fetch the catalog from each provider's API\n" +
|
|
698
|
+
" - `/routing` to inspect routing\n" +
|
|
699
|
+
" - `/agents` to list sub-agents\n" +
|
|
700
|
+
" - `/skills` to list skills\n" +
|
|
701
|
+
" - Edit `~/.phi/agent/models.json` or `routing.json` directly: hot-reload kicks in",
|
|
702
|
+
"info",
|
|
703
|
+
);
|
|
704
|
+
} catch (err) {
|
|
705
|
+
ui.setWidget("setup-status", undefined);
|
|
706
|
+
ui.notify(`Setup wizard error: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
export default function setupExtension(pi: ExtensionAPI) {
|
|
711
|
+
installSetupUnhandledRejectionGuard();
|
|
712
|
+
pi.registerCommand("setup", {
|
|
713
|
+
description: "Phi Code setup wizard (refonte UX, replaces /phi-init)",
|
|
714
|
+
handler: async (_args, ctx) => {
|
|
715
|
+
await runSetupWizard(ctx.ui);
|
|
816
716
|
},
|
|
817
717
|
});
|
|
818
718
|
}
|
|
@@ -65,23 +65,6 @@ export default function smartRouterExtension(pi: ExtensionAPI) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
/**
|
|
69
|
-
* Resolve a model name to an available model.
|
|
70
|
-
* If the preferred model exists in the registry, use it.
|
|
71
|
-
* Otherwise, fall back to the current model.
|
|
72
|
-
*/
|
|
73
|
-
function _resolveModel(preferredModel: string, ctx: any): string {
|
|
74
|
-
try {
|
|
75
|
-
const available = ctx.modelRegistry?.getAvailable?.() || [];
|
|
76
|
-
if (available.some((m: any) => m.id === preferredModel)) {
|
|
77
|
-
return preferredModel;
|
|
78
|
-
}
|
|
79
|
-
return ctx.model?.id || preferredModel;
|
|
80
|
-
} catch {
|
|
81
|
-
return ctx.model?.id || preferredModel;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
68
|
// ─── Input Event ─────────────────────────────────────────────────
|
|
86
69
|
|
|
87
70
|
pi.on("input", async (event, ctx) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phi-code-admin/phi-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.97.1",
|
|
4
4
|
"description": "Coding agent CLI with persistent memory, sub-agents, intelligent routing, and orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -66,9 +66,9 @@
|
|
|
66
66
|
"phi-code-ai": "^0.74.4",
|
|
67
67
|
"phi-code-tui": "^0.74.0",
|
|
68
68
|
"proper-lockfile": "^4.1.2",
|
|
69
|
-
"sigma-agents": "^0.1
|
|
69
|
+
"sigma-agents": "^0.2.1",
|
|
70
70
|
"sigma-memory": "^0.2.9",
|
|
71
|
-
"sigma-skills": "^0.1.
|
|
71
|
+
"sigma-skills": "^0.1.5",
|
|
72
72
|
"strip-ansi": "^7.1.0",
|
|
73
73
|
"typebox": "^1.1.24",
|
|
74
74
|
"undici": "^7.19.1",
|