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.
- package/dist/cli/commands/connect.d.ts +9 -0
- package/dist/cli/commands/connect.d.ts.map +1 -0
- package/dist/cli/commands/connect.js +244 -0
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/init-app.d.ts +1 -1
- package/dist/cli/commands/init-app.d.ts.map +1 -1
- package/dist/cli/commands/init-app.js +1 -1
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +31 -394
- package/dist/cli/utils/api-key-setup.d.ts +1 -1
- package/dist/cli/utils/api-key-setup.d.ts.map +1 -1
- package/dist/cli/utils/api-key-verification.d.ts +1 -1
- package/dist/cli/utils/api-key-verification.d.ts.map +1 -1
- package/dist/cli/utils/config-validation.d.ts.map +1 -1
- package/dist/cli/utils/config-validation.js +3 -1
- package/dist/cli/utils/options.js +1 -1
- package/dist/cli/utils/project-utils.d.ts +1 -1
- package/dist/cli/utils/project-utils.d.ts.map +1 -1
- package/dist/cli/utils/project-utils.js +2 -1
- package/dist/cli/utils/provider-setup.d.ts +1 -1
- package/dist/cli/utils/provider-setup.d.ts.map +1 -1
- package/dist/cli/utils/provider-setup.js +1 -1
- package/dist/config/cli-overrides.d.ts +2 -1
- package/dist/config/cli-overrides.d.ts.map +1 -1
- package/dist/config/cli-overrides.js +2 -1
- package/dist/config/effective-llm.d.ts +1 -1
- package/dist/config/effective-llm.d.ts.map +1 -1
- package/dist/index-main.js +27 -2
- package/dist/webui/assets/{index-M9d4U7k5.js → index-CpolTCmd.js} +173 -173
- package/dist/webui/index.html +1 -1
- 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
|
|
7
|
-
import {
|
|
8
|
-
import { createInitialPreferences, saveGlobalPreferences, loadGlobalPreferences, getGlobalPreferencesPath, updateGlobalPreferences, setActiveModel, isDextoAuthEnabled, loadCustomModels, saveCustomModel, deleteCustomModel, globalPreferencesExist,
|
|
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) &&
|
|
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
|
-
|
|
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(
|
|
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
|
-
?
|
|
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:
|
|
1756
|
-
hint:
|
|
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
|
|
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
|
|
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
|
|
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 ??
|
|
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 +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,
|
|
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 +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,
|
|
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;
|
|
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,
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-utils.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/project-utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,
|
|
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
|
|
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)
|