@phi-code-admin/phi-code 0.66.0 → 0.66.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.
@@ -464,76 +464,107 @@ _Edit this file to customize Phi Code's behavior for your project._
464
464
 
465
465
  // No warning needed — the wizard itself handles configuration
466
466
 
467
- // Always offer to add a provider via select menu
468
- const providerOptions = [
469
- "Skip (continue with current providers)",
470
- ...providers.map(p => {
471
- const status = p.available ? "✅" : "⬜";
472
- const tag = p.local ? " (local)" : "";
473
- return `${status} ${p.name}${tag}`;
474
- }),
475
- ];
476
- const addProvider = await ctx.ui.select("Add or change a provider?", providerOptions);
477
-
478
- const choiceIdx = providerOptions.indexOf(addProvider ?? "");
479
- if (choiceIdx > 0) { // 0 = Skip
467
+ // Provider configuration loop add as many providers as needed
468
+ let addingProviders = true;
469
+ while (addingProviders) {
470
+ const providerOptions = [
471
+ "Done continue with current providers",
472
+ ...providers.map(p => {
473
+ const status = p.available ? "✅" : "⬜";
474
+ const tag = p.local ? " (local)" : "";
475
+ const modelCount = p.available ? ` (${p.models.length} models)` : "";
476
+ return `${status} ${p.name}${tag}${modelCount}`;
477
+ }),
478
+ ];
479
+ const addProvider = await ctx.ui.select("Configure a provider (add multiple!):", providerOptions);
480
+
481
+ const choiceIdx = providerOptions.indexOf(addProvider ?? "");
482
+ if (choiceIdx <= 0) { // 0 = Done, or cancelled
483
+ addingProviders = false;
484
+ break;
485
+ }
486
+
480
487
  const chosen = providers[choiceIdx - 1];
481
488
 
482
489
  if (chosen.local) {
483
490
  const port = chosen.name === "Ollama" ? 11434 : 1234;
484
491
  if (!chosen.available) {
485
492
  ctx.ui.notify(`\n💡 **${chosen.name}** — make sure it's running on port ${port}.`, "info");
486
- ctx.ui.notify("Then restart phi and run `/phi-init` again.", "info");
487
- return;
493
+ ctx.ui.notify("Then restart phi and run `/phi-init` again.\n", "info");
494
+ } else {
495
+ ctx.ui.notify(`\n✅ **${chosen.name}** is running with ${chosen.models.length} model(s).\n`, "info");
488
496
  }
489
- ctx.ui.notify(`\n✅ **${chosen.name}** is running with ${chosen.models.length} model(s).`, "info");
490
497
  } else {
491
- // Cloud provider — ask for API key
492
- ctx.ui.notify(`\n🔑 **${chosen.name}**`, "info");
493
-
494
- const apiKey = await ctx.ui.input(
495
- `Enter your ${chosen.name} API key`,
496
- "Paste your key here"
498
+ // Cloud provider — choose auth method
499
+ const supportsOAuth = ["openai", "anthropic", "google"].includes(
500
+ chosen.name.toLowerCase().split(" ")[0]
497
501
  );
498
502
 
499
- if (!apiKey || apiKey.trim().length < 5) {
500
- ctx.ui.notify("❌ Invalid API key. Skipped.", "error");
501
- } else {
502
- // Save to models.json
503
- let modelsConfig: any = { providers: {} };
504
- try {
505
- const existing = await readFile(modelsJsonPath, "utf-8");
506
- modelsConfig = JSON.parse(existing);
507
- } catch { /* new file */ }
508
-
509
- const providerId = chosen.name.toLowerCase().replace(/\s+/g, "-");
510
- modelsConfig.providers[providerId] = {
511
- baseUrl: chosen.baseUrl,
512
- api: "openai-completions",
513
- apiKey: apiKey.trim(),
514
- models: await Promise.all(chosen.models.map(async (id: string) => {
515
- const spec = await getModelSpec(id);
516
- return {
517
- id,
518
- name: id,
519
- reasoning: spec.reasoning,
520
- input: ["text"],
521
- contextWindow: spec.contextWindow,
522
- maxTokens: spec.maxTokens,
523
- };
524
- })),
525
- };
526
-
527
- await writeFile(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
528
- process.env[chosen.envVar] = apiKey.trim();
529
- chosen.available = true;
503
+ let authMethod = "api-key";
504
+ if (supportsOAuth) {
505
+ const authChoice = await ctx.ui.select(
506
+ `How to authenticate with ${chosen.name}?`,
507
+ ["API Key (paste your key)", "OAuth (browser login via /login)"]
508
+ );
509
+ if (authChoice?.includes("OAuth")) {
510
+ authMethod = "oauth";
511
+ }
512
+ }
530
513
 
531
- const masked = apiKey.trim().substring(0, 6) + "..." + apiKey.trim().slice(-4);
532
- ctx.ui.notify(`✅ **${chosen.name}** saved (${masked})`, "info");
533
- ctx.ui.notify(` ${chosen.models.length} models configured in \`models.json\``, "info");
514
+ if (authMethod === "oauth") {
515
+ ctx.ui.notify(`\n🔐 **${chosen.name}** Use \`/login\` after setup to authenticate via OAuth.`, "info");
516
+ ctx.ui.notify("OAuth opens a browser window for secure login.\n", "info");
517
+ // Mark as available for model assignment (auth will be done via /login)
518
+ chosen.available = true;
519
+ } else {
520
+ // API Key method
521
+ ctx.ui.notify(`\n🔑 **${chosen.name}**`, "info");
522
+
523
+ const apiKey = await ctx.ui.input(
524
+ `Enter your ${chosen.name} API key`,
525
+ "Paste your key here"
526
+ );
527
+
528
+ if (!apiKey || apiKey.trim().length < 5) {
529
+ ctx.ui.notify("❌ Invalid API key. Skipped.\n", "error");
530
+ } else {
531
+ // Save to models.json (merges with existing)
532
+ let modelsConfig: any = { providers: {} };
533
+ try {
534
+ const existing = await readFile(modelsJsonPath, "utf-8");
535
+ modelsConfig = JSON.parse(existing);
536
+ if (!modelsConfig.providers) modelsConfig.providers = {};
537
+ } catch { /* new file */ }
538
+
539
+ const providerId = chosen.name.toLowerCase().replace(/\s+/g, "-");
540
+ modelsConfig.providers[providerId] = {
541
+ baseUrl: chosen.baseUrl,
542
+ api: "openai-completions",
543
+ apiKey: apiKey.trim(),
544
+ models: await Promise.all(chosen.models.map(async (id: string) => {
545
+ const spec = await getModelSpec(id);
546
+ return {
547
+ id,
548
+ name: id,
549
+ reasoning: spec.reasoning,
550
+ input: ["text"],
551
+ contextWindow: spec.contextWindow,
552
+ maxTokens: spec.maxTokens,
553
+ };
554
+ })),
555
+ };
556
+
557
+ await writeFile(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
558
+ process.env[chosen.envVar] = apiKey.trim();
559
+ chosen.available = true;
560
+
561
+ const masked = apiKey.trim().substring(0, 6) + "..." + apiKey.trim().slice(-4);
562
+ ctx.ui.notify(`✅ **${chosen.name}** configured (${masked})`, "info");
563
+ ctx.ui.notify(` ${chosen.models.length} models added to \`models.json\`\n`, "info");
564
+ }
534
565
  }
535
566
  }
536
- }
567
+ } // end while (addingProviders)
537
568
 
538
569
  // Re-check available after potential additions
539
570
  available = providers.filter(p => p.available);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/phi-code",
3
- "version": "0.66.0",
3
+ "version": "0.66.1",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {