dexto 1.5.6 → 1.5.8
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 +13 -0
- package/dist/agents/coding-agent/coding-agent.yml +17 -0
- package/dist/agents/coding-agent/skills/code-review.md +46 -0
- package/dist/agents/explore-agent/explore-agent.yml +2 -0
- package/dist/analytics/events.d.ts +1 -1
- package/dist/analytics/events.d.ts.map +1 -1
- package/dist/api/server-hono.d.ts.map +1 -1
- package/dist/api/server-hono.js +55 -10
- package/dist/cli/assets/dexto-logo.svg +31 -0
- package/dist/cli/auth/api-client.d.ts +49 -0
- package/dist/cli/auth/api-client.d.ts.map +1 -0
- package/dist/cli/auth/api-client.js +127 -0
- package/dist/cli/auth/constants.d.ts +27 -0
- package/dist/cli/auth/constants.d.ts.map +1 -0
- package/dist/cli/auth/constants.js +28 -0
- package/dist/cli/auth/index.d.ts +5 -0
- package/dist/cli/auth/index.d.ts.map +1 -0
- package/dist/cli/auth/index.js +6 -0
- package/dist/cli/auth/oauth.d.ts +26 -0
- package/dist/cli/auth/oauth.d.ts.map +1 -0
- package/dist/cli/auth/oauth.js +327 -0
- package/dist/cli/auth/service.d.ts +20 -0
- package/dist/cli/auth/service.d.ts.map +1 -0
- package/dist/cli/auth/service.js +147 -0
- package/dist/cli/commands/auth/index.d.ts +4 -0
- package/dist/cli/commands/auth/index.d.ts.map +1 -0
- package/dist/cli/commands/auth/index.js +4 -0
- package/dist/cli/commands/auth/login.d.ts +9 -0
- package/dist/cli/commands/auth/login.d.ts.map +1 -0
- package/dist/cli/commands/auth/login.js +255 -0
- package/dist/cli/commands/auth/logout.d.ts +5 -0
- package/dist/cli/commands/auth/logout.d.ts.map +1 -0
- package/dist/cli/commands/auth/logout.js +51 -0
- package/dist/cli/commands/auth/status.d.ts +2 -0
- package/dist/cli/commands/auth/status.d.ts.map +1 -0
- package/dist/cli/commands/auth/status.js +22 -0
- package/dist/cli/commands/billing/index.d.ts +2 -0
- package/dist/cli/commands/billing/index.d.ts.map +1 -0
- package/dist/cli/commands/billing/index.js +2 -0
- package/dist/cli/commands/billing/status.d.ts +8 -0
- package/dist/cli/commands/billing/status.d.ts.map +1 -0
- package/dist/cli/commands/billing/status.js +82 -0
- package/dist/cli/commands/index.d.ts +3 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +8 -0
- package/dist/cli/commands/interactive-commands/auth/index.d.ts +12 -0
- package/dist/cli/commands/interactive-commands/auth/index.d.ts.map +1 -0
- package/dist/cli/commands/interactive-commands/auth/index.js +20 -0
- package/dist/cli/commands/interactive-commands/command-parser.d.ts +5 -0
- package/dist/cli/commands/interactive-commands/command-parser.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/command-parser.js +6 -0
- package/dist/cli/commands/interactive-commands/commands.d.ts +1 -0
- package/dist/cli/commands/interactive-commands/commands.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/commands.js +8 -0
- package/dist/cli/commands/interactive-commands/mcp/index.d.ts +2 -2
- package/dist/cli/commands/interactive-commands/mcp/index.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/mcp/index.js +4 -7
- package/dist/cli/commands/interactive-commands/model/index.d.ts +2 -2
- package/dist/cli/commands/interactive-commands/model/index.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/model/index.js +4 -7
- package/dist/cli/commands/interactive-commands/plugin/index.d.ts +13 -0
- package/dist/cli/commands/interactive-commands/plugin/index.d.ts.map +1 -0
- package/dist/cli/commands/interactive-commands/plugin/index.js +18 -0
- package/dist/cli/commands/interactive-commands/prompt-commands.d.ts +3 -1
- package/dist/cli/commands/interactive-commands/prompt-commands.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/prompt-commands.js +88 -36
- package/dist/cli/commands/list-agents.d.ts.map +1 -1
- package/dist/cli/commands/list-agents.js +3 -2
- package/dist/cli/commands/plugin.d.ts +161 -0
- package/dist/cli/commands/plugin.d.ts.map +1 -0
- package/dist/cli/commands/plugin.js +376 -0
- package/dist/cli/commands/setup.d.ts +9 -9
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +944 -176
- package/dist/cli/ink-cli/InkCLIRefactored.d.ts.map +1 -1
- package/dist/cli/ink-cli/InkCLIRefactored.js +13 -2
- package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts +1 -1
- package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/ApprovalPrompt.js +80 -12
- package/dist/cli/ink-cli/components/BackgroundTasksPanel.d.ts +18 -0
- package/dist/cli/ink-cli/components/BackgroundTasksPanel.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/BackgroundTasksPanel.js +48 -0
- package/dist/cli/ink-cli/components/ErrorBoundary.js +1 -1
- package/dist/cli/ink-cli/components/Footer.d.ts +2 -1
- package/dist/cli/ink-cli/components/Footer.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/Footer.js +6 -2
- package/dist/cli/ink-cli/components/ResourceAutocomplete.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/ResourceAutocomplete.js +150 -41
- package/dist/cli/ink-cli/components/SlashCommandAutocomplete.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/SlashCommandAutocomplete.js +15 -7
- package/dist/cli/ink-cli/components/StatusBar.d.ts +7 -1
- package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/StatusBar.js +18 -2
- package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/chat/MessageItem.js +23 -6
- package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +5 -1
- package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/modes/StaticCLI.js +5 -1
- package/dist/cli/ink-cli/components/overlays/ContextStatsOverlay.js +1 -1
- package/dist/cli/ink-cli/components/overlays/CustomModelWizard.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/CustomModelWizard.js +8 -4
- package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts +1 -0
- package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +31 -20
- package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.d.ts +20 -0
- package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.js +81 -0
- package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.d.ts +31 -0
- package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.js +297 -0
- package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.js +8 -2
- package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts +1 -1
- package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpServerActions.js +9 -0
- package/dist/cli/ink-cli/components/overlays/McpServerList.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/McpServerList.js +9 -2
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts +1 -0
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +150 -34
- package/dist/cli/ink-cli/components/overlays/PluginActions.d.ts +27 -0
- package/dist/cli/ink-cli/components/overlays/PluginActions.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/PluginActions.js +66 -0
- package/dist/cli/ink-cli/components/overlays/PluginList.d.ts +21 -0
- package/dist/cli/ink-cli/components/overlays/PluginList.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/PluginList.js +70 -0
- package/dist/cli/ink-cli/components/overlays/PluginManager.d.ts +21 -0
- package/dist/cli/ink-cli/components/overlays/PluginManager.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/PluginManager.js +63 -0
- package/dist/cli/ink-cli/components/overlays/PromptList.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/PromptList.js +4 -1
- package/dist/cli/ink-cli/components/overlays/ToolBrowser.d.ts +1 -0
- package/dist/cli/ink-cli/components/overlays/ToolBrowser.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/ToolBrowser.js +281 -39
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts +10 -1
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.js +87 -2
- package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts +4 -2
- package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.js +4 -4
- package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
- package/dist/cli/ink-cli/containers/InputContainer.js +32 -5
- package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
- package/dist/cli/ink-cli/containers/OverlayContainer.js +384 -28
- package/dist/cli/ink-cli/hooks/index.d.ts +1 -0
- package/dist/cli/ink-cli/hooks/index.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/index.js +1 -0
- package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useAgentEvents.js +61 -0
- package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useCLIState.js +5 -0
- package/dist/cli/ink-cli/hooks/useGitBranch.d.ts +13 -0
- package/dist/cli/ink-cli/hooks/useGitBranch.d.ts.map +1 -0
- package/dist/cli/ink-cli/hooks/useGitBranch.js +35 -0
- package/dist/cli/ink-cli/hooks/useInputOrchestrator.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useInputOrchestrator.js +53 -6
- package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
- package/dist/cli/ink-cli/services/processStream.js +92 -12
- package/dist/cli/ink-cli/state/initialState.d.ts.map +1 -1
- package/dist/cli/ink-cli/state/initialState.js +5 -0
- package/dist/cli/ink-cli/state/types.d.ts +12 -1
- package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
- package/dist/cli/ink-cli/utils/commandOverlays.d.ts.map +1 -1
- package/dist/cli/ink-cli/utils/commandOverlays.js +1 -0
- package/dist/cli/ink-cli/utils/llm-provider-display.d.ts +3 -0
- package/dist/cli/ink-cli/utils/llm-provider-display.d.ts.map +1 -0
- package/dist/cli/ink-cli/utils/llm-provider-display.js +22 -0
- package/dist/cli/ink-cli/utils/messageFormatting.d.ts +18 -7
- package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
- package/dist/cli/ink-cli/utils/messageFormatting.js +143 -15
- package/dist/cli/ink-cli/utils/toolUtils.d.ts +11 -0
- package/dist/cli/ink-cli/utils/toolUtils.d.ts.map +1 -1
- package/dist/cli/ink-cli/utils/toolUtils.js +17 -0
- package/dist/cli/mcp/index.d.ts +8 -0
- package/dist/cli/mcp/index.d.ts.map +1 -0
- package/dist/cli/mcp/index.js +7 -0
- package/dist/cli/mcp/oauth-factory.d.ts +6 -0
- package/dist/cli/mcp/oauth-factory.d.ts.map +1 -0
- package/dist/cli/mcp/oauth-factory.js +25 -0
- package/dist/cli/mcp/oauth-provider.d.ts +10 -0
- package/dist/cli/mcp/oauth-provider.d.ts.map +1 -0
- package/dist/cli/mcp/oauth-provider.js +77 -0
- package/dist/cli/mcp/oauth-redirect.d.ts +3 -0
- package/dist/cli/mcp/oauth-redirect.d.ts.map +1 -0
- package/dist/cli/mcp/oauth-redirect.js +4 -0
- package/dist/cli/mcp/oauth-server.d.ts +2 -0
- package/dist/cli/mcp/oauth-server.d.ts.map +1 -0
- package/dist/cli/mcp/oauth-server.js +70 -0
- package/dist/cli/mcp/oauth-store.d.ts +10 -0
- package/dist/cli/mcp/oauth-store.d.ts.map +1 -0
- package/dist/cli/mcp/oauth-store.js +27 -0
- package/dist/cli/mcp/oauth-ui.d.ts +2 -0
- package/dist/cli/mcp/oauth-ui.d.ts.map +1 -0
- package/dist/cli/mcp/oauth-ui.js +12 -0
- package/dist/cli/mcp/oauth-utils.d.ts +2 -0
- package/dist/cli/mcp/oauth-utils.d.ts.map +1 -0
- package/dist/cli/mcp/oauth-utils.js +17 -0
- package/dist/cli/utils/api-key-verification.d.ts.map +1 -1
- package/dist/cli/utils/api-key-verification.js +36 -0
- package/dist/cli/utils/config-validation.d.ts +3 -1
- package/dist/cli/utils/config-validation.d.ts.map +1 -1
- package/dist/cli/utils/config-validation.js +42 -19
- package/dist/cli/utils/dexto-auth-check.d.ts +53 -0
- package/dist/cli/utils/dexto-auth-check.d.ts.map +1 -0
- package/dist/cli/utils/dexto-auth-check.js +104 -0
- package/dist/cli/utils/dexto-setup.d.ts +8 -0
- package/dist/cli/utils/dexto-setup.d.ts.map +1 -0
- package/dist/cli/utils/dexto-setup.js +17 -0
- package/dist/cli/utils/options.d.ts.map +1 -1
- package/dist/cli/utils/options.js +5 -1
- package/dist/cli/utils/provider-setup.d.ts +5 -1
- package/dist/cli/utils/provider-setup.d.ts.map +1 -1
- package/dist/cli/utils/provider-setup.js +29 -1
- package/dist/config/cli-overrides.d.ts +17 -8
- package/dist/config/cli-overrides.d.ts.map +1 -1
- package/dist/config/cli-overrides.js +36 -22
- package/dist/config/effective-llm.d.ts +123 -0
- package/dist/config/effective-llm.d.ts.map +1 -0
- package/dist/config/effective-llm.js +171 -0
- package/dist/index.js +404 -99
- package/dist/webui/assets/index-C9JXwpvo.css +1 -0
- package/dist/webui/assets/{index-DVQWNLpT.js → index-Cz2z7NQ8.js} +218 -218
- package/dist/webui/index.html +2 -2
- package/package.json +9 -8
- package/dist/webui/assets/index-BglIVTSG.css +0 -1
package/dist/index.js
CHANGED
|
@@ -14,16 +14,32 @@ import { withAnalytics, safeExit, ExitSignal } from './analytics/wrapper.js';
|
|
|
14
14
|
// Use createRequire to import package.json without experimental warning
|
|
15
15
|
const require = createRequire(import.meta.url);
|
|
16
16
|
const pkg = require('../package.json');
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// Set CLI version for Dexto Gateway usage tracking
|
|
18
|
+
process.env.DEXTO_CLI_VERSION = pkg.version;
|
|
19
|
+
// Populate DEXTO_API_KEY for Dexto gateway routing
|
|
20
|
+
// Resolution order in getDextoApiKey():
|
|
21
|
+
// 1. Explicit env var (CI, testing, account override)
|
|
22
|
+
// 2. auth.json from `dexto login`
|
|
23
|
+
import { isDextoAuthEnabled } from '@dexto/agent-management';
|
|
24
|
+
if (isDextoAuthEnabled()) {
|
|
25
|
+
const { getDextoApiKey } = await import('./cli/auth/index.js');
|
|
26
|
+
const dextoApiKey = await getDextoApiKey();
|
|
27
|
+
if (dextoApiKey) {
|
|
28
|
+
process.env.DEXTO_API_KEY = dextoApiKey;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
import { logger, DextoLogger, FileTransport, DextoLogComponent, getProviderFromModel, getAllSupportedModels, startLlmRegistryAutoUpdate, DextoAgent, isPath, resolveApiKeyForProvider, getPrimaryApiKeyEnvVar, } from '@dexto/core';
|
|
32
|
+
import { resolveAgentPath, loadAgentConfig, globalPreferencesExist, loadGlobalPreferences, resolveBundledScript, getDextoPath, } from '@dexto/agent-management';
|
|
19
33
|
import { startHonoApiServer } from './api/server-hono.js';
|
|
20
34
|
import { validateCliOptions, handleCliOptionsError } from './cli/utils/options.js';
|
|
21
35
|
import { validateAgentConfig } from './cli/utils/config-validation.js';
|
|
22
|
-
import { applyCLIOverrides, applyUserPreferences
|
|
36
|
+
import { applyCLIOverrides, applyUserPreferences } from './config/cli-overrides.js';
|
|
23
37
|
import { enrichAgentConfig } from '@dexto/agent-management';
|
|
24
38
|
import { getPort } from './utils/port-utils.js';
|
|
25
39
|
import { createDextoProject, createImage, getUserInputToInitDextoApp, initDexto, postInitDexto, } from './cli/commands/index.js';
|
|
26
|
-
import { handleSetupCommand, handleInstallCommand, handleUninstallCommand, handleListAgentsCommand, handleWhichCommand, handleSyncAgentsCommand, shouldPromptForSync, markSyncDismissed, clearSyncDismissed,
|
|
40
|
+
import { handleSetupCommand, handleInstallCommand, handleUninstallCommand, handleListAgentsCommand, handleWhichCommand, handleSyncAgentsCommand, shouldPromptForSync, markSyncDismissed, clearSyncDismissed, handleLoginCommand, handleLogoutCommand, handleStatusCommand, handleBillingStatusCommand, handlePluginListCommand, handlePluginInstallCommand, handlePluginUninstallCommand, handlePluginValidateCommand,
|
|
41
|
+
// Marketplace handlers
|
|
42
|
+
handleMarketplaceAddCommand, handleMarketplaceRemoveCommand, handleMarketplaceUpdateCommand, handleMarketplaceListCommand, handleMarketplacePluginsCommand, handleMarketplaceInstallCommand, } from './cli/commands/index.js';
|
|
27
43
|
import { handleSessionListCommand, handleSessionHistoryCommand, handleSessionDeleteCommand, handleSessionSearchCommand, } from './cli/commands/session-commands.js';
|
|
28
44
|
import { requiresSetup } from './cli/utils/setup-utils.js';
|
|
29
45
|
import { checkForFileInCurrentDirectory, FileNotFoundError } from './cli/utils/package-mgmt.js';
|
|
@@ -38,6 +54,9 @@ await initAnalytics({ appVersion: pkg.version });
|
|
|
38
54
|
// Start version check early (non-blocking)
|
|
39
55
|
// We'll check the result later and display notification for interactive modes
|
|
40
56
|
const versionCheckPromise = checkForUpdates(pkg.version);
|
|
57
|
+
// Start self-updating LLM registry refresh (models.dev + OpenRouter mapping).
|
|
58
|
+
// Uses a cached snapshot on disk and refreshes in the background.
|
|
59
|
+
startLlmRegistryAutoUpdate();
|
|
41
60
|
/**
|
|
42
61
|
* Recursively removes null values from an object.
|
|
43
62
|
* This handles YAML files that have explicit `apiKey: null` entries
|
|
@@ -294,30 +313,203 @@ program
|
|
|
294
313
|
safeExit('sync-agents', 1, 'error');
|
|
295
314
|
}
|
|
296
315
|
}));
|
|
316
|
+
// 11) `plugin` SUB-COMMAND
|
|
317
|
+
const pluginCommand = program.command('plugin').description('Manage plugins');
|
|
318
|
+
pluginCommand
|
|
319
|
+
.command('list')
|
|
320
|
+
.description('List installed plugins')
|
|
321
|
+
.option('--verbose', 'Show detailed plugin information')
|
|
322
|
+
.action(withAnalytics('plugin list', async (options) => {
|
|
323
|
+
try {
|
|
324
|
+
await handlePluginListCommand(options);
|
|
325
|
+
safeExit('plugin list', 0);
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
if (err instanceof ExitSignal)
|
|
329
|
+
throw err;
|
|
330
|
+
console.error(`❌ dexto plugin list command failed: ${err}`);
|
|
331
|
+
safeExit('plugin list', 1, 'error');
|
|
332
|
+
}
|
|
333
|
+
}));
|
|
334
|
+
pluginCommand
|
|
335
|
+
.command('install')
|
|
336
|
+
.description('Install a plugin from a local directory')
|
|
337
|
+
.requiredOption('--path <path>', 'Path to the plugin directory')
|
|
338
|
+
.option('--scope <scope>', 'Installation scope: user, project, or local', 'user')
|
|
339
|
+
.option('--force', 'Force overwrite if already installed')
|
|
340
|
+
.action(withAnalytics('plugin install', async (options) => {
|
|
341
|
+
try {
|
|
342
|
+
await handlePluginInstallCommand(options);
|
|
343
|
+
safeExit('plugin install', 0);
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
if (err instanceof ExitSignal)
|
|
347
|
+
throw err;
|
|
348
|
+
console.error(`❌ dexto plugin install command failed: ${err}`);
|
|
349
|
+
safeExit('plugin install', 1, 'error');
|
|
350
|
+
}
|
|
351
|
+
}));
|
|
352
|
+
pluginCommand
|
|
353
|
+
.command('uninstall <name>')
|
|
354
|
+
.description('Uninstall a plugin by name')
|
|
355
|
+
.action(withAnalytics('plugin uninstall', async (name) => {
|
|
356
|
+
try {
|
|
357
|
+
await handlePluginUninstallCommand({ name });
|
|
358
|
+
safeExit('plugin uninstall', 0);
|
|
359
|
+
}
|
|
360
|
+
catch (err) {
|
|
361
|
+
if (err instanceof ExitSignal)
|
|
362
|
+
throw err;
|
|
363
|
+
console.error(`❌ dexto plugin uninstall command failed: ${err}`);
|
|
364
|
+
safeExit('plugin uninstall', 1, 'error');
|
|
365
|
+
}
|
|
366
|
+
}));
|
|
367
|
+
pluginCommand
|
|
368
|
+
.command('validate [path]')
|
|
369
|
+
.description('Validate a plugin directory structure')
|
|
370
|
+
.action(withAnalytics('plugin validate', async (path) => {
|
|
371
|
+
try {
|
|
372
|
+
await handlePluginValidateCommand({ path: path || '.' });
|
|
373
|
+
safeExit('plugin validate', 0);
|
|
374
|
+
}
|
|
375
|
+
catch (err) {
|
|
376
|
+
if (err instanceof ExitSignal)
|
|
377
|
+
throw err;
|
|
378
|
+
console.error(`❌ dexto plugin validate command failed: ${err}`);
|
|
379
|
+
safeExit('plugin validate', 1, 'error');
|
|
380
|
+
}
|
|
381
|
+
}));
|
|
382
|
+
// 12) `plugin marketplace` SUB-COMMANDS
|
|
383
|
+
const marketplaceCommand = pluginCommand
|
|
384
|
+
.command('marketplace')
|
|
385
|
+
.alias('market')
|
|
386
|
+
.description('Manage plugin marketplaces');
|
|
387
|
+
marketplaceCommand
|
|
388
|
+
.command('add <source>')
|
|
389
|
+
.description('Add a marketplace (GitHub: owner/repo, git URL, or local path)')
|
|
390
|
+
.option('--name <name>', 'Custom name for the marketplace')
|
|
391
|
+
.action(withAnalytics('plugin marketplace add', async (source, options) => {
|
|
392
|
+
try {
|
|
393
|
+
await handleMarketplaceAddCommand({ source, name: options.name });
|
|
394
|
+
safeExit('plugin marketplace add', 0);
|
|
395
|
+
}
|
|
396
|
+
catch (err) {
|
|
397
|
+
if (err instanceof ExitSignal)
|
|
398
|
+
throw err;
|
|
399
|
+
console.error(`❌ dexto plugin marketplace add command failed: ${err}`);
|
|
400
|
+
safeExit('plugin marketplace add', 1, 'error');
|
|
401
|
+
}
|
|
402
|
+
}));
|
|
403
|
+
marketplaceCommand
|
|
404
|
+
.command('list')
|
|
405
|
+
.description('List registered marketplaces')
|
|
406
|
+
.option('--verbose', 'Show detailed marketplace information')
|
|
407
|
+
.action(withAnalytics('plugin marketplace list', async (options) => {
|
|
408
|
+
try {
|
|
409
|
+
await handleMarketplaceListCommand(options);
|
|
410
|
+
safeExit('plugin marketplace list', 0);
|
|
411
|
+
}
|
|
412
|
+
catch (err) {
|
|
413
|
+
if (err instanceof ExitSignal)
|
|
414
|
+
throw err;
|
|
415
|
+
console.error(`❌ dexto plugin marketplace list command failed: ${err}`);
|
|
416
|
+
safeExit('plugin marketplace list', 1, 'error');
|
|
417
|
+
}
|
|
418
|
+
}));
|
|
419
|
+
marketplaceCommand
|
|
420
|
+
.command('remove <name>')
|
|
421
|
+
.alias('rm')
|
|
422
|
+
.description('Remove a registered marketplace')
|
|
423
|
+
.action(withAnalytics('plugin marketplace remove', async (name) => {
|
|
424
|
+
try {
|
|
425
|
+
await handleMarketplaceRemoveCommand({ name });
|
|
426
|
+
safeExit('plugin marketplace remove', 0);
|
|
427
|
+
}
|
|
428
|
+
catch (err) {
|
|
429
|
+
if (err instanceof ExitSignal)
|
|
430
|
+
throw err;
|
|
431
|
+
console.error(`❌ dexto plugin marketplace remove command failed: ${err}`);
|
|
432
|
+
safeExit('plugin marketplace remove', 1, 'error');
|
|
433
|
+
}
|
|
434
|
+
}));
|
|
435
|
+
marketplaceCommand
|
|
436
|
+
.command('update [name]')
|
|
437
|
+
.description('Update marketplace(s) from remote (git pull)')
|
|
438
|
+
.action(withAnalytics('plugin marketplace update', async (name) => {
|
|
439
|
+
try {
|
|
440
|
+
await handleMarketplaceUpdateCommand({ name });
|
|
441
|
+
safeExit('plugin marketplace update', 0);
|
|
442
|
+
}
|
|
443
|
+
catch (err) {
|
|
444
|
+
if (err instanceof ExitSignal)
|
|
445
|
+
throw err;
|
|
446
|
+
console.error(`❌ dexto plugin marketplace update command failed: ${err}`);
|
|
447
|
+
safeExit('plugin marketplace update', 1, 'error');
|
|
448
|
+
}
|
|
449
|
+
}));
|
|
450
|
+
marketplaceCommand
|
|
451
|
+
.command('plugins [marketplace]')
|
|
452
|
+
.description('List plugins available in marketplaces')
|
|
453
|
+
.option('--verbose', 'Show plugin descriptions')
|
|
454
|
+
.action(withAnalytics('plugin marketplace plugins', async (marketplace, options) => {
|
|
455
|
+
try {
|
|
456
|
+
await handleMarketplacePluginsCommand({
|
|
457
|
+
marketplace,
|
|
458
|
+
verbose: options?.verbose,
|
|
459
|
+
});
|
|
460
|
+
safeExit('plugin marketplace plugins', 0);
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
if (err instanceof ExitSignal)
|
|
464
|
+
throw err;
|
|
465
|
+
console.error(`❌ dexto plugin marketplace plugins command failed: ${err}`);
|
|
466
|
+
safeExit('plugin marketplace plugins', 1, 'error');
|
|
467
|
+
}
|
|
468
|
+
}));
|
|
469
|
+
marketplaceCommand
|
|
470
|
+
.command('install <plugin>')
|
|
471
|
+
.description('Install a plugin from marketplace (plugin or plugin@marketplace)')
|
|
472
|
+
.option('--scope <scope>', 'Installation scope: user, project, or local', 'user')
|
|
473
|
+
.option('--force', 'Force reinstall if already exists')
|
|
474
|
+
.action(withAnalytics('plugin marketplace install', async (plugin, options) => {
|
|
475
|
+
try {
|
|
476
|
+
await handleMarketplaceInstallCommand({ ...options, plugin });
|
|
477
|
+
safeExit('plugin marketplace install', 0);
|
|
478
|
+
}
|
|
479
|
+
catch (err) {
|
|
480
|
+
if (err instanceof ExitSignal)
|
|
481
|
+
throw err;
|
|
482
|
+
console.error(`❌ dexto plugin marketplace install command failed: ${err}`);
|
|
483
|
+
safeExit('plugin marketplace install', 1, 'error');
|
|
484
|
+
}
|
|
485
|
+
}));
|
|
297
486
|
// Helper to bootstrap a minimal agent for non-interactive session/search ops
|
|
298
487
|
async function bootstrapAgentFromGlobalOpts() {
|
|
299
488
|
const globalOpts = program.opts();
|
|
300
489
|
const resolvedPath = await resolveAgentPath(globalOpts.agent, globalOpts.autoInstall !== false);
|
|
301
490
|
const rawConfig = await loadAgentConfig(resolvedPath);
|
|
302
491
|
const mergedConfig = applyCLIOverrides(rawConfig, globalOpts);
|
|
303
|
-
|
|
304
|
-
logLevel: 'info', // CLI uses info-level logging for visibility
|
|
305
|
-
});
|
|
306
|
-
// Load image dynamically if specified (same priority as main command)
|
|
492
|
+
// Load image first to get bundled plugins
|
|
307
493
|
// Priority: CLI flag > Agent config > Environment variable > Default
|
|
308
|
-
// Images are optional, but default to image-local for convenience
|
|
309
494
|
const imageName = globalOpts.image || // --image flag
|
|
310
|
-
|
|
495
|
+
mergedConfig.image || // image field in agent config
|
|
311
496
|
process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
|
|
312
497
|
'@dexto/image-local'; // Default for convenience
|
|
498
|
+
let imageMetadata = null;
|
|
313
499
|
try {
|
|
314
|
-
await import(imageName);
|
|
500
|
+
const imageModule = await import(imageName);
|
|
501
|
+
imageMetadata = imageModule.imageMetadata || null;
|
|
315
502
|
}
|
|
316
503
|
catch (_err) {
|
|
317
504
|
console.error(`❌ Failed to load image '${imageName}'`);
|
|
318
505
|
console.error(`💡 Install it with: ${existsSync('package.json') ? 'npm install' : 'npm install -g'} ${imageName}`);
|
|
319
506
|
safeExit('bootstrap', 1, 'image-load-failed');
|
|
320
507
|
}
|
|
508
|
+
// Enrich config with bundled plugins from image
|
|
509
|
+
const enrichedConfig = enrichAgentConfig(mergedConfig, resolvedPath, {
|
|
510
|
+
logLevel: 'info', // CLI uses info-level logging for visibility
|
|
511
|
+
bundledPlugins: imageMetadata?.bundledPlugins || [],
|
|
512
|
+
});
|
|
321
513
|
// Override approval config for read-only commands (never run conversations)
|
|
322
514
|
// This avoids needing to set up unused approval handlers
|
|
323
515
|
enrichedConfig.toolConfirmation = {
|
|
@@ -469,7 +661,110 @@ program
|
|
|
469
661
|
safeExit('search', 1, 'error');
|
|
470
662
|
}
|
|
471
663
|
}));
|
|
472
|
-
// 13) `
|
|
664
|
+
// 13) `auth` SUB-COMMAND GROUP
|
|
665
|
+
const authCommand = program.command('auth').description('Manage authentication');
|
|
666
|
+
authCommand
|
|
667
|
+
.command('login')
|
|
668
|
+
.description('Login to Dexto')
|
|
669
|
+
.option('--api-key <key>', 'Use Dexto API key instead of browser login')
|
|
670
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
671
|
+
.action(withAnalytics('auth login', async (options) => {
|
|
672
|
+
try {
|
|
673
|
+
await handleLoginCommand(options);
|
|
674
|
+
safeExit('auth login', 0);
|
|
675
|
+
}
|
|
676
|
+
catch (err) {
|
|
677
|
+
if (err instanceof ExitSignal)
|
|
678
|
+
throw err;
|
|
679
|
+
console.error(`❌ dexto auth login command failed: ${err}`);
|
|
680
|
+
safeExit('auth login', 1, 'error');
|
|
681
|
+
}
|
|
682
|
+
}));
|
|
683
|
+
authCommand
|
|
684
|
+
.command('logout')
|
|
685
|
+
.description('Logout from Dexto')
|
|
686
|
+
.option('--force', 'Skip confirmation prompt')
|
|
687
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
688
|
+
.action(withAnalytics('auth logout', async (options) => {
|
|
689
|
+
try {
|
|
690
|
+
await handleLogoutCommand(options);
|
|
691
|
+
safeExit('auth logout', 0);
|
|
692
|
+
}
|
|
693
|
+
catch (err) {
|
|
694
|
+
if (err instanceof ExitSignal)
|
|
695
|
+
throw err;
|
|
696
|
+
console.error(`❌ dexto auth logout command failed: ${err}`);
|
|
697
|
+
safeExit('auth logout', 1, 'error');
|
|
698
|
+
}
|
|
699
|
+
}));
|
|
700
|
+
authCommand
|
|
701
|
+
.command('status')
|
|
702
|
+
.description('Show authentication status')
|
|
703
|
+
.action(withAnalytics('auth status', async () => {
|
|
704
|
+
try {
|
|
705
|
+
await handleStatusCommand();
|
|
706
|
+
safeExit('auth status', 0);
|
|
707
|
+
}
|
|
708
|
+
catch (err) {
|
|
709
|
+
if (err instanceof ExitSignal)
|
|
710
|
+
throw err;
|
|
711
|
+
console.error(`❌ dexto auth status command failed: ${err}`);
|
|
712
|
+
safeExit('auth status', 1, 'error');
|
|
713
|
+
}
|
|
714
|
+
}));
|
|
715
|
+
// Also add convenience aliases at root level
|
|
716
|
+
program
|
|
717
|
+
.command('login')
|
|
718
|
+
.description('Login to Dexto (alias for `dexto auth login`)')
|
|
719
|
+
.option('--api-key <key>', 'Use Dexto API key instead of browser login')
|
|
720
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
721
|
+
.action(withAnalytics('login', async (options) => {
|
|
722
|
+
try {
|
|
723
|
+
await handleLoginCommand(options);
|
|
724
|
+
safeExit('login', 0);
|
|
725
|
+
}
|
|
726
|
+
catch (err) {
|
|
727
|
+
if (err instanceof ExitSignal)
|
|
728
|
+
throw err;
|
|
729
|
+
console.error(`❌ dexto login command failed: ${err}`);
|
|
730
|
+
safeExit('login', 1, 'error');
|
|
731
|
+
}
|
|
732
|
+
}));
|
|
733
|
+
program
|
|
734
|
+
.command('logout')
|
|
735
|
+
.description('Logout from Dexto (alias for `dexto auth logout`)')
|
|
736
|
+
.option('--force', 'Skip confirmation prompt')
|
|
737
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
738
|
+
.action(withAnalytics('logout', async (options) => {
|
|
739
|
+
try {
|
|
740
|
+
await handleLogoutCommand(options);
|
|
741
|
+
safeExit('logout', 0);
|
|
742
|
+
}
|
|
743
|
+
catch (err) {
|
|
744
|
+
if (err instanceof ExitSignal)
|
|
745
|
+
throw err;
|
|
746
|
+
console.error(`❌ dexto logout command failed: ${err}`);
|
|
747
|
+
safeExit('logout', 1, 'error');
|
|
748
|
+
}
|
|
749
|
+
}));
|
|
750
|
+
// 14) `billing` COMMAND
|
|
751
|
+
program
|
|
752
|
+
.command('billing')
|
|
753
|
+
.description('Show billing status and credit balance')
|
|
754
|
+
.option('--buy', 'Open Dexto Nova credits purchase page')
|
|
755
|
+
.action(withAnalytics('billing', async (options) => {
|
|
756
|
+
try {
|
|
757
|
+
await handleBillingStatusCommand(options);
|
|
758
|
+
safeExit('billing', 0);
|
|
759
|
+
}
|
|
760
|
+
catch (err) {
|
|
761
|
+
if (err instanceof ExitSignal)
|
|
762
|
+
throw err;
|
|
763
|
+
console.error(`❌ dexto billing command failed: ${err}`);
|
|
764
|
+
safeExit('billing', 1, 'error');
|
|
765
|
+
}
|
|
766
|
+
}));
|
|
767
|
+
// 15) `mcp` SUB-COMMAND
|
|
473
768
|
// For now, this mode simply aggregates and re-expose tools from configured MCP servers (no agent)
|
|
474
769
|
// dexto --mode mcp will be moved to this sub-command in the future
|
|
475
770
|
program
|
|
@@ -522,7 +817,7 @@ program
|
|
|
522
817
|
safeExit('mcp', 1, 'mcp-agg-failed');
|
|
523
818
|
}
|
|
524
819
|
}, { timeoutMs: 0 }));
|
|
525
|
-
//
|
|
820
|
+
// 16) Main dexto CLI - Interactive/One shot (CLI/HEADLESS) or run in other modes (--mode web/server/mcp)
|
|
526
821
|
program
|
|
527
822
|
.argument('[prompt...]', 'Natural-language prompt to run once. If not passed, dexto will start as an interactive CLI')
|
|
528
823
|
// Main customer facing description
|
|
@@ -622,6 +917,11 @@ program
|
|
|
622
917
|
}
|
|
623
918
|
// ——— Infer provider & API key from model ———
|
|
624
919
|
if (opts.model) {
|
|
920
|
+
if (opts.model.includes('/')) {
|
|
921
|
+
console.error(`❌ Model '${opts.model}' looks like an OpenRouter-format ID (provider/model).`);
|
|
922
|
+
console.error(` This is ambiguous for --model inference. Please also pass --provider (e.g. --provider dexto-nova or --provider openrouter).`);
|
|
923
|
+
safeExit('main', 1, 'ambiguous-model');
|
|
924
|
+
}
|
|
625
925
|
let provider;
|
|
626
926
|
try {
|
|
627
927
|
provider = getProviderFromModel(opts.model);
|
|
@@ -716,9 +1016,9 @@ program
|
|
|
716
1016
|
const rawConfig = await loadAgentConfig(resolvedPath);
|
|
717
1017
|
let mergedConfig = applyCLIOverrides(rawConfig, opts);
|
|
718
1018
|
// ——— PREFERENCE-AWARE CONFIG HANDLING ———
|
|
719
|
-
//
|
|
720
|
-
//
|
|
721
|
-
const
|
|
1019
|
+
// User's LLM preferences from preferences.yml apply to ALL agents
|
|
1020
|
+
// See feature-plans/auto-update.md section 8.11 - Three-Layer LLM Resolution
|
|
1021
|
+
const agentId = opts.agent ?? 'coding-agent';
|
|
722
1022
|
let preferences = null;
|
|
723
1023
|
if (globalPreferencesExist()) {
|
|
724
1024
|
try {
|
|
@@ -729,10 +1029,42 @@ program
|
|
|
729
1029
|
logger.debug('Could not load preferences, continuing without them');
|
|
730
1030
|
}
|
|
731
1031
|
}
|
|
1032
|
+
// Check if user is configured for Dexto credits but not authenticated
|
|
1033
|
+
// This can happen if user logged out after setting up with Dexto
|
|
1034
|
+
// Now that preferences apply to ALL agents, we check for any agent
|
|
1035
|
+
// Only run this check when Dexto auth feature is enabled
|
|
1036
|
+
if (isDextoAuthEnabled()) {
|
|
1037
|
+
const { checkDextoAuthState } = await import('./cli/utils/dexto-auth-check.js');
|
|
1038
|
+
const authCheck = await checkDextoAuthState(opts.interactive !== false, agentId);
|
|
1039
|
+
if (!authCheck.shouldContinue) {
|
|
1040
|
+
if (authCheck.action === 'login') {
|
|
1041
|
+
// User wants to log in - run login flow then restart
|
|
1042
|
+
const { handleLoginCommand } = await import('./cli/commands/auth/login.js');
|
|
1043
|
+
await handleLoginCommand({ interactive: true });
|
|
1044
|
+
// Verify key was actually provisioned (provisionKeys silently catches errors)
|
|
1045
|
+
const { canUseDextoProvider } = await import('./cli/utils/dexto-setup.js');
|
|
1046
|
+
if (!(await canUseDextoProvider())) {
|
|
1047
|
+
console.error('\n❌ API key provisioning failed. Please try again or run `dexto setup` to use a different provider.\n');
|
|
1048
|
+
safeExit('main', 1, 'dexto-key-provisioning-failed');
|
|
1049
|
+
}
|
|
1050
|
+
// After login, continue with startup (preferences unchanged, now authenticated)
|
|
1051
|
+
}
|
|
1052
|
+
else if (authCheck.action === 'setup') {
|
|
1053
|
+
// User wants to configure different provider - run setup
|
|
1054
|
+
const { handleSetupCommand } = await import('./cli/commands/setup.js');
|
|
1055
|
+
await handleSetupCommand({ interactive: true, force: true });
|
|
1056
|
+
// Reload preferences after setup
|
|
1057
|
+
preferences = await loadGlobalPreferences();
|
|
1058
|
+
}
|
|
1059
|
+
else {
|
|
1060
|
+
// User cancelled
|
|
1061
|
+
safeExit('main', 0, 'dexto-auth-check-cancelled');
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
732
1065
|
// Check for pending API key setup (user skipped during initial setup)
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
opts.interactive !== false) {
|
|
1066
|
+
// Since preferences now apply to ALL agents, this check runs for any agent
|
|
1067
|
+
if (preferences?.setup?.apiKeyPending && opts.interactive !== false) {
|
|
736
1068
|
// Check if API key is still missing (user may have set it manually)
|
|
737
1069
|
const configuredApiKey = resolveApiKeyForProvider(preferences.llm.provider);
|
|
738
1070
|
if (!configuredApiKey) {
|
|
@@ -762,81 +1094,51 @@ program
|
|
|
762
1094
|
logger.debug('API key found in environment, cleared pending flag');
|
|
763
1095
|
}
|
|
764
1096
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
1097
|
+
// Apply user's LLM preferences to ALL agents (not just the default)
|
|
1098
|
+
// See feature-plans/auto-update.md section 8.11 - Three-Layer LLM Resolution:
|
|
1099
|
+
// local.llm ?? preferences.llm ?? bundled.llm
|
|
1100
|
+
// The preferences.llm acts as a "global .local.yml" for LLM settings
|
|
1101
|
+
if (preferences?.llm?.provider && preferences?.llm?.model) {
|
|
768
1102
|
mergedConfig = applyUserPreferences(mergedConfig, preferences);
|
|
769
|
-
logger.debug(
|
|
1103
|
+
logger.debug(`Applied user preferences to ${agentId}`, {
|
|
770
1104
|
provider: preferences.llm.provider,
|
|
771
1105
|
model: preferences.llm.model,
|
|
772
1106
|
});
|
|
773
1107
|
}
|
|
774
|
-
else if (!isDefaultAgent && mergedConfig.llm) {
|
|
775
|
-
// Specific agent: Check if user has the required provider configured
|
|
776
|
-
const agentProvider = mergedConfig.llm.provider;
|
|
777
|
-
const resolvedApiKey = resolveApiKeyForProvider(agentProvider);
|
|
778
|
-
const compatibility = checkAgentCompatibility(mergedConfig, preferences, resolvedApiKey);
|
|
779
|
-
if (!compatibility.compatible && opts.interactive !== false) {
|
|
780
|
-
// User is missing API key for the agent's provider
|
|
781
|
-
if (!compatibility.userHasApiKey &&
|
|
782
|
-
preferences?.llm?.provider &&
|
|
783
|
-
preferences?.llm?.model) {
|
|
784
|
-
// User has a default LLM configured - offer choice
|
|
785
|
-
const { promptForMissingAgentApiKey } = await import('./cli/utils/api-key-setup.js');
|
|
786
|
-
const result = await promptForMissingAgentApiKey(compatibility.agentProvider, compatibility.agentModel, preferences.llm.provider, preferences.llm.model);
|
|
787
|
-
if (result.action === 'cancel') {
|
|
788
|
-
safeExit('main', 0, 'agent-api-key-cancelled');
|
|
789
|
-
}
|
|
790
|
-
if (result.action === 'use-default') {
|
|
791
|
-
// Apply user's default LLM to the agent config
|
|
792
|
-
mergedConfig = applyUserPreferences(mergedConfig, preferences);
|
|
793
|
-
// Also resolve the actual API key from environment
|
|
794
|
-
// (preferences store env var reference like $GOOGLE_API_KEY, not the actual key)
|
|
795
|
-
const userApiKey = resolveApiKeyForProvider(preferences.llm.provider);
|
|
796
|
-
if (userApiKey) {
|
|
797
|
-
mergedConfig.llm.apiKey = userApiKey;
|
|
798
|
-
}
|
|
799
|
-
logger.debug('Applied user preferences to agent (user chose default)', {
|
|
800
|
-
provider: preferences.llm.provider,
|
|
801
|
-
model: preferences.llm.model,
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
if (result.action === 'add-key' && result.apiKey) {
|
|
805
|
-
// User added the API key - update the config
|
|
806
|
-
mergedConfig.llm.apiKey = result.apiKey;
|
|
807
|
-
logger.debug('Applied new API key to agent config');
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
else {
|
|
811
|
-
// No default LLM to fall back to - show warnings only
|
|
812
|
-
console.log(chalk.yellow('\n⚠️ Agent Compatibility Notice:'));
|
|
813
|
-
for (const warning of compatibility.warnings) {
|
|
814
|
-
console.log(chalk.yellow(` ${warning}`));
|
|
815
|
-
}
|
|
816
|
-
if (compatibility.instructions.length > 0) {
|
|
817
|
-
console.log(chalk.dim('\n To fix:'));
|
|
818
|
-
for (const instruction of compatibility.instructions) {
|
|
819
|
-
console.log(chalk.dim(` ${instruction}`));
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
console.log(''); // Empty line for spacing
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
1108
|
// Clean up null values from config (can happen from YAML files with explicit nulls)
|
|
827
1109
|
// This prevents "Expected string, received null" errors for optional fields
|
|
828
1110
|
const cleanedConfig = cleanNullValues(mergedConfig);
|
|
829
|
-
//
|
|
1111
|
+
// Load image first to get bundled plugins
|
|
1112
|
+
// Priority: CLI flag > Agent config > Environment variable > Default
|
|
1113
|
+
const imageNameForEnrichment = opts.image || // --image flag
|
|
1114
|
+
cleanedConfig.image || // image field in agent config
|
|
1115
|
+
process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
|
|
1116
|
+
'@dexto/image-local'; // Default for convenience
|
|
1117
|
+
let imageMetadataForEnrichment = null;
|
|
1118
|
+
try {
|
|
1119
|
+
const imageModule = await import(imageNameForEnrichment);
|
|
1120
|
+
imageMetadataForEnrichment = imageModule.imageMetadata || null;
|
|
1121
|
+
logger.debug(`Loaded image for enrichment: ${imageNameForEnrichment}`);
|
|
1122
|
+
}
|
|
1123
|
+
catch (err) {
|
|
1124
|
+
console.error(`❌ Failed to load image '${imageNameForEnrichment}'`);
|
|
1125
|
+
if (err instanceof Error) {
|
|
1126
|
+
logger.debug(`Image load error: ${err.message}`);
|
|
1127
|
+
}
|
|
1128
|
+
safeExit('main', 1, 'image-load-failed');
|
|
1129
|
+
}
|
|
1130
|
+
// Enrich config with per-agent paths and bundled plugins BEFORE validation
|
|
830
1131
|
// Enrichment adds filesystem paths to storage (schema has in-memory defaults)
|
|
831
1132
|
// Interactive CLI mode: only log to file (console would interfere with chat UI)
|
|
832
1133
|
const isInteractiveCli = opts.mode === 'cli' && !headlessInput;
|
|
833
1134
|
const enrichedConfig = enrichAgentConfig(cleanedConfig, resolvedPath, {
|
|
834
1135
|
isInteractiveCli,
|
|
835
1136
|
logLevel: 'info', // CLI uses info-level logging for visibility
|
|
1137
|
+
bundledPlugins: imageMetadataForEnrichment?.bundledPlugins || [],
|
|
836
1138
|
});
|
|
837
1139
|
// Validate enriched config with interactive setup if needed (for API key issues)
|
|
838
1140
|
// isInteractiveMode is defined above the try block
|
|
839
|
-
const validationResult = await validateAgentConfig(enrichedConfig, opts.interactive !== false, { strict: !isInteractiveMode });
|
|
1141
|
+
const validationResult = await validateAgentConfig(enrichedConfig, opts.interactive !== false, { strict: !isInteractiveMode, agentPath: resolvedPath });
|
|
840
1142
|
if (validationResult.success && validationResult.config) {
|
|
841
1143
|
validatedConfig = validationResult.config;
|
|
842
1144
|
}
|
|
@@ -852,31 +1154,13 @@ program
|
|
|
852
1154
|
// Validation failed and user didn't skip - show next steps and exit
|
|
853
1155
|
safeExit('main', 1, 'config-validation-failed');
|
|
854
1156
|
}
|
|
855
|
-
// ——— LOAD IMAGE DYNAMICALLY (if specified) ———
|
|
856
|
-
// Priority: CLI flag > Agent config > Environment variable > Default
|
|
857
|
-
// Images are optional, but default to image-local for convenience
|
|
858
|
-
const imageName = opts.image || // --image flag
|
|
859
|
-
validatedConfig.image || // image field in agent config
|
|
860
|
-
process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
|
|
861
|
-
'@dexto/image-local'; // Default for convenience
|
|
862
|
-
try {
|
|
863
|
-
await import(imageName);
|
|
864
|
-
logger.debug(`Loaded image: ${imageName}`);
|
|
865
|
-
}
|
|
866
|
-
catch (err) {
|
|
867
|
-
console.error(`❌ Failed to load image '${imageName}'`);
|
|
868
|
-
console.error(`💡 Install it with: ${existsSync('package.json') ? 'npm install' : 'npm install -g'} ${imageName}`);
|
|
869
|
-
if (err instanceof Error) {
|
|
870
|
-
logger.debug(`Image load error: ${err.message}`);
|
|
871
|
-
}
|
|
872
|
-
safeExit('main', 1, 'image-load-failed');
|
|
873
|
-
}
|
|
874
1157
|
// Validate that if config specifies an image, it matches what was loaded
|
|
875
1158
|
// Skip this check if user explicitly provided --image flag (intentional override)
|
|
1159
|
+
// Note: Image was already loaded earlier before enrichment
|
|
876
1160
|
if (!opts.image &&
|
|
877
1161
|
validatedConfig.image &&
|
|
878
|
-
validatedConfig.image !==
|
|
879
|
-
console.error(`❌ Config specifies image '${validatedConfig.image}' but '${
|
|
1162
|
+
validatedConfig.image !== imageNameForEnrichment) {
|
|
1163
|
+
console.error(`❌ Config specifies image '${validatedConfig.image}' but '${imageNameForEnrichment}' was loaded instead`);
|
|
880
1164
|
console.error(`💡 Either remove 'image' from config or ensure it matches the loaded image`);
|
|
881
1165
|
safeExit('main', 1, 'image-mismatch');
|
|
882
1166
|
}
|
|
@@ -932,8 +1216,29 @@ program
|
|
|
932
1216
|
// Config is already enriched and validated - ready for agent creation
|
|
933
1217
|
// DextoAgent will parse/validate again (parse-twice pattern)
|
|
934
1218
|
// isInteractiveMode is already defined above for validateAgentConfig
|
|
1219
|
+
const sessionLoggerFactory = ({ baseLogger, agentId, sessionId, }) => {
|
|
1220
|
+
// Sanitize sessionId to prevent path traversal attacks
|
|
1221
|
+
// Allow only alphanumeric, dots, hyphens, and underscores
|
|
1222
|
+
const safeSessionId = sessionId.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
1223
|
+
const logFilePath = getDextoPath('logs', path.join(agentId, `${safeSessionId}.log`));
|
|
1224
|
+
// Standalone per-session file logger.
|
|
1225
|
+
return new DextoLogger({
|
|
1226
|
+
level: baseLogger.getLevel(),
|
|
1227
|
+
agentId,
|
|
1228
|
+
sessionId,
|
|
1229
|
+
component: DextoLogComponent.SESSION,
|
|
1230
|
+
transports: [new FileTransport({ path: logFilePath })],
|
|
1231
|
+
});
|
|
1232
|
+
};
|
|
1233
|
+
const mcpAuthProviderFactory = opts.mode === 'cli'
|
|
1234
|
+
? (await import('./cli/mcp/oauth-factory.js')).createMcpAuthProviderFactory({
|
|
1235
|
+
logger,
|
|
1236
|
+
})
|
|
1237
|
+
: null;
|
|
935
1238
|
agent = new DextoAgent(validatedConfig, resolvedPath, {
|
|
936
1239
|
strict: !isInteractiveMode,
|
|
1240
|
+
sessionLoggerFactory,
|
|
1241
|
+
mcpAuthProviderFactory,
|
|
937
1242
|
});
|
|
938
1243
|
// Start the agent (initialize async services)
|
|
939
1244
|
// - web/server modes: initializeHonoApi will set approval handler and start the agent
|
|
@@ -1283,5 +1588,5 @@ program
|
|
|
1283
1588
|
safeExit('main', 1, 'unknown-mode');
|
|
1284
1589
|
}
|
|
1285
1590
|
}, { timeoutMs: 0 }));
|
|
1286
|
-
//
|
|
1591
|
+
// 17) PARSE & EXECUTE
|
|
1287
1592
|
program.parseAsync(process.argv);
|