dexto 1.4.0 → 1.5.0
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/README.md +62 -7
- package/dist/agents/agent-template.yml +2 -2
- package/dist/agents/coding-agent/coding-agent.yml +22 -16
- package/dist/agents/database-agent/database-agent.yml +2 -2
- package/dist/agents/default-agent.yml +7 -5
- package/dist/agents/github-agent/github-agent.yml +2 -2
- package/dist/agents/product-name-researcher/product-name-researcher.yml +2 -2
- package/dist/agents/talk2pdf-agent/talk2pdf-agent.yml +2 -2
- package/dist/analytics/events.d.ts +13 -6
- package/dist/analytics/events.d.ts.map +1 -1
- package/dist/analytics/index.d.ts +1 -1
- package/dist/analytics/index.d.ts.map +1 -1
- package/dist/analytics/index.js +6 -2
- package/dist/api/server-hono.d.ts.map +1 -1
- package/dist/api/server-hono.js +27 -5
- package/dist/cli/cli-subscriber.d.ts +4 -0
- package/dist/cli/cli-subscriber.d.ts.map +1 -1
- package/dist/cli/cli-subscriber.js +40 -2
- package/dist/cli/commands/create-app.d.ts +16 -14
- package/dist/cli/commands/create-app.d.ts.map +1 -1
- package/dist/cli/commands/create-app.js +626 -102
- package/dist/cli/commands/create-image.d.ts +7 -0
- package/dist/cli/commands/create-image.d.ts.map +1 -0
- package/dist/cli/commands/create-image.js +201 -0
- package/dist/cli/commands/helpers/formatters.js +7 -7
- package/dist/cli/commands/index.d.ts +2 -1
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +2 -1
- package/dist/cli/commands/init-app.js +7 -7
- package/dist/cli/commands/install.d.ts +0 -3
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +10 -35
- package/dist/cli/commands/interactive-commands/command-parser.js +7 -7
- package/dist/cli/commands/interactive-commands/general-commands.js +1 -1
- package/dist/cli/commands/interactive-commands/prompt-commands.js +11 -11
- package/dist/cli/commands/interactive-commands/system/system-commands.js +3 -3
- package/dist/cli/commands/list-agents.js +2 -2
- package/dist/cli/commands/session-commands.js +16 -16
- package/dist/cli/commands/setup.d.ts +13 -5
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +860 -65
- package/dist/cli/commands/which.js +1 -1
- package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts +2 -0
- package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/ApprovalPrompt.js +29 -7
- package/dist/cli/ink-cli/components/CustomInput.js +1 -1
- package/dist/cli/ink-cli/components/EditableMultiLineInput.js +4 -4
- package/dist/cli/ink-cli/components/ElicitationForm.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/ElicitationForm.js +6 -6
- package/dist/cli/ink-cli/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/ErrorBoundary.js +1 -1
- package/dist/cli/ink-cli/components/Footer.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/Footer.js +1 -1
- package/dist/cli/ink-cli/components/HistorySearchBar.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/HistorySearchBar.js +1 -1
- package/dist/cli/ink-cli/components/MultiLineInput.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/MultiLineInput.js +3 -3
- package/dist/cli/ink-cli/components/ResourceAutocomplete.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/ResourceAutocomplete.js +4 -4
- package/dist/cli/ink-cli/components/SlashCommandAutocomplete.js +3 -3
- package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/StatusBar.js +7 -5
- package/dist/cli/ink-cli/components/TextBufferInput.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/TextBufferInput.js +6 -6
- package/dist/cli/ink-cli/components/base/BaseAutocomplete.js +4 -4
- package/dist/cli/ink-cli/components/base/BaseSelector.js +2 -2
- package/dist/cli/ink-cli/components/chat/Footer.js +1 -1
- package/dist/cli/ink-cli/components/chat/Header.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/chat/Header.js +2 -4
- package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/chat/MessageItem.js +5 -5
- package/dist/cli/ink-cli/components/chat/MessageList.js +1 -1
- package/dist/cli/ink-cli/components/chat/QueuedMessagesDisplay.js +1 -1
- package/dist/cli/ink-cli/components/chat/ToolIcon.d.ts +1 -1
- package/dist/cli/ink-cli/components/chat/ToolIcon.js +4 -4
- package/dist/cli/ink-cli/components/chat/styled-boxes/ConfigBox.js +1 -1
- package/dist/cli/ink-cli/components/chat/styled-boxes/HelpBox.js +1 -1
- package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.js +2 -2
- package/dist/cli/ink-cli/components/chat/styled-boxes/SessionHistoryBox.js +5 -5
- package/dist/cli/ink-cli/components/chat/styled-boxes/SessionListBox.js +2 -2
- package/dist/cli/ink-cli/components/chat/styled-boxes/ShortcutsBox.js +1 -1
- package/dist/cli/ink-cli/components/chat/styled-boxes/StatsBox.js +1 -1
- package/dist/cli/ink-cli/components/chat/styled-boxes/StyledBox.js +2 -2
- package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +1 -1
- package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/modes/StaticCLI.js +1 -1
- package/dist/cli/ink-cli/components/overlays/ApiKeyInput.js +1 -1
- package/dist/cli/ink-cli/components/overlays/CustomModelWizard.d.ts +10 -2
- package/dist/cli/ink-cli/components/overlays/CustomModelWizard.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/CustomModelWizard.js +198 -89
- package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +2 -2
- package/dist/cli/ink-cli/components/overlays/McpAddChoice.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpAddChoice.js +1 -1
- package/dist/cli/ink-cli/components/overlays/McpAddSelector.js +1 -1
- package/dist/cli/ink-cli/components/overlays/McpCustomTypeSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpCustomTypeSelector.js +2 -2
- package/dist/cli/ink-cli/components/overlays/McpCustomWizard.js +1 -1
- package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.js +1 -1
- package/dist/cli/ink-cli/components/overlays/McpSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpSelector.js +1 -1
- package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpServerActions.js +2 -2
- package/dist/cli/ink-cli/components/overlays/McpServerList.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpServerList.js +1 -1
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts +5 -5
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +222 -68
- package/dist/cli/ink-cli/components/overlays/PromptAddChoice.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/PromptAddChoice.js +2 -2
- package/dist/cli/ink-cli/components/overlays/PromptAddWizard.js +1 -1
- package/dist/cli/ink-cli/components/overlays/PromptDeleteSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/PromptDeleteSelector.js +2 -2
- package/dist/cli/ink-cli/components/overlays/PromptList.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/PromptList.js +2 -2
- package/dist/cli/ink-cli/components/overlays/SearchOverlay.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/SearchOverlay.js +4 -4
- package/dist/cli/ink-cli/components/overlays/SessionSubcommandSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/SessionSubcommandSelector.js +1 -1
- package/dist/cli/ink-cli/components/overlays/StreamSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/StreamSelector.js +1 -1
- package/dist/cli/ink-cli/components/overlays/ToolBrowser.js +12 -12
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/LocalModelWizard.d.ts +25 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/LocalModelWizard.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/LocalModelWizard.js +609 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/index.d.ts +15 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/index.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/index.js +14 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts +33 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.js +419 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/ApiKeyStep.d.ts +25 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/ApiKeyStep.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/ApiKeyStep.js +29 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/ProviderSelector.d.ts +17 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/ProviderSelector.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/ProviderSelector.js +11 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/SetupInfoBanner.d.ts +20 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/SetupInfoBanner.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/SetupInfoBanner.js +10 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/WizardStepInput.d.ts +30 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/WizardStepInput.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/WizardStepInput.js +13 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/index.d.ts +8 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/index.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/shared/index.js +7 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/types.d.ts +79 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/types.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/types.js +38 -0
- package/dist/cli/ink-cli/components/renderers/DiffRenderer.js +2 -2
- package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.js +1 -1
- package/dist/cli/ink-cli/components/renderers/FileRenderer.js +4 -4
- package/dist/cli/ink-cli/components/renderers/GenericRenderer.js +2 -2
- package/dist/cli/ink-cli/components/renderers/SearchRenderer.js +1 -1
- package/dist/cli/ink-cli/components/renderers/ShellRenderer.js +3 -3
- package/dist/cli/ink-cli/components/renderers/diff-shared.js +1 -1
- package/dist/cli/ink-cli/components/shared/MarkdownText.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/shared/MarkdownText.js +8 -6
- package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
- package/dist/cli/ink-cli/containers/InputContainer.js +23 -1
- package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
- package/dist/cli/ink-cli/containers/OverlayContainer.js +80 -24
- package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts +1 -1
- package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useAgentEvents.js +5 -1
- package/dist/cli/ink-cli/hooks/useCLIState.d.ts +1 -1
- package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useCLIState.js +4 -2
- package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
- package/dist/cli/ink-cli/services/processStream.js +77 -9
- package/dist/cli/ink-cli/state/types.d.ts +3 -2
- package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
- package/dist/cli/ink-cli/utils/messageFormatting.d.ts +5 -0
- package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
- package/dist/cli/ink-cli/utils/messageFormatting.js +59 -1
- package/dist/cli/ink-cli/utils/toolUtils.d.ts.map +1 -1
- package/dist/cli/ink-cli/utils/toolUtils.js +2 -0
- package/dist/cli/utils/api-key-setup.d.ts +54 -4
- package/dist/cli/utils/api-key-setup.d.ts.map +1 -1
- package/dist/cli/utils/api-key-setup.js +433 -107
- package/dist/cli/utils/api-key-verification.d.ts +17 -0
- package/dist/cli/utils/api-key-verification.d.ts.map +1 -0
- package/dist/cli/utils/api-key-verification.js +211 -0
- package/dist/cli/utils/config-validation.d.ts +22 -2
- package/dist/cli/utils/config-validation.d.ts.map +1 -1
- package/dist/cli/utils/config-validation.js +354 -25
- package/dist/cli/utils/local-model-setup.d.ts +46 -0
- package/dist/cli/utils/local-model-setup.d.ts.map +1 -0
- package/dist/cli/utils/local-model-setup.js +662 -0
- package/dist/cli/utils/options.js +1 -1
- package/dist/cli/utils/prompt-helpers.d.ts +47 -0
- package/dist/cli/utils/prompt-helpers.d.ts.map +1 -0
- package/dist/cli/utils/prompt-helpers.js +66 -0
- package/dist/cli/utils/provider-setup.d.ts +66 -8
- package/dist/cli/utils/provider-setup.d.ts.map +1 -1
- package/dist/cli/utils/provider-setup.js +324 -84
- package/dist/cli/utils/scaffolding-utils.d.ts +76 -0
- package/dist/cli/utils/scaffolding-utils.d.ts.map +1 -0
- package/dist/cli/utils/scaffolding-utils.js +246 -0
- package/dist/cli/utils/setup-utils.d.ts +16 -0
- package/dist/cli/utils/setup-utils.d.ts.map +1 -1
- package/dist/cli/utils/setup-utils.js +72 -21
- package/dist/cli/utils/template-engine.d.ts +65 -0
- package/dist/cli/utils/template-engine.d.ts.map +1 -0
- package/dist/cli/utils/template-engine.js +1089 -0
- package/dist/config/cli-overrides.d.ts +44 -1
- package/dist/config/cli-overrides.d.ts.map +1 -1
- package/dist/config/cli-overrides.js +102 -0
- package/dist/index.js +315 -53
- package/dist/webui/assets/index-8j-KMkX1.js +2054 -0
- package/dist/webui/assets/index-c_AX24V4.css +1 -0
- package/dist/webui/index.html +3 -9
- package/dist/webui/logos/aws-color.svg +1 -0
- package/dist/webui/logos/dexto/dexto_logo.svg +1 -1
- package/dist/webui/logos/dexto/dexto_logo_light.svg +6 -6
- package/dist/webui/logos/glama.svg +7 -0
- package/dist/webui/logos/litellm.svg +7 -0
- package/dist/webui/logos/openrouter.svg +1 -0
- package/package.json +8 -7
- package/dist/webui/assets/index-BkwPkZpd.css +0 -1
- package/dist/webui/assets/index-D9u1XfyH.js +0 -2025
package/dist/index.js
CHANGED
|
@@ -19,10 +19,10 @@ import { resolveAgentPath, loadAgentConfig, globalPreferencesExist, loadGlobalPr
|
|
|
19
19
|
import { startHonoApiServer } from './api/server-hono.js';
|
|
20
20
|
import { validateCliOptions, handleCliOptionsError } from './cli/utils/options.js';
|
|
21
21
|
import { validateAgentConfig } from './cli/utils/config-validation.js';
|
|
22
|
-
import { applyCLIOverrides } from './config/cli-overrides.js';
|
|
22
|
+
import { applyCLIOverrides, applyUserPreferences, checkAgentCompatibility, } from './config/cli-overrides.js';
|
|
23
23
|
import { enrichAgentConfig } from '@dexto/agent-management';
|
|
24
24
|
import { getPort } from './utils/port-utils.js';
|
|
25
|
-
import { createDextoProject,
|
|
25
|
+
import { createDextoProject, createImage, getUserInputToInitDextoApp, initDexto, postInitDexto, } from './cli/commands/index.js';
|
|
26
26
|
import { handleSetupCommand, handleInstallCommand, handleUninstallCommand, handleListAgentsCommand, handleWhichCommand, } from './cli/commands/index.js';
|
|
27
27
|
import { handleSessionListCommand, handleSessionHistoryCommand, handleSessionDeleteCommand, handleSessionSearchCommand, } from './cli/commands/session-commands.js';
|
|
28
28
|
import { requiresSetup } from './cli/utils/setup-utils.js';
|
|
@@ -34,6 +34,41 @@ import { initializeMcpToolAggregationServer } from './api/mcp/tool-aggregation-h
|
|
|
34
34
|
const program = new Command();
|
|
35
35
|
// Initialize analytics early (no-op if disabled)
|
|
36
36
|
await initAnalytics({ appVersion: pkg.version });
|
|
37
|
+
/**
|
|
38
|
+
* Recursively removes null values from an object.
|
|
39
|
+
* This handles YAML files that have explicit `apiKey: null` entries
|
|
40
|
+
* which would otherwise cause "Expected string, received null" validation errors.
|
|
41
|
+
*/
|
|
42
|
+
function cleanNullValues(obj) {
|
|
43
|
+
if (obj === null || obj === undefined)
|
|
44
|
+
return obj;
|
|
45
|
+
if (typeof obj !== 'object')
|
|
46
|
+
return obj;
|
|
47
|
+
if (Array.isArray(obj)) {
|
|
48
|
+
return obj.map((item) => typeof item === 'object' && item !== null
|
|
49
|
+
? cleanNullValues(item)
|
|
50
|
+
: item);
|
|
51
|
+
}
|
|
52
|
+
const cleaned = {};
|
|
53
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
54
|
+
if (value === null) {
|
|
55
|
+
// Skip null values - they become undefined (missing)
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
59
|
+
cleaned[key] = cleanNullValues(value);
|
|
60
|
+
}
|
|
61
|
+
else if (Array.isArray(value)) {
|
|
62
|
+
cleaned[key] = value.map((item) => typeof item === 'object' && item !== null
|
|
63
|
+
? cleanNullValues(item)
|
|
64
|
+
: item);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
cleaned[key] = value;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return cleaned;
|
|
71
|
+
}
|
|
37
72
|
// 1) GLOBAL OPTIONS
|
|
38
73
|
program
|
|
39
74
|
.name('dexto')
|
|
@@ -52,36 +87,22 @@ program
|
|
|
52
87
|
.option('--mode <mode>', 'The application in which dexto should talk to you - web | cli | server | mcp', 'web')
|
|
53
88
|
.option('--port <port>', 'port for the server (default: 3000 for web, 3001 for server mode)')
|
|
54
89
|
.option('--no-auto-install', 'Disable automatic installation of missing agents from registry')
|
|
90
|
+
.option('--image <package>', 'Image package to load (e.g., @dexto/image-local). Overrides config image field.')
|
|
55
91
|
.enablePositionalOptions();
|
|
56
92
|
// 2) `create-app` SUB-COMMAND
|
|
57
93
|
program
|
|
58
|
-
.command('create-app')
|
|
59
|
-
.description('
|
|
60
|
-
.
|
|
94
|
+
.command('create-app [name]')
|
|
95
|
+
.description('Create a Dexto application (CLI, web, bot, etc.)')
|
|
96
|
+
.option('--from-image <package>', 'Use existing image (e.g., @dexto/image-local)')
|
|
97
|
+
.option('--extend-image <package>', 'Extend image with custom providers')
|
|
98
|
+
.option('--from-core', 'Build from @dexto/core (advanced)')
|
|
99
|
+
.option('--type <type>', 'App type: script, webapp (default: script)')
|
|
100
|
+
.action(withAnalytics('create-app', async (name, options) => {
|
|
61
101
|
try {
|
|
62
|
-
p.intro(chalk.inverse('Dexto
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const userInput = await getUserInputToInitDextoApp();
|
|
67
|
-
try {
|
|
68
|
-
capture('dexto_create', {
|
|
69
|
-
provider: userInput.llmProvider,
|
|
70
|
-
providedKey: Boolean(userInput.llmApiKey),
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// Analytics failures should not block CLI execution.
|
|
75
|
-
}
|
|
76
|
-
// move to project directory, then add the dexto scripts to the package.json and create the tsconfig.json
|
|
77
|
-
process.chdir(appPath);
|
|
78
|
-
await addDextoScriptsToPackageJson(userInput.directory, appPath);
|
|
79
|
-
await createTsconfigJson(appPath, userInput.directory);
|
|
80
|
-
// then initialize the other parts of the project
|
|
81
|
-
await initDexto(userInput.directory, userInput.createExampleFile, userInput.llmProvider, userInput.llmApiKey);
|
|
82
|
-
p.outro(chalk.greenBright('Dexto app created and initialized successfully!'));
|
|
83
|
-
// add notes for users to get started with their newly created Dexto project
|
|
84
|
-
await postCreateDexto(appPath, userInput.directory);
|
|
102
|
+
p.intro(chalk.inverse('Create Dexto App'));
|
|
103
|
+
// Create the app project structure (fully self-contained)
|
|
104
|
+
await createDextoProject(name, options);
|
|
105
|
+
p.outro(chalk.greenBright('Dexto app created successfully!'));
|
|
85
106
|
safeExit('create-app', 0);
|
|
86
107
|
}
|
|
87
108
|
catch (err) {
|
|
@@ -91,7 +112,26 @@ program
|
|
|
91
112
|
safeExit('create-app', 1, 'error');
|
|
92
113
|
}
|
|
93
114
|
}));
|
|
94
|
-
// 3) `
|
|
115
|
+
// 3) `create-image` SUB-COMMAND
|
|
116
|
+
program
|
|
117
|
+
.command('create-image [name]')
|
|
118
|
+
.description('Create a Dexto image - a distributable agent harness package')
|
|
119
|
+
.action(withAnalytics('create-image', async (name) => {
|
|
120
|
+
try {
|
|
121
|
+
p.intro(chalk.inverse('Create Dexto Image'));
|
|
122
|
+
// Create the image project structure
|
|
123
|
+
const projectPath = await createImage(name);
|
|
124
|
+
p.outro(chalk.greenBright(`Dexto image created successfully at ${projectPath}!`));
|
|
125
|
+
safeExit('create-image', 0);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
if (err instanceof ExitSignal)
|
|
129
|
+
throw err;
|
|
130
|
+
console.error(`❌ dexto create-image command failed: ${err}`);
|
|
131
|
+
safeExit('create-image', 1, 'error');
|
|
132
|
+
}
|
|
133
|
+
}));
|
|
134
|
+
// 4) `init-app` SUB-COMMAND
|
|
95
135
|
program
|
|
96
136
|
.command('init-app')
|
|
97
137
|
.description('Initialize an existing Typescript app with Dexto')
|
|
@@ -130,13 +170,13 @@ program
|
|
|
130
170
|
safeExit('init-app', 1, 'error');
|
|
131
171
|
}
|
|
132
172
|
}));
|
|
133
|
-
//
|
|
173
|
+
// 5) `setup` SUB-COMMAND
|
|
134
174
|
program
|
|
135
175
|
.command('setup')
|
|
136
176
|
.description('Configure global Dexto preferences')
|
|
137
177
|
.option('--provider <provider>', 'LLM provider (openai, anthropic, google, groq)')
|
|
138
178
|
.option('--model <model>', 'Model name (uses provider default if not specified)')
|
|
139
|
-
.option('--default-agent <agent>', 'Default agent name (default:
|
|
179
|
+
.option('--default-agent <agent>', 'Default agent name (default: coding-agent)')
|
|
140
180
|
.option('--no-interactive', 'Skip interactive prompts and API key setup')
|
|
141
181
|
.option('--force', 'Overwrite existing setup without confirmation')
|
|
142
182
|
.action(withAnalytics('setup', async (options) => {
|
|
@@ -151,7 +191,7 @@ program
|
|
|
151
191
|
safeExit('setup', 1, 'error');
|
|
152
192
|
}
|
|
153
193
|
}));
|
|
154
|
-
//
|
|
194
|
+
// 6) `install` SUB-COMMAND
|
|
155
195
|
program
|
|
156
196
|
.command('install [agents...]')
|
|
157
197
|
.description('Install agents from registry or custom YAML files/directories')
|
|
@@ -160,7 +200,7 @@ program
|
|
|
160
200
|
.option('--force', 'Force reinstall even if agent is already installed')
|
|
161
201
|
.addHelpText('after', `
|
|
162
202
|
Examples:
|
|
163
|
-
$ dexto install
|
|
203
|
+
$ dexto install coding-agent Install agent from registry
|
|
164
204
|
$ dexto install agent1 agent2 Install multiple registry agents
|
|
165
205
|
$ dexto install --all Install all available registry agents
|
|
166
206
|
$ dexto install ./my-agent.yml Install custom agent from YAML file
|
|
@@ -177,12 +217,12 @@ Examples:
|
|
|
177
217
|
safeExit('install', 1, 'error');
|
|
178
218
|
}
|
|
179
219
|
}));
|
|
180
|
-
//
|
|
220
|
+
// 7) `uninstall` SUB-COMMAND
|
|
181
221
|
program
|
|
182
222
|
.command('uninstall [agents...]')
|
|
183
223
|
.description('Uninstall agents from the local installation')
|
|
184
224
|
.option('--all', 'Uninstall all installed agents')
|
|
185
|
-
.option('--force', 'Force uninstall even if agent is protected (e.g.,
|
|
225
|
+
.option('--force', 'Force uninstall even if agent is protected (e.g., coding-agent)')
|
|
186
226
|
.action(withAnalytics('uninstall', async (agents, options) => {
|
|
187
227
|
try {
|
|
188
228
|
await handleUninstallCommand(agents, options);
|
|
@@ -195,7 +235,7 @@ program
|
|
|
195
235
|
safeExit('uninstall', 1, 'error');
|
|
196
236
|
}
|
|
197
237
|
}));
|
|
198
|
-
//
|
|
238
|
+
// 8) `list-agents` SUB-COMMAND
|
|
199
239
|
program
|
|
200
240
|
.command('list-agents')
|
|
201
241
|
.description('List available and installed agents')
|
|
@@ -214,7 +254,7 @@ program
|
|
|
214
254
|
safeExit('list-agents', 1, 'error');
|
|
215
255
|
}
|
|
216
256
|
}));
|
|
217
|
-
//
|
|
257
|
+
// 9) `which` SUB-COMMAND
|
|
218
258
|
program
|
|
219
259
|
.command('which <agent>')
|
|
220
260
|
.description('Show the path to an agent')
|
|
@@ -233,12 +273,27 @@ program
|
|
|
233
273
|
// Helper to bootstrap a minimal agent for non-interactive session/search ops
|
|
234
274
|
async function bootstrapAgentFromGlobalOpts() {
|
|
235
275
|
const globalOpts = program.opts();
|
|
236
|
-
const resolvedPath = await resolveAgentPath(globalOpts.agent, globalOpts.autoInstall !== false
|
|
276
|
+
const resolvedPath = await resolveAgentPath(globalOpts.agent, globalOpts.autoInstall !== false);
|
|
237
277
|
const rawConfig = await loadAgentConfig(resolvedPath);
|
|
238
278
|
const mergedConfig = applyCLIOverrides(rawConfig, globalOpts);
|
|
239
279
|
const enrichedConfig = enrichAgentConfig(mergedConfig, resolvedPath, {
|
|
240
280
|
logLevel: 'info', // CLI uses info-level logging for visibility
|
|
241
281
|
});
|
|
282
|
+
// Load image dynamically if specified (same priority as main command)
|
|
283
|
+
// Priority: CLI flag > Agent config > Environment variable > Default
|
|
284
|
+
// Images are optional, but default to image-local for convenience
|
|
285
|
+
const imageName = globalOpts.image || // --image flag
|
|
286
|
+
enrichedConfig.image || // image field in agent config
|
|
287
|
+
process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
|
|
288
|
+
'@dexto/image-local'; // Default for convenience
|
|
289
|
+
try {
|
|
290
|
+
await import(imageName);
|
|
291
|
+
}
|
|
292
|
+
catch (_err) {
|
|
293
|
+
console.error(`❌ Failed to load image '${imageName}'`);
|
|
294
|
+
console.error(`💡 Install it with: ${existsSync('package.json') ? 'npm install' : 'npm install -g'} ${imageName}`);
|
|
295
|
+
safeExit('bootstrap', 1, 'image-load-failed');
|
|
296
|
+
}
|
|
242
297
|
// Override approval config for read-only commands (never run conversations)
|
|
243
298
|
// This avoids needing to set up unused approval handlers
|
|
244
299
|
enrichedConfig.toolConfirmation = {
|
|
@@ -253,7 +308,8 @@ async function bootstrapAgentFromGlobalOpts() {
|
|
|
253
308
|
timeout: enrichedConfig.elicitation.timeout,
|
|
254
309
|
}),
|
|
255
310
|
};
|
|
256
|
-
|
|
311
|
+
// Use relaxed validation for session commands - they don't need LLM calls
|
|
312
|
+
const agent = new DextoAgent(enrichedConfig, resolvedPath, { strict: false });
|
|
257
313
|
await agent.start();
|
|
258
314
|
// Register graceful shutdown
|
|
259
315
|
const shutdown = async () => {
|
|
@@ -292,7 +348,7 @@ async function getMostRecentSessionId(agent, includeHeadless = false) {
|
|
|
292
348
|
}
|
|
293
349
|
return mostRecentId;
|
|
294
350
|
}
|
|
295
|
-
//
|
|
351
|
+
// 10) `session` SUB-COMMAND
|
|
296
352
|
const sessionCommand = program.command('session').description('Manage chat sessions');
|
|
297
353
|
sessionCommand
|
|
298
354
|
.command('list')
|
|
@@ -347,7 +403,7 @@ sessionCommand
|
|
|
347
403
|
safeExit('session delete', 1, 'error');
|
|
348
404
|
}
|
|
349
405
|
}));
|
|
350
|
-
//
|
|
406
|
+
// 11) `search` SUB-COMMAND
|
|
351
407
|
program
|
|
352
408
|
.command('search')
|
|
353
409
|
.description('Search session history')
|
|
@@ -389,7 +445,7 @@ program
|
|
|
389
445
|
safeExit('search', 1, 'error');
|
|
390
446
|
}
|
|
391
447
|
}));
|
|
392
|
-
//
|
|
448
|
+
// 12) `mcp` SUB-COMMAND
|
|
393
449
|
// For now, this mode simply aggregates and re-expose tools from configured MCP servers (no agent)
|
|
394
450
|
// dexto --mode mcp will be moved to this sub-command in the future
|
|
395
451
|
program
|
|
@@ -412,7 +468,7 @@ program
|
|
|
412
468
|
// Get the global agent option from the main program
|
|
413
469
|
const globalOpts = program.opts();
|
|
414
470
|
const nameOrPath = globalOpts.agent;
|
|
415
|
-
const configPath = await resolveAgentPath(nameOrPath, globalOpts.autoInstall !== false
|
|
471
|
+
const configPath = await resolveAgentPath(nameOrPath, globalOpts.autoInstall !== false);
|
|
416
472
|
console.log(`📄 Loading Dexto config from: ${configPath}`);
|
|
417
473
|
const config = await loadAgentConfig(configPath);
|
|
418
474
|
logger.info(`Validating MCP servers...`);
|
|
@@ -442,7 +498,7 @@ program
|
|
|
442
498
|
safeExit('mcp', 1, 'mcp-agg-failed');
|
|
443
499
|
}
|
|
444
500
|
}, { timeoutMs: 0 }));
|
|
445
|
-
//
|
|
501
|
+
// 13) Main dexto CLI - Interactive/One shot (CLI/HEADLESS) or run in other modes (--mode web/server/mcp)
|
|
446
502
|
program
|
|
447
503
|
.argument('[prompt...]', 'Natural-language prompt to run once. If not passed, dexto will start as an interactive CLI')
|
|
448
504
|
// Main customer facing description
|
|
@@ -565,10 +621,14 @@ program
|
|
|
565
621
|
// ——— ENHANCED PREFERENCE-AWARE CONFIG LOADING ———
|
|
566
622
|
let validatedConfig;
|
|
567
623
|
let resolvedPath;
|
|
624
|
+
// Determine validation mode early - used throughout config loading and agent creation
|
|
625
|
+
// Use relaxed validation for interactive modes (web/cli) where users can configure later
|
|
626
|
+
// Use strict validation for headless modes (server/mcp) that need full config upfront
|
|
627
|
+
const isInteractiveMode = opts.mode === 'web' || opts.mode === 'cli';
|
|
568
628
|
try {
|
|
569
629
|
// Case 1: File path - skip all validation and setup
|
|
570
630
|
if (opts.agent && isPath(opts.agent)) {
|
|
571
|
-
resolvedPath = await resolveAgentPath(opts.agent, opts.autoInstall !== false
|
|
631
|
+
resolvedPath = await resolveAgentPath(opts.agent, opts.autoInstall !== false);
|
|
572
632
|
}
|
|
573
633
|
// Cases 2 & 3: Default agent or registry agent
|
|
574
634
|
else {
|
|
@@ -608,23 +668,190 @@ program
|
|
|
608
668
|
safeExit('main', 1, 'setup-required-non-interactive');
|
|
609
669
|
}
|
|
610
670
|
await handleSetupCommand({ interactive: true });
|
|
671
|
+
// Reload preferences after setup to get the newly selected default mode
|
|
672
|
+
// (setup may have just saved a different mode than the default 'web')
|
|
673
|
+
try {
|
|
674
|
+
const newPreferences = await loadGlobalPreferences();
|
|
675
|
+
if (newPreferences.defaults?.defaultMode) {
|
|
676
|
+
opts.mode = newPreferences.defaults.defaultMode;
|
|
677
|
+
logger.debug(`Updated mode from setup preferences: ${opts.mode}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
catch {
|
|
681
|
+
// Ignore errors - will use default mode
|
|
682
|
+
}
|
|
611
683
|
}
|
|
612
|
-
// Now resolve agent (will auto-install
|
|
613
|
-
resolvedPath = await resolveAgentPath(opts.agent, opts.autoInstall !== false
|
|
684
|
+
// Now resolve agent (will auto-install since setup is complete)
|
|
685
|
+
resolvedPath = await resolveAgentPath(opts.agent, opts.autoInstall !== false);
|
|
614
686
|
}
|
|
615
687
|
// Load raw config and apply CLI overrides
|
|
616
688
|
const rawConfig = await loadAgentConfig(resolvedPath);
|
|
617
|
-
|
|
689
|
+
let mergedConfig = applyCLIOverrides(rawConfig, opts);
|
|
690
|
+
// ——— PREFERENCE-AWARE CONFIG HANDLING ———
|
|
691
|
+
// For coding-agent (no explicit agent specified): Apply user preferences
|
|
692
|
+
// For specific agents: Check compatibility and warn if needed
|
|
693
|
+
const isDefaultAgent = !opts.agent;
|
|
694
|
+
let preferences = null;
|
|
695
|
+
if (globalPreferencesExist()) {
|
|
696
|
+
try {
|
|
697
|
+
preferences = await loadGlobalPreferences();
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
// Preferences exist but couldn't load - continue without them
|
|
701
|
+
logger.debug('Could not load preferences, continuing without them');
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
// Check for pending API key setup (user skipped during initial setup)
|
|
705
|
+
if (isDefaultAgent &&
|
|
706
|
+
preferences?.setup?.apiKeyPending &&
|
|
707
|
+
opts.interactive !== false) {
|
|
708
|
+
// Check if API key is still missing (user may have set it manually)
|
|
709
|
+
const configuredApiKey = resolveApiKeyForProvider(preferences.llm.provider);
|
|
710
|
+
if (!configuredApiKey) {
|
|
711
|
+
const { promptForPendingApiKey } = await import('./cli/utils/api-key-setup.js');
|
|
712
|
+
const { updateGlobalPreferences } = await import('@dexto/agent-management');
|
|
713
|
+
const result = await promptForPendingApiKey(preferences.llm.provider, preferences.llm.model);
|
|
714
|
+
if (result.action === 'cancel') {
|
|
715
|
+
safeExit('main', 0, 'pending-api-key-cancelled');
|
|
716
|
+
}
|
|
717
|
+
if (result.action === 'setup' && result.apiKey) {
|
|
718
|
+
// API key was configured - update preferences to clear pending flag
|
|
719
|
+
await updateGlobalPreferences({
|
|
720
|
+
setup: { apiKeyPending: false },
|
|
721
|
+
});
|
|
722
|
+
// Update the merged config with the new API key
|
|
723
|
+
mergedConfig.llm.apiKey = result.apiKey;
|
|
724
|
+
logger.debug('API key configured, pending flag cleared');
|
|
725
|
+
}
|
|
726
|
+
// If 'skip', continue without API key (user chose to proceed)
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
// API key exists (user set it manually) - clear the pending flag
|
|
730
|
+
const { updateGlobalPreferences } = await import('@dexto/agent-management');
|
|
731
|
+
await updateGlobalPreferences({
|
|
732
|
+
setup: { apiKeyPending: false },
|
|
733
|
+
});
|
|
734
|
+
logger.debug('API key found in environment, cleared pending flag');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (isDefaultAgent && preferences) {
|
|
738
|
+
// Default-agent: Apply user's LLM preferences at runtime
|
|
739
|
+
// This ensures the base agent always uses user's preferred model/provider
|
|
740
|
+
mergedConfig = applyUserPreferences(mergedConfig, preferences);
|
|
741
|
+
logger.debug('Applied user preferences to coding-agent', {
|
|
742
|
+
provider: preferences.llm.provider,
|
|
743
|
+
model: preferences.llm.model,
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
else if (!isDefaultAgent && mergedConfig.llm) {
|
|
747
|
+
// Specific agent: Check if user has the required provider configured
|
|
748
|
+
const agentProvider = mergedConfig.llm.provider;
|
|
749
|
+
const resolvedApiKey = resolveApiKeyForProvider(agentProvider);
|
|
750
|
+
const compatibility = checkAgentCompatibility(mergedConfig, preferences, resolvedApiKey);
|
|
751
|
+
if (!compatibility.compatible && opts.interactive !== false) {
|
|
752
|
+
// User is missing API key for the agent's provider
|
|
753
|
+
if (!compatibility.userHasApiKey &&
|
|
754
|
+
preferences?.llm?.provider &&
|
|
755
|
+
preferences?.llm?.model) {
|
|
756
|
+
// User has a default LLM configured - offer choice
|
|
757
|
+
const { promptForMissingAgentApiKey } = await import('./cli/utils/api-key-setup.js');
|
|
758
|
+
const result = await promptForMissingAgentApiKey(compatibility.agentProvider, compatibility.agentModel, preferences.llm.provider, preferences.llm.model);
|
|
759
|
+
if (result.action === 'cancel') {
|
|
760
|
+
safeExit('main', 0, 'agent-api-key-cancelled');
|
|
761
|
+
}
|
|
762
|
+
if (result.action === 'use-default') {
|
|
763
|
+
// Apply user's default LLM to the agent config
|
|
764
|
+
mergedConfig = applyUserPreferences(mergedConfig, preferences);
|
|
765
|
+
// Also resolve the actual API key from environment
|
|
766
|
+
// (preferences store env var reference like $GOOGLE_API_KEY, not the actual key)
|
|
767
|
+
const userApiKey = resolveApiKeyForProvider(preferences.llm.provider);
|
|
768
|
+
if (userApiKey) {
|
|
769
|
+
mergedConfig.llm.apiKey = userApiKey;
|
|
770
|
+
}
|
|
771
|
+
logger.debug('Applied user preferences to agent (user chose default)', {
|
|
772
|
+
provider: preferences.llm.provider,
|
|
773
|
+
model: preferences.llm.model,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
if (result.action === 'add-key' && result.apiKey) {
|
|
777
|
+
// User added the API key - update the config
|
|
778
|
+
mergedConfig.llm.apiKey = result.apiKey;
|
|
779
|
+
logger.debug('Applied new API key to agent config');
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
// No default LLM to fall back to - show warnings only
|
|
784
|
+
console.log(chalk.yellow('\n⚠️ Agent Compatibility Notice:'));
|
|
785
|
+
for (const warning of compatibility.warnings) {
|
|
786
|
+
console.log(chalk.yellow(` ${warning}`));
|
|
787
|
+
}
|
|
788
|
+
if (compatibility.instructions.length > 0) {
|
|
789
|
+
console.log(chalk.dim('\n To fix:'));
|
|
790
|
+
for (const instruction of compatibility.instructions) {
|
|
791
|
+
console.log(chalk.dim(` ${instruction}`));
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
console.log(''); // Empty line for spacing
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
// Clean up null values from config (can happen from YAML files with explicit nulls)
|
|
799
|
+
// This prevents "Expected string, received null" errors for optional fields
|
|
800
|
+
const cleanedConfig = cleanNullValues(mergedConfig);
|
|
618
801
|
// Enrich config with per-agent paths BEFORE validation
|
|
619
802
|
// Enrichment adds filesystem paths to storage (schema has in-memory defaults)
|
|
620
803
|
// Interactive CLI mode: only log to file (console would interfere with chat UI)
|
|
621
804
|
const isInteractiveCli = opts.mode === 'cli' && !headlessInput;
|
|
622
|
-
const enrichedConfig = enrichAgentConfig(
|
|
805
|
+
const enrichedConfig = enrichAgentConfig(cleanedConfig, resolvedPath, {
|
|
623
806
|
isInteractiveCli,
|
|
624
807
|
logLevel: 'info', // CLI uses info-level logging for visibility
|
|
625
808
|
});
|
|
626
809
|
// Validate enriched config with interactive setup if needed (for API key issues)
|
|
627
|
-
|
|
810
|
+
// isInteractiveMode is defined above the try block
|
|
811
|
+
const validationResult = await validateAgentConfig(enrichedConfig, opts.interactive !== false, { strict: !isInteractiveMode });
|
|
812
|
+
if (validationResult.success && validationResult.config) {
|
|
813
|
+
validatedConfig = validationResult.config;
|
|
814
|
+
}
|
|
815
|
+
else if (validationResult.skipped) {
|
|
816
|
+
// User chose to continue despite validation errors
|
|
817
|
+
// SAFETY: This cast is intentionally unsafe - it's an escape hatch for users
|
|
818
|
+
// when validation is overly strict or incorrect. Runtime errors will surface
|
|
819
|
+
// if the config truly doesn't work. Future: explicit `allowUnvalidated` mode.
|
|
820
|
+
logger.warn('Starting with validation warnings - some features may not work');
|
|
821
|
+
validatedConfig = enrichedConfig;
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
// Validation failed and user didn't skip - show next steps and exit
|
|
825
|
+
safeExit('main', 1, 'config-validation-failed');
|
|
826
|
+
}
|
|
827
|
+
// ——— LOAD IMAGE DYNAMICALLY (if specified) ———
|
|
828
|
+
// Priority: CLI flag > Agent config > Environment variable > Default
|
|
829
|
+
// Images are optional, but default to image-local for convenience
|
|
830
|
+
const imageName = opts.image || // --image flag
|
|
831
|
+
validatedConfig.image || // image field in agent config
|
|
832
|
+
process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
|
|
833
|
+
'@dexto/image-local'; // Default for convenience
|
|
834
|
+
try {
|
|
835
|
+
await import(imageName);
|
|
836
|
+
logger.debug(`Loaded image: ${imageName}`);
|
|
837
|
+
}
|
|
838
|
+
catch (err) {
|
|
839
|
+
console.error(`❌ Failed to load image '${imageName}'`);
|
|
840
|
+
console.error(`💡 Install it with: ${existsSync('package.json') ? 'npm install' : 'npm install -g'} ${imageName}`);
|
|
841
|
+
if (err instanceof Error) {
|
|
842
|
+
logger.debug(`Image load error: ${err.message}`);
|
|
843
|
+
}
|
|
844
|
+
safeExit('main', 1, 'image-load-failed');
|
|
845
|
+
}
|
|
846
|
+
// Validate that if config specifies an image, it matches what was loaded
|
|
847
|
+
// Skip this check if user explicitly provided --image flag (intentional override)
|
|
848
|
+
if (!opts.image &&
|
|
849
|
+
validatedConfig.image &&
|
|
850
|
+
validatedConfig.image !== imageName) {
|
|
851
|
+
console.error(`❌ Config specifies image '${validatedConfig.image}' but '${imageName}' was loaded instead`);
|
|
852
|
+
console.error(`💡 Either remove 'image' from config or ensure it matches the loaded image`);
|
|
853
|
+
safeExit('main', 1, 'image-mismatch');
|
|
854
|
+
}
|
|
628
855
|
}
|
|
629
856
|
catch (err) {
|
|
630
857
|
if (err instanceof ExitSignal)
|
|
@@ -677,7 +904,10 @@ program
|
|
|
677
904
|
}
|
|
678
905
|
// Config is already enriched and validated - ready for agent creation
|
|
679
906
|
// DextoAgent will parse/validate again (parse-twice pattern)
|
|
680
|
-
|
|
907
|
+
// isInteractiveMode is already defined above for validateAgentConfig
|
|
908
|
+
agent = new DextoAgent(validatedConfig, resolvedPath, {
|
|
909
|
+
strict: !isInteractiveMode,
|
|
910
|
+
});
|
|
681
911
|
// Start the agent (initialize async services)
|
|
682
912
|
// - web/server modes: initializeHonoApi will set approval handler and start the agent
|
|
683
913
|
// - cli mode: handles its own approval setup in the case block
|
|
@@ -822,6 +1052,38 @@ program
|
|
|
822
1052
|
else {
|
|
823
1053
|
// Interactive mode - session management handled via /resume command
|
|
824
1054
|
// Note: -c and -r flags are validated to require a prompt (headless mode only)
|
|
1055
|
+
// Check if API key is configured before trying to create session
|
|
1056
|
+
// Session creation triggers LLM service init which requires API key
|
|
1057
|
+
const llmConfig = agent.getCurrentLLMConfig();
|
|
1058
|
+
const { requiresApiKey } = await import('@dexto/core');
|
|
1059
|
+
if (requiresApiKey(llmConfig.provider) && !llmConfig.apiKey?.trim()) {
|
|
1060
|
+
// Offer interactive API key setup instead of just exiting
|
|
1061
|
+
const { interactiveApiKeySetup } = await import('./cli/utils/api-key-setup.js');
|
|
1062
|
+
console.log(chalk.yellow(`\n⚠️ API key required for provider '${llmConfig.provider}'\n`));
|
|
1063
|
+
const setupResult = await interactiveApiKeySetup(llmConfig.provider, {
|
|
1064
|
+
exitOnCancel: false,
|
|
1065
|
+
model: llmConfig.model,
|
|
1066
|
+
});
|
|
1067
|
+
if (setupResult.cancelled) {
|
|
1068
|
+
await agent.stop().catch(() => { });
|
|
1069
|
+
safeExit('main', 0, 'api-key-setup-cancelled');
|
|
1070
|
+
}
|
|
1071
|
+
if (setupResult.skipped) {
|
|
1072
|
+
// User chose to skip - exit with instructions
|
|
1073
|
+
await agent.stop().catch(() => { });
|
|
1074
|
+
safeExit('main', 0, 'api-key-pending');
|
|
1075
|
+
}
|
|
1076
|
+
if (setupResult.success && setupResult.apiKey) {
|
|
1077
|
+
// API key was entered and saved - reload config and continue
|
|
1078
|
+
// Update the agent's LLM config with the new API key
|
|
1079
|
+
await agent.switchLLM({
|
|
1080
|
+
provider: llmConfig.provider,
|
|
1081
|
+
model: llmConfig.model,
|
|
1082
|
+
apiKey: setupResult.apiKey,
|
|
1083
|
+
});
|
|
1084
|
+
logger.info('API key configured successfully, continuing...');
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
825
1087
|
// Create session eagerly so slash commands work immediately
|
|
826
1088
|
const session = await agent.createSession();
|
|
827
1089
|
const cliSessionId = session.id;
|
|
@@ -873,8 +1135,8 @@ program
|
|
|
873
1135
|
}
|
|
874
1136
|
safeExit('main', 0);
|
|
875
1137
|
}
|
|
876
|
-
break;
|
|
877
1138
|
}
|
|
1139
|
+
// falls through - safeExit returns never, but eslint doesn't know that
|
|
878
1140
|
case 'web': {
|
|
879
1141
|
// Default to 3000 for web mode
|
|
880
1142
|
const defaultPort = opts.port ? parseInt(opts.port, 10) : 3000;
|
|
@@ -975,5 +1237,5 @@ program
|
|
|
975
1237
|
safeExit('main', 1, 'unknown-mode');
|
|
976
1238
|
}
|
|
977
1239
|
}, { timeoutMs: 0 }));
|
|
978
|
-
//
|
|
1240
|
+
// 14) PARSE & EXECUTE
|
|
979
1241
|
program.parseAsync(process.argv);
|