dexto 1.8.0 → 1.8.2

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.
Files changed (33) hide show
  1. package/dist/cli/commands/connect.d.ts +9 -0
  2. package/dist/cli/commands/connect.d.ts.map +1 -0
  3. package/dist/cli/commands/connect.js +244 -0
  4. package/dist/cli/commands/index.d.ts +1 -0
  5. package/dist/cli/commands/index.d.ts.map +1 -1
  6. package/dist/cli/commands/index.js +1 -0
  7. package/dist/cli/commands/init-app.d.ts +1 -1
  8. package/dist/cli/commands/init-app.d.ts.map +1 -1
  9. package/dist/cli/commands/init-app.js +1 -1
  10. package/dist/cli/commands/setup.d.ts.map +1 -1
  11. package/dist/cli/commands/setup.js +31 -394
  12. package/dist/cli/utils/api-key-setup.d.ts +1 -1
  13. package/dist/cli/utils/api-key-setup.d.ts.map +1 -1
  14. package/dist/cli/utils/api-key-verification.d.ts +1 -1
  15. package/dist/cli/utils/api-key-verification.d.ts.map +1 -1
  16. package/dist/cli/utils/config-validation.d.ts.map +1 -1
  17. package/dist/cli/utils/config-validation.js +3 -1
  18. package/dist/cli/utils/options.js +1 -1
  19. package/dist/cli/utils/project-utils.d.ts +1 -1
  20. package/dist/cli/utils/project-utils.d.ts.map +1 -1
  21. package/dist/cli/utils/project-utils.js +2 -1
  22. package/dist/cli/utils/provider-setup.d.ts +1 -1
  23. package/dist/cli/utils/provider-setup.d.ts.map +1 -1
  24. package/dist/cli/utils/provider-setup.js +1 -1
  25. package/dist/config/cli-overrides.d.ts +2 -1
  26. package/dist/config/cli-overrides.d.ts.map +1 -1
  27. package/dist/config/cli-overrides.js +2 -1
  28. package/dist/config/effective-llm.d.ts +1 -1
  29. package/dist/config/effective-llm.d.ts.map +1 -1
  30. package/dist/index-main.js +27 -2
  31. package/dist/webui/assets/{index-M9d4U7k5.js → index-CpolTCmd.js} +173 -173
  32. package/dist/webui/index.html +1 -1
  33. package/package.json +13 -12
@@ -1,15 +1,12 @@
1
1
  // packages/cli/src/cli/commands/setup.ts
2
- import { promises as fs } from 'node:fs';
3
- import path from 'node:path';
4
2
  import chalk from 'chalk';
5
3
  import { z } from 'zod';
6
- import open from 'open';
7
- import { acceptsAnyModel, CodexAppServerClient, createCodexBaseURL, getDefaultModelForProvider, getCodexAuthModeLabel, getCodexProviderDisplayName, getCuratedModelsForProvider, getReasoningProfile, getSupportedModels, isCodexBaseURL, LLM_PROVIDERS, LLM_REGISTRY, logger, parseCodexBaseURL, isValidProviderModel, supportsCustomModels, requiresApiKey, resolveApiKeyForProvider, } from '@dexto/core';
8
- import { createInitialPreferences, saveGlobalPreferences, loadGlobalPreferences, getGlobalPreferencesPath, updateGlobalPreferences, setActiveModel, isDextoAuthEnabled, loadCustomModels, saveCustomModel, deleteCustomModel, globalPreferencesExist, getDextoGlobalPath, } from '@dexto/agent-management';
4
+ import { acceptsAnyModel, getDefaultModelForProvider, getReasoningProfile, getSupportedModels, isValidProviderModel, LLM_PROVIDERS, LLM_REGISTRY, requiresApiKey, supportsCustomModels, } from '@dexto/llm';
5
+ import { getCuratedModelsForProvider, logger, resolveApiKeyForProvider } from '@dexto/core';
6
+ import { createInitialPreferences, saveGlobalPreferences, loadGlobalPreferences, getGlobalPreferencesPath, updateGlobalPreferences, setActiveModel, isDextoAuthEnabled, loadCustomModels, saveCustomModel, deleteCustomModel, globalPreferencesExist, getDefaultModelAuthProfile, loadModelAuthProfilesSync, } from '@dexto/agent-management';
9
7
  import { interactiveApiKeySetup, hasApiKeyConfigured } from '../utils/api-key-setup.js';
10
8
  import { selectProvider, getProviderDisplayName, getProviderEnvVar, providerRequiresBaseURL, getDefaultModel, } from '../utils/provider-setup.js';
11
9
  import { setupLocalModels, setupOllamaModels, hasSelectedModel, getModelFromResult, } from '../utils/local-model-setup.js';
