@phi-code-admin/phi-code 0.59.9 → 0.60.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/extensions/phi/init.ts +98 -75
- package/package.json +1 -1
- package/scripts/postinstall.cjs +10 -0
package/extensions/phi/init.ts
CHANGED
|
@@ -608,104 +608,127 @@ _Edit this file to customize Phi Code's behavior for your project._
|
|
|
608
608
|
ctx.ui.notify("║ Φ Phi Code Setup Wizard ║", "info");
|
|
609
609
|
ctx.ui.notify("╚══════════════════════════════════════╝\n", "info");
|
|
610
610
|
|
|
611
|
-
// 1. Detect
|
|
612
|
-
ctx.ui.notify("🔍 Detecting providers
|
|
611
|
+
// 1. Detect providers
|
|
612
|
+
ctx.ui.notify("🔍 Detecting providers...\n", "info");
|
|
613
613
|
const providers = detectProviders();
|
|
614
614
|
|
|
615
|
+
// Also check models.json for previously configured providers
|
|
616
|
+
const modelsJsonPath = join(agentDir, "models.json");
|
|
617
|
+
try {
|
|
618
|
+
const mjContent = await readFile(modelsJsonPath, "utf-8");
|
|
619
|
+
const mjConfig = JSON.parse(mjContent);
|
|
620
|
+
if (mjConfig.providers) {
|
|
621
|
+
for (const [id, config] of Object.entries<any>(mjConfig.providers)) {
|
|
622
|
+
// Mark provider as available if it has an API key in models.json
|
|
623
|
+
if (config.apiKey) {
|
|
624
|
+
const match = providers.find(p =>
|
|
625
|
+
id.includes(p.name.toLowerCase().split(" ")[0]) ||
|
|
626
|
+
p.name.toLowerCase().replace(/\s+/g, "-") === id
|
|
627
|
+
);
|
|
628
|
+
if (match) {
|
|
629
|
+
match.available = true;
|
|
630
|
+
if (config.models?.length > 0) {
|
|
631
|
+
match.models = config.models.map((m: any) => m.id || m);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
} catch { /* no models.json yet */ }
|
|
638
|
+
|
|
615
639
|
// Probe local providers (Ollama, LM Studio)
|
|
616
|
-
ctx.ui.notify("🔍 Probing local model servers...", "info");
|
|
617
640
|
await detectLocalProviders(providers);
|
|
618
641
|
|
|
619
642
|
let available = providers.filter(p => p.available);
|
|
643
|
+
const cloudConfigured = available.filter(p => !p.local);
|
|
620
644
|
|
|
621
|
-
//
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
645
|
+
// Always show provider status and offer to add/change
|
|
646
|
+
ctx.ui.notify("**Provider Status:**", "info");
|
|
647
|
+
for (const p of providers) {
|
|
648
|
+
const status = p.available ? "✅" : "⬜";
|
|
649
|
+
const tag = p.local ? " (local)" : "";
|
|
650
|
+
const modelCount = p.available ? ` — ${p.models.length} model(s)` : "";
|
|
651
|
+
ctx.ui.notify(` ${status} ${p.name}${tag}${modelCount}`, "info");
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// If no cloud providers have keys, prompt for setup
|
|
655
|
+
if (cloudConfigured.length === 0) {
|
|
656
|
+
ctx.ui.notify("\n⚠️ No cloud API keys configured.\n", "warning");
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Always offer to add a provider
|
|
660
|
+
const addProvider = await ctx.ui.input(
|
|
661
|
+
"Add/change a provider? (number, or Enter to skip)",
|
|
662
|
+
providers.map((p, i) => `${i+1}=${p.name}`).join(", ")
|
|
663
|
+
);
|
|
631
664
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
);
|
|
636
|
-
const choiceNum = parseInt(providerChoice ?? "0");
|
|
665
|
+
const choiceNum = parseInt(addProvider ?? "0");
|
|
666
|
+
if (choiceNum >= 1 && choiceNum <= providers.length) {
|
|
667
|
+
const chosen = providers[choiceNum - 1];
|
|
637
668
|
|
|
638
|
-
if (
|
|
669
|
+
if (chosen.local) {
|
|
670
|
+
const port = chosen.name === "Ollama" ? 11434 : 1234;
|
|
671
|
+
if (!chosen.available) {
|
|
672
|
+
ctx.ui.notify(`\n💡 **${chosen.name}** — make sure it's running on port ${port}.`, "info");
|
|
673
|
+
ctx.ui.notify("Then restart phi and run `/phi-init` again.", "info");
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
ctx.ui.notify(`\n✅ **${chosen.name}** is running with ${chosen.models.length} model(s).`, "info");
|
|
677
|
+
} else {
|
|
639
678
|
// Cloud provider — ask for API key
|
|
640
|
-
|
|
641
|
-
ctx.ui.notify(`\n🔑 **${chosen.name}** selected.`, "info");
|
|
679
|
+
ctx.ui.notify(`\n🔑 **${chosen.name}**`, "info");
|
|
642
680
|
|
|
643
681
|
const apiKey = await ctx.ui.input(
|
|
644
682
|
`Enter your ${chosen.name} API key`,
|
|
645
|
-
"
|
|
683
|
+
"Paste your key here"
|
|
646
684
|
);
|
|
647
685
|
|
|
648
686
|
if (!apiKey || apiKey.trim().length < 5) {
|
|
649
|
-
ctx.ui.notify("❌ Invalid API key.
|
|
650
|
-
|
|
687
|
+
ctx.ui.notify("❌ Invalid API key. Skipped.", "error");
|
|
688
|
+
} else {
|
|
689
|
+
// Save to models.json
|
|
690
|
+
let modelsConfig: any = { providers: {} };
|
|
691
|
+
try {
|
|
692
|
+
const existing = await readFile(modelsJsonPath, "utf-8");
|
|
693
|
+
modelsConfig = JSON.parse(existing);
|
|
694
|
+
} catch { /* new file */ }
|
|
695
|
+
|
|
696
|
+
const providerId = chosen.name.toLowerCase().replace(/\s+/g, "-");
|
|
697
|
+
modelsConfig.providers[providerId] = {
|
|
698
|
+
baseUrl: chosen.baseUrl,
|
|
699
|
+
api: "openai-completions",
|
|
700
|
+
apiKey: apiKey.trim(),
|
|
701
|
+
models: chosen.models.map((id: string) => ({
|
|
702
|
+
id,
|
|
703
|
+
name: id,
|
|
704
|
+
reasoning: true,
|
|
705
|
+
input: ["text"],
|
|
706
|
+
contextWindow: 131072,
|
|
707
|
+
maxTokens: 16384,
|
|
708
|
+
})),
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
await writeFile(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
|
|
712
|
+
process.env[chosen.envVar] = apiKey.trim();
|
|
713
|
+
chosen.available = true;
|
|
714
|
+
|
|
715
|
+
const masked = apiKey.trim().substring(0, 6) + "..." + apiKey.trim().slice(-4);
|
|
716
|
+
ctx.ui.notify(`✅ **${chosen.name}** saved (${masked})`, "info");
|
|
717
|
+
ctx.ui.notify(` ${chosen.models.length} models configured in \`models.json\``, "info");
|
|
651
718
|
}
|
|
652
|
-
|
|
653
|
-
// Save to models.json for persistence
|
|
654
|
-
const modelsJsonPath = join(agentDir, "models.json");
|
|
655
|
-
let modelsConfig: any = { providers: {} };
|
|
656
|
-
try {
|
|
657
|
-
const existing = await readFile(modelsJsonPath, "utf-8");
|
|
658
|
-
modelsConfig = JSON.parse(existing);
|
|
659
|
-
} catch { /* file doesn't exist yet */ }
|
|
660
|
-
|
|
661
|
-
// Build provider config with correct provider ID
|
|
662
|
-
const providerId = chosen.name.toLowerCase().replace(/\s+/g, "-");
|
|
663
|
-
modelsConfig.providers[providerId] = {
|
|
664
|
-
baseUrl: chosen.baseUrl,
|
|
665
|
-
api: "openai-completions",
|
|
666
|
-
apiKey: apiKey.trim(),
|
|
667
|
-
models: chosen.models.map((id: string) => ({
|
|
668
|
-
id,
|
|
669
|
-
name: id,
|
|
670
|
-
reasoning: true,
|
|
671
|
-
input: ["text"],
|
|
672
|
-
contextWindow: 131072,
|
|
673
|
-
maxTokens: 16384,
|
|
674
|
-
})),
|
|
675
|
-
};
|
|
676
|
-
|
|
677
|
-
await writeFile(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
|
|
678
|
-
|
|
679
|
-
// Also set in current session
|
|
680
|
-
process.env[chosen.envVar] = apiKey.trim();
|
|
681
|
-
chosen.available = true;
|
|
682
|
-
|
|
683
|
-
ctx.ui.notify(`\n✅ API key saved to \`~/.phi/agent/models.json\``, "info");
|
|
684
|
-
ctx.ui.notify(` ${chosen.models.length} models configured.`, "info");
|
|
685
|
-
ctx.ui.notify(` ⚠️ **Restart phi** for models to load.\n`, "warning");
|
|
686
|
-
ctx.ui.notify("Run `phi` again, then `/phi-init` to complete setup.", "info");
|
|
687
|
-
return;
|
|
688
|
-
|
|
689
|
-
} else if (choiceNum === cloudProviders.length + 1 || choiceNum === cloudProviders.length + 2) {
|
|
690
|
-
const localName = choiceNum === cloudProviders.length + 1 ? "Ollama" : "LM Studio";
|
|
691
|
-
const port = choiceNum === cloudProviders.length + 1 ? 11434 : 1234;
|
|
692
|
-
ctx.ui.notify(`\n💡 **${localName}** selected. Make sure it's running on port ${port}.`, "info");
|
|
693
|
-
ctx.ui.notify(`Then restart phi and run /phi-init again.`, "info");
|
|
694
|
-
return;
|
|
695
|
-
} else {
|
|
696
|
-
ctx.ui.notify("❌ Invalid choice. Cancelled.", "error");
|
|
697
|
-
return;
|
|
698
719
|
}
|
|
699
720
|
}
|
|
700
721
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
722
|
+
// Re-check available after potential additions
|
|
723
|
+
available = providers.filter(p => p.available);
|
|
724
|
+
|
|
725
|
+
if (available.length === 0) {
|
|
726
|
+
ctx.ui.notify("\n❌ No providers available. Run `/phi-init` again after setting up a provider.", "error");
|
|
727
|
+
return;
|
|
705
728
|
}
|
|
706
729
|
|
|
707
730
|
const allModels = getAllAvailableModels(providers);
|
|
708
|
-
ctx.ui.notify(
|
|
731
|
+
ctx.ui.notify(`\n✅ **${allModels.length} models** available from ${available.length} provider(s).\n`, "info");
|
|
709
732
|
|
|
710
733
|
// 2. Choose mode
|
|
711
734
|
ctx.ui.notify("Choose setup mode:\n" +
|
package/package.json
CHANGED
package/scripts/postinstall.cjs
CHANGED
|
@@ -58,6 +58,16 @@ for (const pkg of sigmaPackages) {
|
|
|
58
58
|
createLink(srcPkg, destLink, pkg);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// 3. Create default settings.json with quietStartup if it doesn't exist
|
|
62
|
+
const settingsPath = join(agentDir, "settings.json");
|
|
63
|
+
if (!existsSync(settingsPath)) {
|
|
64
|
+
try {
|
|
65
|
+
const defaults = { quietStartup: true };
|
|
66
|
+
require("fs").writeFileSync(settingsPath, JSON.stringify(defaults, null, 2), "utf-8");
|
|
67
|
+
console.log(` Φ Created settings.json (quietStartup: true)`);
|
|
68
|
+
} catch { /* skip */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
61
71
|
function createLink(src, dest, name) {
|
|
62
72
|
try {
|
|
63
73
|
// Remove existing (symlink or directory)
|