12
- import { executeCommand } from '../utils/self-management.js';
13
10
  import { requiresSetup } from '../utils/setup-utils.js';
14
11
  import { canUseDextoProvider } from '../utils/dexto-setup.js';
15
12
  import { handleAutoLogin } from './auth/login.js';
@@ -76,13 +73,6 @@ const REASONING_VARIANT_HINTS = {
76
73
  max: 'Maximum reasoning within provider limits',
77
74
  xhigh: 'Extra high reasoning',
78
75
  };
79
- const OPENAI_CODEX_PACKAGE = '@openai/codex';
80
- const DEXTO_DEPS_PACKAGE_JSON = {
81
- name: 'dexto-deps',
82
- version: '1.0.0',
83
- private: true,
84
- description: 'Managed dependencies for Dexto',
85
- };
86
76
  function toReasoningVariantLabel(variant, defaultVariant) {
87
77
  const normalized = variant.toLowerCase();
88
78
  const withKnownCasing = normalized === 'xhigh'
@@ -103,6 +93,16 @@ function getReasoningVariantSelectOptions(variants, defaultVariant) {
103
93
  hint: REASONING_VARIANT_HINTS[variant] ?? 'Model/provider-native reasoning variant',
104
94
  }));
105
95
  }
96
+ function hasUsableModelAuthProfile(provider) {
97
+ const profile = getDefaultModelAuthProfile(loadModelAuthProfilesSync(), provider);
98
+ if (!profile) {
99
+ return false;
100
+ }
101
+ if (profile.credential.type === 'api_key_env') {
102
+ return Boolean(process.env[profile.credential.envVar]?.trim());
103
+ }
104
+ return true;
105
+ }
106
106
  /**
107
107
  * Get the steps to display for the current provider and model.
108
108
  * - Local/Ollama providers skip the API Key step.
@@ -357,7 +357,7 @@ async function handleQuickStart(options = { onCancel: 'exit' }) {
357
357
  const apiKeyVar = getProviderEnvVar(provider);
358
358
  let apiKeySkipped = false;
359
359
  // Check if API key exists
360
- const hasKey = hasApiKeyConfigured(provider);
360
+ const hasKey = hasApiKeyConfigured(provider) || hasUsableModelAuthProfile(provider);
361
361
  if (!hasKey) {
362
362
  const providerName = getProviderDisplayName(provider);
363
363
  p.note(`${providerName} is ${chalk.green('free')} to use!\n\n` +
@@ -410,7 +410,7 @@ async function handleQuickStart(options = { onCancel: 'exit' }) {
410
410
  apiKeyPending: apiKeySkipped,
411
411
  };
412
412
  // Only include apiKeyVar if not skipped
413
- if (!apiKeySkipped) {
413
+ if (!apiKeySkipped && hasApiKeyConfigured(provider)) {
414
414
  preferencesOptions.apiKeyVar = apiKeyVar;
415
415
  }
416
416
  const preferences = createInitialPreferences(preferencesOptions);
@@ -427,316 +427,6 @@ async function handleQuickStart(options = { onCancel: 'exit' }) {
427
427
  return 'completed';
428
428
  }
429
429
  }
430
- function getConfiguredProviderDisplayName(provider, baseURL) {
431
- if (provider === 'openai-compatible') {
432
- const codex = parseCodexBaseURL(baseURL);
433
- if (codex) {
434
- return getCodexProviderDisplayName(codex.authMode);
435
- }
436
- }
437
- return getProviderDisplayName(provider);
438
- }
439
- function isCodexConfigured(provider, baseURL) {
440
- return provider === 'openai-compatible' && isCodexBaseURL(baseURL);
441
- }
442
- async function ensureDextoDepsPackageJson() {
443
- const depsDir = getDextoGlobalPath('deps');
444
- await fs.mkdir(depsDir, { recursive: true });
445
- const packageJsonPath = path.join(depsDir, 'package.json');
446
- try {
447
- await fs.access(packageJsonPath);
448
- }
449
- catch (error) {
450
- if (error.code !== 'ENOENT') {
451
- throw error;
452
- }
453
- await fs.writeFile(packageJsonPath, JSON.stringify(DEXTO_DEPS_PACKAGE_JSON, null, 2), 'utf-8');
454
- }
455
- return depsDir;
456
- }
457
- function isMissingCodexCliError(error) {
458
- if (!(error instanceof Error)) {
459
- return false;
460
- }
461
- const code = error.code;
462
- return (error.message.includes('Codex CLI not found on PATH') ||
463
- error.message.includes('spawn codex ENOENT') ||
464
- (code === 'ENOENT' && error.message.includes('spawn')));
465
- }
466
- function getCodexSetupErrorMessage(error) {
467
- if (isMissingCodexCliError(error)) {
468
- return 'Codex CLI not found on PATH. Install Codex to use ChatGPT Login in Dexto.';
469
- }
470
- return error instanceof Error ? error.message : String(error);
471
- }
472
- async function resolveCodexInstaller() {
473
- const candidates = [
474
- {
475
- command: 'npm',
476
- args: ['install', OPENAI_CODEX_PACKAGE, '--no-audit', '--no-fund'],
477
- label: 'npm',
478
- },
479
- {
480
- command: 'pnpm',
481
- args: ['add', OPENAI_CODEX_PACKAGE],
482
- label: 'pnpm',
483
- },
484
- {
485
- command: 'bun',
486
- args: ['add', OPENAI_CODEX_PACKAGE],
487
- label: 'bun',
488
- },
489
- ];
490
- for (const candidate of candidates) {
491
- const probe = await executeCommand(candidate.command, ['--version']);
492
- if (probe.code === 0) {
493
- return candidate;
494
- }
495
- }
496
- return null;
497
- }
498
- function getCodexInstallerFailureMessage(installer, result) {
499
- const details = `${result.stderr}\n${result.stdout}`
500
- .split(/\r?\n/)
501
- .map((line) => line.trim())
502
- .filter((line) => line.length > 0);
503
- const lastLine = details.at(-1);
504
- return lastLine
505
- ? `Failed to install the OpenAI Codex CLI via ${installer.label}: ${lastLine}`
506
- : `Failed to install the OpenAI Codex CLI via ${installer.label}.`;
507
- }
508
- async function installManagedCodexCli() {
509
- const depsDir = await ensureDextoDepsPackageJson();
510
- const installer = await resolveCodexInstaller();
511
- if (!installer) {
512
- throw new Error('Could not find npm, pnpm, or bun to install the OpenAI Codex CLI automatically.');
513
- }
514
- const result = await executeCommand(installer.command, installer.args, { cwd: depsDir });
515
- if (result.code !== 0) {
516
- throw new Error(getCodexInstallerFailureMessage(installer, result));
517
- }
518
- }
519
- async function createCodexClientForSetup() {
520
- try {
521
- return await CodexAppServerClient.create();
522
- }
523
- catch (error) {
524
- if (!isMissingCodexCliError(error)) {
525
- throw error;
526
- }
527
- const spinner = p.spinner();
528
- spinner.start('Installing OpenAI Codex CLI...');
529
- try {
530
- await installManagedCodexCli();
531
- spinner.stop('OpenAI Codex CLI installed');
532
- }
533
- catch (installError) {
534
- spinner.stop('OpenAI Codex CLI installation failed');
535
- throw installError;
536
- }
537
- return await CodexAppServerClient.create();
538
- }
539
- }
540
- async function ensureCodexChatGPTLogin(client) {
541
- const spinner = p.spinner();
542
- spinner.start('Starting ChatGPT login with Codex...');
543
- const login = await client.startLogin({ type: 'chatgpt' });
544
- if (login.type !== 'chatgpt') {
545
- spinner.stop('ChatGPT login failed');
546
- throw new Error('Codex did not return a ChatGPT login URL');
547
- }
548
- spinner.stop('ChatGPT login ready');
549
- try {
550
- await open(login.authUrl);
551
- p.log.success('Opened your browser for ChatGPT login');
552
- }
553
- catch (error) {
554
- const errorMessage = error instanceof Error ? error.message : String(error);
555
- p.log.warn(`Could not open browser automatically: ${errorMessage}`);
556
- }
557
- p.note(`Finish the ChatGPT login in your browser.\n\n${chalk.dim(login.authUrl)}`, 'ChatGPT Login');
558
- const waitSpinner = p.spinner();
559
- waitSpinner.start('Waiting for ChatGPT login to complete...');
560
- const completed = await client.waitForLoginCompleted(login.loginId, {
561
- timeoutMs: 5 * 60 * 1000,
562
- });
563
- if (!completed.success) {
564
- waitSpinner.stop('ChatGPT login failed');
565
- throw new Error(completed.error ?? 'Codex ChatGPT login failed');
566
- }
567
- waitSpinner.stop('ChatGPT login complete');
568
- return await client.readAccount(true);
569
- }
570
- async function ensureCodexChatGPTSession(client) {
571
- const current = await client.readAccount(false);
572
- if (current.account?.type === 'chatgpt') {
573
- return current;
574
- }
575
- if (current.account) {
576
- const currentLabel = 'OpenAI API key';
577
- const shouldSwitch = await p.confirm({
578
- message: `Codex is currently using ${currentLabel}. Switch to ChatGPT login?`,
579
- initialValue: true,
580
- });
581
- if (p.isCancel(shouldSwitch) || !shouldSwitch) {
582
- return null;
583
- }
584
- await client.logout();
585
- }
586
- return await ensureCodexChatGPTLogin(client);
587
- }
588
- function getCodexModelOptions(models) {
589
- const visibleModels = models.filter((model) => !model.hidden);
590
- const defaultModel = visibleModels.find((model) => model.isDefault) ?? visibleModels[0] ?? null;
591
- if (!defaultModel) {
592
- return [];
593
- }
594
- const secondaryModels = visibleModels
595
- .filter((model) => model.model !== defaultModel.model)
596
- .slice(0, 9);
597
- return [
598
- {
599
- value: defaultModel.model,
600
- label: defaultModel.displayName,
601
- hint: `${defaultModel.description || 'Recommended'} (recommended)`,
602
- },
603
- ...secondaryModels.map((model) => ({
604
- value: model.model,
605
- label: model.displayName,
606
- hint: model.description || model.model,
607
- })),
608
- ];
609
- }
610
- /**
611
- * ChatGPT Login setup flow - authenticate with ChatGPT through Codex, choose a model, save preferences
612
- *
613
- * Config storage:
614
- * - provider: 'openai-compatible'
615
- * - baseURL: special 'codex://chatgpt' URI resolved at runtime to Codex app-server
616
- * - model: model ID returned by Codex model/list
617
- */
618
- async function handleCodexProviderSetup(options = {}) {
619
- const exitOnCancel = options.exitOnCancel ?? true;
620
- const abort = (message, exitCode = 0) => {
621
- p.cancel(message);
622
- if (exitOnCancel) {
623
- process.exit(exitCode);
624
- }
625
- return false;
626
- };
627
- console.log(chalk.cyan('\nChatGPT Login Setup\n'));
628
- let client = null;
629
- try {
630
- client = await createCodexClientForSetup();
631
- const account = await ensureCodexChatGPTSession(client);
632
- if (!account || account.account?.type !== 'chatgpt') {
633
- return abort('Setup cancelled');
634
- }
635
- p.log.success(`Codex authenticated with ChatGPT as ${account.account.email} (${account.account.planType})`);
636
- const models = await client.listModels();
637
- const modelOptions = getCodexModelOptions(models);
638
- if (modelOptions.length === 0) {
639
- p.log.error('Codex did not return any available models.');
640
- return abort('Setup cancelled', 1);
641
- }
642
- const model = await p.select({
643
- message: 'Select a model to start with',
644
- options: modelOptions,
645
- });
646
- if (p.isCancel(model)) {
647
- return abort('Setup cancelled');
648
- }
649
- const selectedModel = model;
650
- const provider = 'openai-compatible';
651
- const baseURL = createCodexBaseURL('chatgpt');
652
- const codexReasoningProfile = getReasoningProfile(provider, selectedModel);
653
- let reasoningPreset;
654
- if (codexReasoningProfile.capable) {
655
- const selectedReasoning = await p.select({
656
- message: 'Select reasoning variant',
657
- options: getReasoningVariantSelectOptions(codexReasoningProfile.supportedVariants, codexReasoningProfile.defaultVariant),
658
- ...(codexReasoningProfile.defaultVariant
659
- ? { initialValue: codexReasoningProfile.defaultVariant }
660
- : {}),
661
- });
662
- if (p.isCancel(selectedReasoning)) {
663
- return abort('Setup cancelled');
664
- }
665
- if (selectedReasoning !== codexReasoningProfile.defaultVariant) {
666
- reasoningPreset = selectedReasoning;
667
- }
668
- }
669
- const defaultMode = await selectDefaultMode();
670
- if (defaultMode === null) {
671
- return abort('Setup cancelled');
672
- }
673
- const preferences = createInitialPreferences({
674
- provider,
675
- model: selectedModel,
676
- baseURL,
677
- defaultMode,
678
- setupCompleted: true,
679
- apiKeyPending: false,
680
- ...(reasoningPreset ? { reasoning: { variant: reasoningPreset } } : {}),
681
- });
682
- await saveGlobalPreferences(preferences);
683
- capture('dexto_setup', {
684
- provider,
685
- model: selectedModel,
686
- setupMode: 'interactive',
687
- setupVariant: 'codex-chatgpt',
688
- defaultMode,
689
- hasBaseURL: true,
690
- });
691
- await showSetupComplete(provider, selectedModel, defaultMode, false, {
692
- providerLabel: getCodexProviderDisplayName('chatgpt'),
693
- authLabel: getCodexAuthModeLabel('chatgpt'),
694
- baseURL,
695
- });
696
- return true;
697
- }
698
- catch (error) {
699
- const errorMessage = getCodexSetupErrorMessage(error);
700
- p.log.error(`ChatGPT Login setup failed: ${errorMessage}`);
701
- return abort('Setup cancelled', 1);
702
- }
703
- finally {
704
- if (client) {
705
- await client.close().catch(() => undefined);
706
- }
707
- }
708
- }
709
- async function handleCodexChatGPTLoginRefresh(options = {}) {
710
- const exitOnCancel = options.exitOnCancel ?? true;
711
- const abort = (message, exitCode = 0) => {
712
- p.cancel(message);
713
- if (exitOnCancel) {
714
- process.exit(exitCode);
715
- }
716
- return false;
717
- };
718
- console.log(chalk.cyan('\nChatGPT Login\n'));
719
- let client = null;
720
- try {
721
- client = await createCodexClientForSetup();
722
- const account = await ensureCodexChatGPTSession(client);
723
- if (!account || account.account?.type !== 'chatgpt') {
724
- return abort('ChatGPT login cancelled');
725
- }
726
- p.log.success(`Codex authenticated with ChatGPT as ${account.account.email} (${account.account.planType})`);
727
- return true;
728
- }
729
- catch (error) {
730
- const errorMessage = getCodexSetupErrorMessage(error);
731
- p.log.error(`ChatGPT Login failed: ${errorMessage}`);
732
- return abort('ChatGPT login cancelled', 1);
733
- }
734
- finally {
735
- if (client) {
736
- await client.close().catch(() => undefined);
737
- }
738
- }
739
- }
740
430
  /**
741
431
  * Dexto setup flow - login if needed, select model, save preferences
742
432
  *
@@ -989,10 +679,6 @@ async function wizardStepSetupType(state) {
989
679
  });
990
680
  }
991
681
  options.push({
992
- value: 'openai-codex',
993
- label: `${chalk.green('●')} ChatGPT Login`,
994
- hint: 'Use your ChatGPT account through Codex',
995
- }, {
996
682
  value: 'quick',
997
683
  label: `${chalk.blue('●')} Quick Start`,
998
684
  hint: 'Google Gemini (free) - no account needed',
@@ -1014,13 +700,6 @@ async function wizardStepSetupType(state) {
1014
700
  await handleDextoProviderSetup();
1015
701
  return { ...state, step: 'complete', quickStartHandled: true };
1016
702
  }
1017
- if (setupType === 'openai-codex') {
1018
- const completed = await handleCodexProviderSetup({ exitOnCancel: false });
1019
- if (!completed) {
1020
- return { ...state, step: 'setupType' };
1021
- }
1022
- return { ...state, step: 'complete', quickStartHandled: true };
1023
- }
1024
703
  if (setupType === 'quick') {
1025
704
  // Quick start bypasses the wizard - handle it directly
1026
705
  const result = await handleQuickStart({
@@ -1139,7 +818,7 @@ async function wizardStepApiKey(state) {
1139
818
  const provider = state.provider;
1140
819
  const model = state.model;
1141
820
  showStepProgress('apiKey', provider, model);
1142
- const hasKey = hasApiKeyConfigured(provider);
821
+ const hasKey = hasApiKeyConfigured(provider) || hasUsableModelAuthProfile(provider);
1143
822
  const needsApiKey = requiresApiKey(provider);
1144
823
  if (needsApiKey && !hasKey) {
1145
824
  const result = await interactiveApiKeySetup(provider, {
@@ -1183,7 +862,9 @@ async function wizardStepMode(state) {
1183
862
  defaultMode: undefined,
1184
863
  };
1185
864
  }
1186
- const canShowApiKeyStep = requiresApiKey(provider) && !hasApiKeyConfigured(provider);
865
+ const canShowApiKeyStep = requiresApiKey(provider) &&
866
+ !hasApiKeyConfigured(provider) &&
867
+ !hasUsableModelAuthProfile(provider);
1187
868
  let prevStep = 'model';
1188
869
  if (canShowApiKeyStep) {
1189
870
  prevStep = 'apiKey';
@@ -1610,7 +1291,7 @@ async function saveWizardPreferences(state) {
1610
1291
  setupCompleted: true,
1611
1292
  apiKeyPending: apiKeySkipped,
1612
1293
  };
1613
- if (needsApiKey && !apiKeySkipped) {
1294
+ if (needsApiKey && !apiKeySkipped && hasApiKeyConfigured(provider)) {
1614
1295
  preferencesOptions.apiKeyVar = apiKeyVar;
1615
1296
  }
1616
1297
  if (state.baseURL) {
@@ -1631,17 +1312,7 @@ async function saveWizardPreferences(state) {
1631
1312
  hasBaseURL: Boolean(state.baseURL),
1632
1313
  apiKeySkipped,
1633
1314
  });
1634
- const codex = parseCodexBaseURL(state.baseURL);
1635
- const codexSetupOptions = codex && typeof state.baseURL === 'string'
1636
- ? {
1637
- providerLabel: getCodexProviderDisplayName(codex.authMode),
1638
- authLabel: getCodexAuthModeLabel(codex.authMode),
1639
- baseURL: state.baseURL,
1640
- }
1641
- : {};
1642
- await showSetupComplete(provider, model, defaultMode, apiKeySkipped, {
1643
- ...codexSetupOptions,
1644
- });
1315
+ await showSetupComplete(provider, model, defaultMode, apiKeySkipped);
1645
1316
  }
1646
1317
  /**
1647
1318
  * Non-interactive setup with CLI options
@@ -1708,14 +1379,10 @@ async function showSettingsMenu() {
1708
1379
  }
1709
1380
  // Show current configuration
1710
1381
  if (currentPrefs) {
1711
- const codex = parseCodexBaseURL(currentPrefs.llm.baseURL);
1712
1382
  const currentConfig = [
1713
- `Provider: ${chalk.cyan(getConfiguredProviderDisplayName(currentPrefs.llm.provider, currentPrefs.llm.baseURL))}`,
1383
+ `Provider: ${chalk.cyan(getProviderDisplayName(currentPrefs.llm.provider))}`,
1714
1384
  `Model: ${chalk.cyan(currentPrefs.llm.model)}`,
1715
1385
  `Default Mode: ${chalk.cyan(currentPrefs.defaults.defaultMode)}`,
1716
- ...(codex
1717
- ? [`Authentication: ${chalk.cyan(getCodexAuthModeLabel(codex.authMode))}`]
1718
- : []),
1719
1386
  ...(currentPrefs.llm.baseURL
1720
1387
  ? [`Base URL: ${chalk.cyan(currentPrefs.llm.baseURL)}`]
1721
1388
  : []),
@@ -1731,14 +1398,9 @@ async function showSettingsMenu() {
1731
1398
  p.note(currentConfig, 'Current Configuration');
1732
1399
  }
1733
1400
  const currentProviderLabel = currentPrefs
1734
- ? getConfiguredProviderDisplayName(currentPrefs.llm.provider, currentPrefs.llm.baseURL)
1401
+ ? getProviderDisplayName(currentPrefs.llm.provider)
1735
1402
  : 'not set';
1736
1403
  const currentModelLabel = currentPrefs?.llm.model || 'not set';
1737
- const currentCodex = currentPrefs ? parseCodexBaseURL(currentPrefs.llm.baseURL) : null;
1738
- const authActionLabel = currentCodex ? 'Manage ChatGPT login' : 'Update API key';
1739
- const authActionHint = currentCodex
1740
- ? 'Verify or reconnect your ChatGPT login for Codex'
1741
- : 'Re-enter your API key';
1742
1404
  const options = [
1743
1405
  {
1744
1406
  value: 'model',
@@ -1752,8 +1414,8 @@ async function showSettingsMenu() {
1752
1414
  },
1753
1415
  {
1754
1416
  value: 'auth',
1755
- label: authActionLabel,
1756
- hint: authActionHint,
1417
+ label: 'Update API key',
1418
+ hint: 'Re-enter your API key',
1757
1419
  },
1758
1420
  {
1759
1421
  value: 'reset',
@@ -1799,7 +1461,7 @@ async function showSettingsMenu() {
1799
1461
  await openCreditsPage();
1800
1462
  break;
1801
1463
  case 'auth':
1802
- await updateApiKey(currentPrefs?.llm.provider, currentPrefs?.llm.baseURL);
1464
+ await updateApiKey(currentPrefs?.llm.provider);
1803
1465
  break;
1804
1466
  case 'reset': {
1805
1467
  // Reset exits the menu after completion, but returns to menu if cancelled
@@ -1820,15 +1482,8 @@ async function showSettingsMenu() {
1820
1482
  /**
1821
1483
  * Change model setting (includes provider selection)
1822
1484
  */
1823
- async function changeModel(currentProvider, currentBaseURL) {
1485
+ async function changeModel(currentProvider) {
1824
1486
  let provider = currentProvider ?? null;
1825
- if (isCodexConfigured(provider ?? undefined, currentBaseURL)) {
1826
- const completed = await handleCodexProviderSetup({ exitOnCancel: false });
1827
- if (!completed) {
1828
- p.log.warn('Model change cancelled');
1829
- }
1830
- return;
1831
- }
1832
1487
  // If no provider specified, show selection
1833
1488
  if (!provider) {
1834
1489
  const sourceOptions = [];
@@ -1840,10 +1495,6 @@ async function changeModel(currentProvider, currentBaseURL) {
1840
1495
  });
1841
1496
  }
1842
1497
  sourceOptions.push({
1843
- value: 'openai-codex',
1844
- label: `${chalk.green('●')} ChatGPT Login`,
1845
- hint: 'Use your ChatGPT account through Codex',
1846
- }, {
1847
1498
  value: 'other',
1848
1499
  label: `${chalk.blue('●')} Other providers`,
1849
1500
  hint: 'OpenAI, Anthropic, Gemini, Ollama, etc.',
@@ -1864,13 +1515,6 @@ async function changeModel(currentProvider, currentBaseURL) {
1864
1515
  }
1865
1516
  return;
1866
1517
  }
1867
- if (providerChoice === 'openai-codex') {
1868
- const completed = await handleCodexProviderSetup({ exitOnCancel: false });
1869
- if (!completed) {
1870
- return;
1871
- }
1872
- return;
1873
- }
1874
1518
  // 'other' - fall through to normal provider selection
1875
1519
  }
1876
1520
  // Get provider if not already set
@@ -1949,7 +1593,7 @@ async function changeModel(currentProvider, currentBaseURL) {
1949
1593
  }
1950
1594
  const apiKeyVar = getProviderEnvVar(provider);
1951
1595
  const needsApiKey = requiresApiKey(provider);
1952
- const hasKey = hasApiKeyConfigured(provider);
1596
+ const hasKey = hasApiKeyConfigured(provider) || hasUsableModelAuthProfile(provider);
1953
1597
  // Check if API key is needed and missing - prompt for it
1954
1598
  if (needsApiKey && !hasKey) {
1955
1599
  const result = await interactiveApiKeySetup(provider, {
@@ -1970,7 +1614,7 @@ async function changeModel(currentProvider, currentBaseURL) {
1970
1614
  model,
1971
1615
  };
1972
1616
  // Only include apiKey for providers that need it
1973
- if (needsApiKey) {
1617
+ if (needsApiKey && hasApiKeyConfigured(provider)) {
1974
1618
  llmUpdate.apiKey = `$${apiKeyVar}`;
1975
1619
  }
1976
1620
  // Ask for reasoning variant if applicable
@@ -2006,20 +1650,13 @@ async function changeDefaultMode() {
2006
1650
  /**
2007
1651
  * Update authentication for current provider
2008
1652
  */
2009
- async function updateApiKey(currentProvider, currentBaseURL) {
1653
+ async function updateApiKey(currentProvider) {
2010
1654
  const provider = currentProvider || (await selectProvider());
2011
1655
  // Handle cancellation or back from selectProvider
2012
1656
  if (provider === null || provider === '_back') {
2013
1657
  p.log.warn('API key update cancelled');
2014
1658
  return;
2015
1659
  }
2016
- if (isCodexConfigured(provider, currentBaseURL)) {
2017
- const completed = await handleCodexChatGPTLoginRefresh({ exitOnCancel: false });
2018
- if (!completed) {
2019
- p.log.warn('ChatGPT login update cancelled');
2020
- }
2021
- return;
2022
- }
2023
1660
  // Handle providers that use non-API-key authentication
2024
1661
  if (provider === 'vertex') {
2025
1662
  p.note(`Google Vertex AI uses Application Default Credentials (ADC).\n\n` +
@@ -2304,7 +1941,7 @@ async function promptForBaseURL(provider) {
2304
1941
  async function showSetupComplete(provider, model, defaultMode, apiKeySkipped = false, options = {}) {
2305
1942
  const modeCommand = defaultMode === 'web' ? 'dexto' : `dexto --mode ${defaultMode}`;
2306
1943
  const isLocalProvider = provider === 'local' || provider === 'ollama';
2307
- const providerLabel = options.providerLabel ?? getConfiguredProviderDisplayName(provider, options.baseURL);
1944
+ const providerLabel = options.providerLabel ?? getProviderDisplayName(provider);
2308
1945
  if (apiKeySkipped) {
2309
1946
  console.log(chalk.rgb(255, 165, 0)('\n⚠️ Setup complete (API key pending)\n'));
2310
1947
  }
@@ -1,4 +1,4 @@
1
- import type { LLMProvider } from '@dexto/core';
1
+ import type { LLMProvider } from '@dexto/llm';
2
2
  export interface ApiKeySetupResult {
3
3
  success: boolean;
4
4
  apiKey?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"api-key-setup.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api-key-setup.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAc/C,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CACxC,QAAQ,EAAE,WAAW,EACrB,OAAO,GAAE;IACL,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CACb,GACP,OAAO,CAAC,iBAAiB,CAAC,CAmK5B;AA8GD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAGlE;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CACxC,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,CAAC,CAkEnC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,2BAA2B,CAC7C,aAAa,EAAE,WAAW,EAC1B,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,WAAW,EACzB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,uBAAuB,CAAC,CAiGlC"}
1
+ {"version":3,"file":"api-key-setup.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api-key-setup.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAc9C,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CACxC,QAAQ,EAAE,WAAW,EACrB,OAAO,GAAE;IACL,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CACb,GACP,OAAO,CAAC,iBAAiB,CAAC,CAmK5B;AA8GD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAGlE;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CACxC,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,CAAC,CAkEnC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,2BAA2B,CAC7C,aAAa,EAAE,WAAW,EAC1B,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,WAAW,EACzB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,uBAAuB,CAAC,CAiGlC"}
@@ -1,4 +1,4 @@
1
- import type { LLMProvider } from '@dexto/core';
1
+ import type { LLMProvider } from '@dexto/llm';
2
2
  export interface VerificationResult {
3
3
  success: boolean;
4
4
  error?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"api-key-verification.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api-key-verification.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CA0C7B"}
1
+ {"version":3,"file":"api-key-verification.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api-key-verification.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CA0C7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-validation.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/config-validation.ts"],"names":[],"mappings":"AAGA,OAAO,EAEH,KAAK,WAAW,EAChB,KAAK,oBAAoB,EAC5B,MAAM,qBAAqB,CAAC;AAc7B,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;CAClD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACrC,MAAM,EAAE,WAAW,EACnB,WAAW,GAAE,OAAe,EAC5B,OAAO,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAsC3B;AAuMD;;GAEG"}
1
+ {"version":3,"file":"config-validation.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/config-validation.ts"],"names":[],"mappings":"AAGA,OAAO,EAEH,KAAK,WAAW,EAChB,KAAK,oBAAoB,EAC5B,MAAM,qBAAqB,CAAC;AAU7B,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;CAClD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACrC,MAAM,EAAE,WAAW,EACnB,WAAW,GAAE,OAAe,EAC5B,OAAO,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAsC3B;AAuMD;;GAEG"}
@@ -1,7 +1,9 @@
1
1
  import chalk from 'chalk';
2
2
  import * as p from '@clack/prompts';
3
3
  import { AgentConfigSchema, } from '@dexto/agent-config';
4
- import { getPrimaryApiKeyEnvVar, logger, requiresApiKey, requiresBaseURL, resolveApiKeyForProvider, } from '@dexto/core';
4
+ import { getPrimaryApiKeyEnvVar, resolveApiKeyForProvider } from '@dexto/agent-management';
5
+ import { requiresApiKey, requiresBaseURL } from '@dexto/llm';
6
+ import { logger } from '@dexto/core';
5
7
  import { getGlobalPreferencesPath } from '@dexto/agent-management';
6
8
  import { getBundledSyncTargetForAgentPath, handleSyncAgentsCommand, } from '../commands/agents/sync.js';
7
9
  /**
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { getSupportedProviders } from '@dexto/core';
2
+ import { getSupportedProviders } from '@dexto/llm';
3
3
  import { isDextoAuthEnabled } from '@dexto/agent-management';
4
4
  import chalk from 'chalk';
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { type LLMProvider } from '@dexto/core';
1
+ import { type LLMProvider } from '@dexto/llm';
2
2
  /**
3
3
  * Updates the LLM provider information in a dexto config file
4
4
  * Used for project creation/initialization (modifies agent yml files)
@@ -1 +1 @@
1
- {"version":3,"file":"project-utils.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/project-utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAsD,MAAM,aAAa,CAAC;AAEnG;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACvC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,GACzB,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"project-utils.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/project-utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAA8B,MAAM,YAAY,CAAC;AAG1E;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACvC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,GACzB,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -1,7 +1,8 @@
1
1
  // packages/cli/src/cli/utils/project-utils.ts
2
2
  import fs from 'node:fs/promises';
3
3
  import { parseDocument } from 'yaml';
4
- import { getDefaultModelForProvider, getPrimaryApiKeyEnvVar } from '@dexto/core';
4
+ import { getDefaultModelForProvider } from '@dexto/llm';
5
+ import { getPrimaryApiKeyEnvVar } from '@dexto/core';
5
6
  /**
6
7
  * Updates the LLM provider information in a dexto config file
7
8
  * Used for project creation/initialization (modifies agent yml files)