dexto 1.5.5 → 1.5.7
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/agent-template.yml +1 -1
- package/dist/agents/coding-agent/coding-agent.yml +17 -2
- package/dist/agents/coding-agent/skills/code-review.md +46 -0
- package/dist/agents/explore-agent/explore-agent.yml +2 -0
- package/dist/agents/podcast-agent/podcast-agent.yml +1 -1
- 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 +23 -0
- package/dist/cli/auth/constants.d.ts.map +1 -0
- package/dist/cli/auth/constants.js +24 -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 +6 -0
- package/dist/cli/commands/billing/status.d.ts.map +1 -0
- package/dist/cli/commands/billing/status.js +60 -0
- package/dist/cli/commands/index.d.ts +4 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +9 -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 +10 -0
- package/dist/cli/commands/interactive-commands/export/index.d.ts +13 -0
- package/dist/cli/commands/interactive-commands/export/index.d.ts.map +1 -0
- package/dist/cli/commands/interactive-commands/export/index.js +21 -0
- package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/general-commands.js +1 -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 +72 -36
- package/dist/cli/commands/interactive-commands/system/system-commands.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/system/system-commands.js +2 -3
- 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 +325 -37
- package/dist/cli/commands/sync-agents.d.ts +44 -0
- package/dist/cli/commands/sync-agents.d.ts.map +1 -0
- package/dist/cli/commands/sync-agents.js +483 -0
- package/dist/cli/ink-cli/InkCLIRefactored.d.ts +14 -1
- package/dist/cli/ink-cli/InkCLIRefactored.d.ts.map +1 -1
- package/dist/cli/ink-cli/InkCLIRefactored.js +8 -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/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/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 +9 -1
- package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/StatusBar.js +17 -5
- package/dist/cli/ink-cli/components/TodoPanel.d.ts +11 -8
- package/dist/cli/ink-cli/components/TodoPanel.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/TodoPanel.js +38 -36
- package/dist/cli/ink-cli/components/chat/Header.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/chat/Header.js +1 -1
- package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/chat/MessageItem.js +14 -1
- package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.js +1 -1
- package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +16 -4
- package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/modes/StaticCLI.js +4 -1
- package/dist/cli/ink-cli/components/overlays/ExportWizard.d.ts +22 -0
- package/dist/cli/ink-cli/components/overlays/ExportWizard.d.ts.map +1 -0
- package/dist/cli/ink-cli/components/overlays/ExportWizard.js +308 -0
- 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 +7 -1
- 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.map +1 -1
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +15 -2
- 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/custom-model-wizard/provider-config.d.ts +2 -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 +61 -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/constants/tips.js +2 -2
- package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
- package/dist/cli/ink-cli/containers/InputContainer.js +31 -3
- package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
- package/dist/cli/ink-cli/containers/OverlayContainer.js +260 -11
- 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/useCLIState.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useCLIState.js +3 -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 +50 -6
- package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
- package/dist/cli/ink-cli/services/processStream.js +42 -10
- package/dist/cli/ink-cli/state/initialState.d.ts.map +1 -1
- package/dist/cli/ink-cli/state/initialState.js +3 -0
- package/dist/cli/ink-cli/state/types.d.ts +16 -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 +2 -0
- package/dist/cli/ink-cli/utils/messageFormatting.d.ts +14 -1
- package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
- package/dist/cli/ink-cli/utils/messageFormatting.js +68 -8
- 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-setup.d.ts.map +1 -1
- package/dist/cli/utils/api-key-setup.js +13 -90
- 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 +4 -0
- package/dist/cli/utils/provider-setup.d.ts.map +1 -1
- package/dist/cli/utils/provider-setup.js +20 -0
- package/dist/cli/utils/version-check.d.ts +45 -0
- package/dist/cli/utils/version-check.d.ts.map +1 -0
- package/dist/cli/utils/version-check.js +195 -0
- 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 +451 -126
- package/dist/webui/assets/index-C9JXwpvo.css +1 -0
- package/dist/webui/assets/{index-DVQWNLpT.js → index-Dl3mj53P.js} +217 -217
- 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,19 +14,36 @@ 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, 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,
|
|
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';
|
|
46
|
+
import { checkForUpdates, displayUpdateNotification } from './cli/utils/version-check.js';
|
|
30
47
|
import { resolveWebRoot } from './web.js';
|
|
31
48
|
import { initializeMcpServer, createMcpTransport } from '@dexto/server';
|
|
32
49
|
import { createAgentCard } from '@dexto/core';
|
|
@@ -34,6 +51,9 @@ import { initializeMcpToolAggregationServer } from './api/mcp/tool-aggregation-h
|
|
|
34
51
|
const program = new Command();
|
|
35
52
|
// Initialize analytics early (no-op if disabled)
|
|
36
53
|
await initAnalytics({ appVersion: pkg.version });
|
|
54
|
+
// Start version check early (non-blocking)
|
|
55
|
+
// We'll check the result later and display notification for interactive modes
|
|
56
|
+
const versionCheckPromise = checkForUpdates(pkg.version);
|
|
37
57
|
/**
|
|
38
58
|
* Recursively removes null values from an object.
|
|
39
59
|
* This handles YAML files that have explicit `apiKey: null` entries
|
|
@@ -89,7 +109,6 @@ program
|
|
|
89
109
|
.option('--port <port>', 'port for the server (default: 3000 for web, 3001 for server mode)')
|
|
90
110
|
.option('--no-auto-install', 'Disable automatic installation of missing agents from registry')
|
|
91
111
|
.option('--image <package>', 'Image package to load (e.g., @dexto/image-local). Overrides config image field.')
|
|
92
|
-
.option('--privacy-mode', 'Hide full file paths from display (useful for screen recording/sharing). Can also set DEXTO_PRIVACY_MODE=true')
|
|
93
112
|
.option('--dev', '[maintainers] Use local ./agents instead of ~/.dexto (for dexto repo development)')
|
|
94
113
|
.enablePositionalOptions();
|
|
95
114
|
// 2) `create-app` SUB-COMMAND
|
|
@@ -190,12 +209,7 @@ program
|
|
|
190
209
|
catch (err) {
|
|
191
210
|
if (err instanceof ExitSignal)
|
|
192
211
|
throw err;
|
|
193
|
-
|
|
194
|
-
console.error(`❌ dexto setup command failed: ${err}. Check logs for more information`);
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
console.error(`❌ dexto setup command failed: ${err}. Check logs in ~/.dexto/logs/dexto.log for more information`);
|
|
198
|
-
}
|
|
212
|
+
console.error(`❌ dexto setup command failed: ${err}. Check logs in ~/.dexto/logs/dexto.log for more information`);
|
|
199
213
|
safeExit('setup', 1, 'error');
|
|
200
214
|
}
|
|
201
215
|
}));
|
|
@@ -278,30 +292,221 @@ program
|
|
|
278
292
|
safeExit('which', 1, 'error');
|
|
279
293
|
}
|
|
280
294
|
}));
|
|
295
|
+
// 10) `sync-agents` SUB-COMMAND
|
|
296
|
+
program
|
|
297
|
+
.command('sync-agents')
|
|
298
|
+
.description('Sync installed agents with bundled versions')
|
|
299
|
+
.option('--list', 'List agent status without updating')
|
|
300
|
+
.option('--force', 'Update all agents without prompting')
|
|
301
|
+
.action(withAnalytics('sync-agents', async (options) => {
|
|
302
|
+
try {
|
|
303
|
+
await handleSyncAgentsCommand(options);
|
|
304
|
+
safeExit('sync-agents', 0);
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
if (err instanceof ExitSignal)
|
|
308
|
+
throw err;
|
|
309
|
+
console.error(`❌ dexto sync-agents command failed: ${err}`);
|
|
310
|
+
safeExit('sync-agents', 1, 'error');
|
|
311
|
+
}
|
|
312
|
+
}));
|
|
313
|
+
// 11) `plugin` SUB-COMMAND
|
|
314
|
+
const pluginCommand = program.command('plugin').description('Manage plugins');
|
|
315
|
+
pluginCommand
|
|
316
|
+
.command('list')
|
|
317
|
+
.description('List installed plugins')
|
|
318
|
+
.option('--verbose', 'Show detailed plugin information')
|
|
319
|
+
.action(withAnalytics('plugin list', async (options) => {
|
|
320
|
+
try {
|
|
321
|
+
await handlePluginListCommand(options);
|
|
322
|
+
safeExit('plugin list', 0);
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
if (err instanceof ExitSignal)
|
|
326
|
+
throw err;
|
|
327
|
+
console.error(`❌ dexto plugin list command failed: ${err}`);
|
|
328
|
+
safeExit('plugin list', 1, 'error');
|
|
329
|
+
}
|
|
330
|
+
}));
|
|
331
|
+
pluginCommand
|
|
332
|
+
.command('install')
|
|
333
|
+
.description('Install a plugin from a local directory')
|
|
334
|
+
.requiredOption('--path <path>', 'Path to the plugin directory')
|
|
335
|
+
.option('--scope <scope>', 'Installation scope: user, project, or local', 'user')
|
|
336
|
+
.option('--force', 'Force overwrite if already installed')
|
|
337
|
+
.action(withAnalytics('plugin install', async (options) => {
|
|
338
|
+
try {
|
|
339
|
+
await handlePluginInstallCommand(options);
|
|
340
|
+
safeExit('plugin install', 0);
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
if (err instanceof ExitSignal)
|
|
344
|
+
throw err;
|
|
345
|
+
console.error(`❌ dexto plugin install command failed: ${err}`);
|
|
346
|
+
safeExit('plugin install', 1, 'error');
|
|
347
|
+
}
|
|
348
|
+
}));
|
|
349
|
+
pluginCommand
|
|
350
|
+
.command('uninstall <name>')
|
|
351
|
+
.description('Uninstall a plugin by name')
|
|
352
|
+
.action(withAnalytics('plugin uninstall', async (name) => {
|
|
353
|
+
try {
|
|
354
|
+
await handlePluginUninstallCommand({ name });
|
|
355
|
+
safeExit('plugin uninstall', 0);
|
|
356
|
+
}
|
|
357
|
+
catch (err) {
|
|
358
|
+
if (err instanceof ExitSignal)
|
|
359
|
+
throw err;
|
|
360
|
+
console.error(`❌ dexto plugin uninstall command failed: ${err}`);
|
|
361
|
+
safeExit('plugin uninstall', 1, 'error');
|
|
362
|
+
}
|
|
363
|
+
}));
|
|
364
|
+
pluginCommand
|
|
365
|
+
.command('validate [path]')
|
|
366
|
+
.description('Validate a plugin directory structure')
|
|
367
|
+
.action(withAnalytics('plugin validate', async (path) => {
|
|
368
|
+
try {
|
|
369
|
+
await handlePluginValidateCommand({ path: path || '.' });
|
|
370
|
+
safeExit('plugin validate', 0);
|
|
371
|
+
}
|
|
372
|
+
catch (err) {
|
|
373
|
+
if (err instanceof ExitSignal)
|
|
374
|
+
throw err;
|
|
375
|
+
console.error(`❌ dexto plugin validate command failed: ${err}`);
|
|
376
|
+
safeExit('plugin validate', 1, 'error');
|
|
377
|
+
}
|
|
378
|
+
}));
|
|
379
|
+
// 12) `plugin marketplace` SUB-COMMANDS
|
|
380
|
+
const marketplaceCommand = pluginCommand
|
|
381
|
+
.command('marketplace')
|
|
382
|
+
.alias('market')
|
|
383
|
+
.description('Manage plugin marketplaces');
|
|
384
|
+
marketplaceCommand
|
|
385
|
+
.command('add <source>')
|
|
386
|
+
.description('Add a marketplace (GitHub: owner/repo, git URL, or local path)')
|
|
387
|
+
.option('--name <name>', 'Custom name for the marketplace')
|
|
388
|
+
.action(withAnalytics('plugin marketplace add', async (source, options) => {
|
|
389
|
+
try {
|
|
390
|
+
await handleMarketplaceAddCommand({ source, name: options.name });
|
|
391
|
+
safeExit('plugin marketplace add', 0);
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
if (err instanceof ExitSignal)
|
|
395
|
+
throw err;
|
|
396
|
+
console.error(`❌ dexto plugin marketplace add command failed: ${err}`);
|
|
397
|
+
safeExit('plugin marketplace add', 1, 'error');
|
|
398
|
+
}
|
|
399
|
+
}));
|
|
400
|
+
marketplaceCommand
|
|
401
|
+
.command('list')
|
|
402
|
+
.description('List registered marketplaces')
|
|
403
|
+
.option('--verbose', 'Show detailed marketplace information')
|
|
404
|
+
.action(withAnalytics('plugin marketplace list', async (options) => {
|
|
405
|
+
try {
|
|
406
|
+
await handleMarketplaceListCommand(options);
|
|
407
|
+
safeExit('plugin marketplace list', 0);
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
if (err instanceof ExitSignal)
|
|
411
|
+
throw err;
|
|
412
|
+
console.error(`❌ dexto plugin marketplace list command failed: ${err}`);
|
|
413
|
+
safeExit('plugin marketplace list', 1, 'error');
|
|
414
|
+
}
|
|
415
|
+
}));
|
|
416
|
+
marketplaceCommand
|
|
417
|
+
.command('remove <name>')
|
|
418
|
+
.alias('rm')
|
|
419
|
+
.description('Remove a registered marketplace')
|
|
420
|
+
.action(withAnalytics('plugin marketplace remove', async (name) => {
|
|
421
|
+
try {
|
|
422
|
+
await handleMarketplaceRemoveCommand({ name });
|
|
423
|
+
safeExit('plugin marketplace remove', 0);
|
|
424
|
+
}
|
|
425
|
+
catch (err) {
|
|
426
|
+
if (err instanceof ExitSignal)
|
|
427
|
+
throw err;
|
|
428
|
+
console.error(`❌ dexto plugin marketplace remove command failed: ${err}`);
|
|
429
|
+
safeExit('plugin marketplace remove', 1, 'error');
|
|
430
|
+
}
|
|
431
|
+
}));
|
|
432
|
+
marketplaceCommand
|
|
433
|
+
.command('update [name]')
|
|
434
|
+
.description('Update marketplace(s) from remote (git pull)')
|
|
435
|
+
.action(withAnalytics('plugin marketplace update', async (name) => {
|
|
436
|
+
try {
|
|
437
|
+
await handleMarketplaceUpdateCommand({ name });
|
|
438
|
+
safeExit('plugin marketplace update', 0);
|
|
439
|
+
}
|
|
440
|
+
catch (err) {
|
|
441
|
+
if (err instanceof ExitSignal)
|
|
442
|
+
throw err;
|
|
443
|
+
console.error(`❌ dexto plugin marketplace update command failed: ${err}`);
|
|
444
|
+
safeExit('plugin marketplace update', 1, 'error');
|
|
445
|
+
}
|
|
446
|
+
}));
|
|
447
|
+
marketplaceCommand
|
|
448
|
+
.command('plugins [marketplace]')
|
|
449
|
+
.description('List plugins available in marketplaces')
|
|
450
|
+
.option('--verbose', 'Show plugin descriptions')
|
|
451
|
+
.action(withAnalytics('plugin marketplace plugins', async (marketplace, options) => {
|
|
452
|
+
try {
|
|
453
|
+
await handleMarketplacePluginsCommand({
|
|
454
|
+
marketplace,
|
|
455
|
+
verbose: options?.verbose,
|
|
456
|
+
});
|
|
457
|
+
safeExit('plugin marketplace plugins', 0);
|
|
458
|
+
}
|
|
459
|
+
catch (err) {
|
|
460
|
+
if (err instanceof ExitSignal)
|
|
461
|
+
throw err;
|
|
462
|
+
console.error(`❌ dexto plugin marketplace plugins command failed: ${err}`);
|
|
463
|
+
safeExit('plugin marketplace plugins', 1, 'error');
|
|
464
|
+
}
|
|
465
|
+
}));
|
|
466
|
+
marketplaceCommand
|
|
467
|
+
.command('install <plugin>')
|
|
468
|
+
.description('Install a plugin from marketplace (plugin or plugin@marketplace)')
|
|
469
|
+
.option('--scope <scope>', 'Installation scope: user, project, or local', 'user')
|
|
470
|
+
.option('--force', 'Force reinstall if already exists')
|
|
471
|
+
.action(withAnalytics('plugin marketplace install', async (plugin, options) => {
|
|
472
|
+
try {
|
|
473
|
+
await handleMarketplaceInstallCommand({ ...options, plugin });
|
|
474
|
+
safeExit('plugin marketplace install', 0);
|
|
475
|
+
}
|
|
476
|
+
catch (err) {
|
|
477
|
+
if (err instanceof ExitSignal)
|
|
478
|
+
throw err;
|
|
479
|
+
console.error(`❌ dexto plugin marketplace install command failed: ${err}`);
|
|
480
|
+
safeExit('plugin marketplace install', 1, 'error');
|
|
481
|
+
}
|
|
482
|
+
}));
|
|
281
483
|
// Helper to bootstrap a minimal agent for non-interactive session/search ops
|
|
282
484
|
async function bootstrapAgentFromGlobalOpts() {
|
|
283
485
|
const globalOpts = program.opts();
|
|
284
486
|
const resolvedPath = await resolveAgentPath(globalOpts.agent, globalOpts.autoInstall !== false);
|
|
285
487
|
const rawConfig = await loadAgentConfig(resolvedPath);
|
|
286
488
|
const mergedConfig = applyCLIOverrides(rawConfig, globalOpts);
|
|
287
|
-
|
|
288
|
-
logLevel: 'info', // CLI uses info-level logging for visibility
|
|
289
|
-
});
|
|
290
|
-
// Load image dynamically if specified (same priority as main command)
|
|
489
|
+
// Load image first to get bundled plugins
|
|
291
490
|
// Priority: CLI flag > Agent config > Environment variable > Default
|
|
292
|
-
// Images are optional, but default to image-local for convenience
|
|
293
491
|
const imageName = globalOpts.image || // --image flag
|
|
294
|
-
|
|
492
|
+
mergedConfig.image || // image field in agent config
|
|
295
493
|
process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
|
|
296
494
|
'@dexto/image-local'; // Default for convenience
|
|
495
|
+
let imageMetadata = null;
|
|
297
496
|
try {
|
|
298
|
-
await import(imageName);
|
|
497
|
+
const imageModule = await import(imageName);
|
|
498
|
+
imageMetadata = imageModule.imageMetadata || null;
|
|
299
499
|
}
|
|
300
500
|
catch (_err) {
|
|
301
501
|
console.error(`❌ Failed to load image '${imageName}'`);
|
|
302
502
|
console.error(`💡 Install it with: ${existsSync('package.json') ? 'npm install' : 'npm install -g'} ${imageName}`);
|
|
303
503
|
safeExit('bootstrap', 1, 'image-load-failed');
|
|
304
504
|
}
|
|
505
|
+
// Enrich config with bundled plugins from image
|
|
506
|
+
const enrichedConfig = enrichAgentConfig(mergedConfig, resolvedPath, {
|
|
507
|
+
logLevel: 'info', // CLI uses info-level logging for visibility
|
|
508
|
+
bundledPlugins: imageMetadata?.bundledPlugins || [],
|
|
509
|
+
});
|
|
305
510
|
// Override approval config for read-only commands (never run conversations)
|
|
306
511
|
// This avoids needing to set up unused approval handlers
|
|
307
512
|
enrichedConfig.toolConfirmation = {
|
|
@@ -356,7 +561,7 @@ async function getMostRecentSessionId(agent, includeHeadless = false) {
|
|
|
356
561
|
}
|
|
357
562
|
return mostRecentId;
|
|
358
563
|
}
|
|
359
|
-
//
|
|
564
|
+
// 11) `session` SUB-COMMAND
|
|
360
565
|
const sessionCommand = program.command('session').description('Manage chat sessions');
|
|
361
566
|
sessionCommand
|
|
362
567
|
.command('list')
|
|
@@ -411,7 +616,7 @@ sessionCommand
|
|
|
411
616
|
safeExit('session delete', 1, 'error');
|
|
412
617
|
}
|
|
413
618
|
}));
|
|
414
|
-
//
|
|
619
|
+
// 12) `search` SUB-COMMAND
|
|
415
620
|
program
|
|
416
621
|
.command('search')
|
|
417
622
|
.description('Search session history')
|
|
@@ -453,7 +658,109 @@ program
|
|
|
453
658
|
safeExit('search', 1, 'error');
|
|
454
659
|
}
|
|
455
660
|
}));
|
|
456
|
-
//
|
|
661
|
+
// 13) `auth` SUB-COMMAND GROUP
|
|
662
|
+
const authCommand = program.command('auth').description('Manage authentication');
|
|
663
|
+
authCommand
|
|
664
|
+
.command('login')
|
|
665
|
+
.description('Login to Dexto')
|
|
666
|
+
.option('--api-key <key>', 'Use Dexto API key instead of browser login')
|
|
667
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
668
|
+
.action(withAnalytics('auth login', async (options) => {
|
|
669
|
+
try {
|
|
670
|
+
await handleLoginCommand(options);
|
|
671
|
+
safeExit('auth login', 0);
|
|
672
|
+
}
|
|
673
|
+
catch (err) {
|
|
674
|
+
if (err instanceof ExitSignal)
|
|
675
|
+
throw err;
|
|
676
|
+
console.error(`❌ dexto auth login command failed: ${err}`);
|
|
677
|
+
safeExit('auth login', 1, 'error');
|
|
678
|
+
}
|
|
679
|
+
}));
|
|
680
|
+
authCommand
|
|
681
|
+
.command('logout')
|
|
682
|
+
.description('Logout from Dexto')
|
|
683
|
+
.option('--force', 'Skip confirmation prompt')
|
|
684
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
685
|
+
.action(withAnalytics('auth logout', async (options) => {
|
|
686
|
+
try {
|
|
687
|
+
await handleLogoutCommand(options);
|
|
688
|
+
safeExit('auth logout', 0);
|
|
689
|
+
}
|
|
690
|
+
catch (err) {
|
|
691
|
+
if (err instanceof ExitSignal)
|
|
692
|
+
throw err;
|
|
693
|
+
console.error(`❌ dexto auth logout command failed: ${err}`);
|
|
694
|
+
safeExit('auth logout', 1, 'error');
|
|
695
|
+
}
|
|
696
|
+
}));
|
|
697
|
+
authCommand
|
|
698
|
+
.command('status')
|
|
699
|
+
.description('Show authentication status')
|
|
700
|
+
.action(withAnalytics('auth status', async () => {
|
|
701
|
+
try {
|
|
702
|
+
await handleStatusCommand();
|
|
703
|
+
safeExit('auth status', 0);
|
|
704
|
+
}
|
|
705
|
+
catch (err) {
|
|
706
|
+
if (err instanceof ExitSignal)
|
|
707
|
+
throw err;
|
|
708
|
+
console.error(`❌ dexto auth status command failed: ${err}`);
|
|
709
|
+
safeExit('auth status', 1, 'error');
|
|
710
|
+
}
|
|
711
|
+
}));
|
|
712
|
+
// Also add convenience aliases at root level
|
|
713
|
+
program
|
|
714
|
+
.command('login')
|
|
715
|
+
.description('Login to Dexto (alias for `dexto auth login`)')
|
|
716
|
+
.option('--api-key <key>', 'Use Dexto API key instead of browser login')
|
|
717
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
718
|
+
.action(withAnalytics('login', async (options) => {
|
|
719
|
+
try {
|
|
720
|
+
await handleLoginCommand(options);
|
|
721
|
+
safeExit('login', 0);
|
|
722
|
+
}
|
|
723
|
+
catch (err) {
|
|
724
|
+
if (err instanceof ExitSignal)
|
|
725
|
+
throw err;
|
|
726
|
+
console.error(`❌ dexto login command failed: ${err}`);
|
|
727
|
+
safeExit('login', 1, 'error');
|
|
728
|
+
}
|
|
729
|
+
}));
|
|
730
|
+
program
|
|
731
|
+
.command('logout')
|
|
732
|
+
.description('Logout from Dexto (alias for `dexto auth logout`)')
|
|
733
|
+
.option('--force', 'Skip confirmation prompt')
|
|
734
|
+
.option('--no-interactive', 'Disable interactive prompts')
|
|
735
|
+
.action(withAnalytics('logout', async (options) => {
|
|
736
|
+
try {
|
|
737
|
+
await handleLogoutCommand(options);
|
|
738
|
+
safeExit('logout', 0);
|
|
739
|
+
}
|
|
740
|
+
catch (err) {
|
|
741
|
+
if (err instanceof ExitSignal)
|
|
742
|
+
throw err;
|
|
743
|
+
console.error(`❌ dexto logout command failed: ${err}`);
|
|
744
|
+
safeExit('logout', 1, 'error');
|
|
745
|
+
}
|
|
746
|
+
}));
|
|
747
|
+
// 14) `billing` COMMAND
|
|
748
|
+
program
|
|
749
|
+
.command('billing')
|
|
750
|
+
.description('Show billing status and credit balance')
|
|
751
|
+
.action(withAnalytics('billing', async () => {
|
|
752
|
+
try {
|
|
753
|
+
await handleBillingStatusCommand();
|
|
754
|
+
safeExit('billing', 0);
|
|
755
|
+
}
|
|
756
|
+
catch (err) {
|
|
757
|
+
if (err instanceof ExitSignal)
|
|
758
|
+
throw err;
|
|
759
|
+
console.error(`❌ dexto billing command failed: ${err}`);
|
|
760
|
+
safeExit('billing', 1, 'error');
|
|
761
|
+
}
|
|
762
|
+
}));
|
|
763
|
+
// 15) `mcp` SUB-COMMAND
|
|
457
764
|
// For now, this mode simply aggregates and re-expose tools from configured MCP servers (no agent)
|
|
458
765
|
// dexto --mode mcp will be moved to this sub-command in the future
|
|
459
766
|
program
|
|
@@ -477,12 +784,7 @@ program
|
|
|
477
784
|
const globalOpts = program.opts();
|
|
478
785
|
const nameOrPath = globalOpts.agent;
|
|
479
786
|
const configPath = await resolveAgentPath(nameOrPath, globalOpts.autoInstall !== false);
|
|
480
|
-
|
|
481
|
-
console.log(`📄 Loading Dexto config...`);
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
console.log(`📄 Loading Dexto config from: ${configPath}`);
|
|
485
|
-
}
|
|
787
|
+
console.log(`📄 Loading Dexto config from: ${configPath}`);
|
|
486
788
|
const config = await loadAgentConfig(configPath);
|
|
487
789
|
logger.info(`Validating MCP servers...`);
|
|
488
790
|
// Validate that MCP servers are configured
|
|
@@ -511,7 +813,7 @@ program
|
|
|
511
813
|
safeExit('mcp', 1, 'mcp-agg-failed');
|
|
512
814
|
}
|
|
513
815
|
}, { timeoutMs: 0 }));
|
|
514
|
-
//
|
|
816
|
+
// 16) Main dexto CLI - Interactive/One shot (CLI/HEADLESS) or run in other modes (--mode web/server/mcp)
|
|
515
817
|
program
|
|
516
818
|
.argument('[prompt...]', 'Natural-language prompt to run once. If not passed, dexto will start as an interactive CLI')
|
|
517
819
|
// Main customer facing description
|
|
@@ -549,10 +851,6 @@ program
|
|
|
549
851
|
logger.debug('WARNING: .env file not found; copy .env.example and set your API keys.');
|
|
550
852
|
}
|
|
551
853
|
const opts = program.opts();
|
|
552
|
-
// Set privacy mode early to gate all path-related output
|
|
553
|
-
if (opts.privacyMode) {
|
|
554
|
-
process.env.DEXTO_PRIVACY_MODE = 'true';
|
|
555
|
-
}
|
|
556
854
|
// Set dev mode early to use local repo agents instead of ~/.dexto
|
|
557
855
|
if (opts.dev) {
|
|
558
856
|
process.env.DEXTO_DEV_MODE = 'true';
|
|
@@ -709,9 +1007,9 @@ program
|
|
|
709
1007
|
const rawConfig = await loadAgentConfig(resolvedPath);
|
|
710
1008
|
let mergedConfig = applyCLIOverrides(rawConfig, opts);
|
|
711
1009
|
// ——— PREFERENCE-AWARE CONFIG HANDLING ———
|
|
712
|
-
//
|
|
713
|
-
//
|
|
714
|
-
const
|
|
1010
|
+
// User's LLM preferences from preferences.yml apply to ALL agents
|
|
1011
|
+
// See feature-plans/auto-update.md section 8.11 - Three-Layer LLM Resolution
|
|
1012
|
+
const agentId = opts.agent ?? 'coding-agent';
|
|
715
1013
|
let preferences = null;
|
|
716
1014
|
if (globalPreferencesExist()) {
|
|
717
1015
|
try {
|
|
@@ -722,10 +1020,42 @@ program
|
|
|
722
1020
|
logger.debug('Could not load preferences, continuing without them');
|
|
723
1021
|
}
|
|
724
1022
|
}
|
|
1023
|
+
// Check if user is configured for Dexto credits but not authenticated
|
|
1024
|
+
// This can happen if user logged out after setting up with Dexto
|
|
1025
|
+
// Now that preferences apply to ALL agents, we check for any agent
|
|
1026
|
+
// Only run this check when Dexto auth feature is enabled
|
|
1027
|
+
if (isDextoAuthEnabled()) {
|
|
1028
|
+
const { checkDextoAuthState } = await import('./cli/utils/dexto-auth-check.js');
|
|
1029
|
+
const authCheck = await checkDextoAuthState(opts.interactive !== false, agentId);
|
|
1030
|
+
if (!authCheck.shouldContinue) {
|
|
1031
|
+
if (authCheck.action === 'login') {
|
|
1032
|
+
// User wants to log in - run login flow then restart
|
|
1033
|
+
const { handleLoginCommand } = await import('./cli/commands/auth/login.js');
|
|
1034
|
+
await handleLoginCommand({ interactive: true });
|
|
1035
|
+
// Verify key was actually provisioned (provisionKeys silently catches errors)
|
|
1036
|
+
const { canUseDextoProvider } = await import('./cli/utils/dexto-setup.js');
|
|
1037
|
+
if (!(await canUseDextoProvider())) {
|
|
1038
|
+
console.error('\n❌ API key provisioning failed. Please try again or run `dexto setup` to use a different provider.\n');
|
|
1039
|
+
safeExit('main', 1, 'dexto-key-provisioning-failed');
|
|
1040
|
+
}
|
|
1041
|
+
// After login, continue with startup (preferences unchanged, now authenticated)
|
|
1042
|
+
}
|
|
1043
|
+
else if (authCheck.action === 'setup') {
|
|
1044
|
+
// User wants to configure different provider - run setup
|
|
1045
|
+
const { handleSetupCommand } = await import('./cli/commands/setup.js');
|
|
1046
|
+
await handleSetupCommand({ interactive: true, force: true });
|
|
1047
|
+
// Reload preferences after setup
|
|
1048
|
+
preferences = await loadGlobalPreferences();
|
|
1049
|
+
}
|
|
1050
|
+
else {
|
|
1051
|
+
// User cancelled
|
|
1052
|
+
safeExit('main', 0, 'dexto-auth-check-cancelled');
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
725
1056
|
// Check for pending API key setup (user skipped during initial setup)
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
opts.interactive !== false) {
|
|
1057
|
+
// Since preferences now apply to ALL agents, this check runs for any agent
|
|
1058
|
+
if (preferences?.setup?.apiKeyPending && opts.interactive !== false) {
|
|
729
1059
|
// Check if API key is still missing (user may have set it manually)
|
|
730
1060
|
const configuredApiKey = resolveApiKeyForProvider(preferences.llm.provider);
|
|
731
1061
|
if (!configuredApiKey) {
|
|
@@ -755,81 +1085,51 @@ program
|
|
|
755
1085
|
logger.debug('API key found in environment, cleared pending flag');
|
|
756
1086
|
}
|
|
757
1087
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
1088
|
+
// Apply user's LLM preferences to ALL agents (not just the default)
|
|
1089
|
+
// See feature-plans/auto-update.md section 8.11 - Three-Layer LLM Resolution:
|
|
1090
|
+
// local.llm ?? preferences.llm ?? bundled.llm
|
|
1091
|
+
// The preferences.llm acts as a "global .local.yml" for LLM settings
|
|
1092
|
+
if (preferences?.llm?.provider && preferences?.llm?.model) {
|
|
761
1093
|
mergedConfig = applyUserPreferences(mergedConfig, preferences);
|
|
762
|
-
logger.debug(
|
|
1094
|
+
logger.debug(`Applied user preferences to ${agentId}`, {
|
|
763
1095
|
provider: preferences.llm.provider,
|
|
764
1096
|
model: preferences.llm.model,
|
|
765
1097
|
});
|
|
766
1098
|
}
|
|
767
|
-
else if (!isDefaultAgent && mergedConfig.llm) {
|
|
768
|
-
// Specific agent: Check if user has the required provider configured
|
|
769
|
-
const agentProvider = mergedConfig.llm.provider;
|
|
770
|
-
const resolvedApiKey = resolveApiKeyForProvider(agentProvider);
|
|
771
|
-
const compatibility = checkAgentCompatibility(mergedConfig, preferences, resolvedApiKey);
|
|
772
|
-
if (!compatibility.compatible && opts.interactive !== false) {
|
|
773
|
-
// User is missing API key for the agent's provider
|
|
774
|
-
if (!compatibility.userHasApiKey &&
|
|
775
|
-
preferences?.llm?.provider &&
|
|
776
|
-
preferences?.llm?.model) {
|
|
777
|
-
// User has a default LLM configured - offer choice
|
|
778
|
-
const { promptForMissingAgentApiKey } = await import('./cli/utils/api-key-setup.js');
|
|
779
|
-
const result = await promptForMissingAgentApiKey(compatibility.agentProvider, compatibility.agentModel, preferences.llm.provider, preferences.llm.model);
|
|
780
|
-
if (result.action === 'cancel') {
|
|
781
|
-
safeExit('main', 0, 'agent-api-key-cancelled');
|
|
782
|
-
}
|
|
783
|
-
if (result.action === 'use-default') {
|
|
784
|
-
// Apply user's default LLM to the agent config
|
|
785
|
-
mergedConfig = applyUserPreferences(mergedConfig, preferences);
|
|
786
|
-
// Also resolve the actual API key from environment
|
|
787
|
-
// (preferences store env var reference like $GOOGLE_API_KEY, not the actual key)
|
|
788
|
-
const userApiKey = resolveApiKeyForProvider(preferences.llm.provider);
|
|
789
|
-
if (userApiKey) {
|
|
790
|
-
mergedConfig.llm.apiKey = userApiKey;
|
|
791
|
-
}
|
|
792
|
-
logger.debug('Applied user preferences to agent (user chose default)', {
|
|
793
|
-
provider: preferences.llm.provider,
|
|
794
|
-
model: preferences.llm.model,
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
|
-
if (result.action === 'add-key' && result.apiKey) {
|
|
798
|
-
// User added the API key - update the config
|
|
799
|
-
mergedConfig.llm.apiKey = result.apiKey;
|
|
800
|
-
logger.debug('Applied new API key to agent config');
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
804
|
-
// No default LLM to fall back to - show warnings only
|
|
805
|
-
console.log(chalk.yellow('\n⚠️ Agent Compatibility Notice:'));
|
|
806
|
-
for (const warning of compatibility.warnings) {
|
|
807
|
-
console.log(chalk.yellow(` ${warning}`));
|
|
808
|
-
}
|
|
809
|
-
if (compatibility.instructions.length > 0) {
|
|
810
|
-
console.log(chalk.dim('\n To fix:'));
|
|
811
|
-
for (const instruction of compatibility.instructions) {
|
|
812
|
-
console.log(chalk.dim(` ${instruction}`));
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
console.log(''); // Empty line for spacing
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
1099
|
// Clean up null values from config (can happen from YAML files with explicit nulls)
|
|
820
1100
|
// This prevents "Expected string, received null" errors for optional fields
|
|
821
1101
|
const cleanedConfig = cleanNullValues(mergedConfig);
|
|
822
|
-
//
|
|
1102
|
+
// Load image first to get bundled plugins
|
|
1103
|
+
// Priority: CLI flag > Agent config > Environment variable > Default
|
|
1104
|
+
const imageNameForEnrichment = opts.image || // --image flag
|
|
1105
|
+
cleanedConfig.image || // image field in agent config
|
|
1106
|
+
process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
|
|
1107
|
+
'@dexto/image-local'; // Default for convenience
|
|
1108
|
+
let imageMetadataForEnrichment = null;
|
|
1109
|
+
try {
|
|
1110
|
+
const imageModule = await import(imageNameForEnrichment);
|
|
1111
|
+
imageMetadataForEnrichment = imageModule.imageMetadata || null;
|
|
1112
|
+
logger.debug(`Loaded image for enrichment: ${imageNameForEnrichment}`);
|
|
1113
|
+
}
|
|
1114
|
+
catch (err) {
|
|
1115
|
+
console.error(`❌ Failed to load image '${imageNameForEnrichment}'`);
|
|
1116
|
+
if (err instanceof Error) {
|
|
1117
|
+
logger.debug(`Image load error: ${err.message}`);
|
|
1118
|
+
}
|
|
1119
|
+
safeExit('main', 1, 'image-load-failed');
|
|
1120
|
+
}
|
|
1121
|
+
// Enrich config with per-agent paths and bundled plugins BEFORE validation
|
|
823
1122
|
// Enrichment adds filesystem paths to storage (schema has in-memory defaults)
|
|
824
1123
|
// Interactive CLI mode: only log to file (console would interfere with chat UI)
|
|
825
1124
|
const isInteractiveCli = opts.mode === 'cli' && !headlessInput;
|
|
826
1125
|
const enrichedConfig = enrichAgentConfig(cleanedConfig, resolvedPath, {
|
|
827
1126
|
isInteractiveCli,
|
|
828
1127
|
logLevel: 'info', // CLI uses info-level logging for visibility
|
|
1128
|
+
bundledPlugins: imageMetadataForEnrichment?.bundledPlugins || [],
|
|
829
1129
|
});
|
|
830
1130
|
// Validate enriched config with interactive setup if needed (for API key issues)
|
|
831
1131
|
// isInteractiveMode is defined above the try block
|
|
832
|
-
const validationResult = await validateAgentConfig(enrichedConfig, opts.interactive !== false, { strict: !isInteractiveMode });
|
|
1132
|
+
const validationResult = await validateAgentConfig(enrichedConfig, opts.interactive !== false, { strict: !isInteractiveMode, agentPath: resolvedPath });
|
|
833
1133
|
if (validationResult.success && validationResult.config) {
|
|
834
1134
|
validatedConfig = validationResult.config;
|
|
835
1135
|
}
|
|
@@ -845,31 +1145,13 @@ program
|
|
|
845
1145
|
// Validation failed and user didn't skip - show next steps and exit
|
|
846
1146
|
safeExit('main', 1, 'config-validation-failed');
|
|
847
1147
|
}
|
|
848
|
-
// ——— LOAD IMAGE DYNAMICALLY (if specified) ———
|
|
849
|
-
// Priority: CLI flag > Agent config > Environment variable > Default
|
|
850
|
-
// Images are optional, but default to image-local for convenience
|
|
851
|
-
const imageName = opts.image || // --image flag
|
|
852
|
-
validatedConfig.image || // image field in agent config
|
|
853
|
-
process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
|
|
854
|
-
'@dexto/image-local'; // Default for convenience
|
|
855
|
-
try {
|
|
856
|
-
await import(imageName);
|
|
857
|
-
logger.debug(`Loaded image: ${imageName}`);
|
|
858
|
-
}
|
|
859
|
-
catch (err) {
|
|
860
|
-
console.error(`❌ Failed to load image '${imageName}'`);
|
|
861
|
-
console.error(`💡 Install it with: ${existsSync('package.json') ? 'npm install' : 'npm install -g'} ${imageName}`);
|
|
862
|
-
if (err instanceof Error) {
|
|
863
|
-
logger.debug(`Image load error: ${err.message}`);
|
|
864
|
-
}
|
|
865
|
-
safeExit('main', 1, 'image-load-failed');
|
|
866
|
-
}
|
|
867
1148
|
// Validate that if config specifies an image, it matches what was loaded
|
|
868
1149
|
// Skip this check if user explicitly provided --image flag (intentional override)
|
|
1150
|
+
// Note: Image was already loaded earlier before enrichment
|
|
869
1151
|
if (!opts.image &&
|
|
870
1152
|
validatedConfig.image &&
|
|
871
|
-
validatedConfig.image !==
|
|
872
|
-
console.error(`❌ Config specifies image '${validatedConfig.image}' but '${
|
|
1153
|
+
validatedConfig.image !== imageNameForEnrichment) {
|
|
1154
|
+
console.error(`❌ Config specifies image '${validatedConfig.image}' but '${imageNameForEnrichment}' was loaded instead`);
|
|
873
1155
|
console.error(`💡 Either remove 'image' from config or ensure it matches the loaded image`);
|
|
874
1156
|
safeExit('main', 1, 'image-mismatch');
|
|
875
1157
|
}
|
|
@@ -913,13 +1195,6 @@ program
|
|
|
913
1195
|
let agent;
|
|
914
1196
|
let derivedAgentId;
|
|
915
1197
|
try {
|
|
916
|
-
// Show startup message (respecting privacy mode)
|
|
917
|
-
if (process.env.DEXTO_PRIVACY_MODE === 'true') {
|
|
918
|
-
console.error(`🚀 Initializing Dexto...`);
|
|
919
|
-
}
|
|
920
|
-
else {
|
|
921
|
-
console.error(`🚀 Initializing Dexto with config: ${resolvedPath}`);
|
|
922
|
-
}
|
|
923
1198
|
// Set run mode for tool confirmation provider
|
|
924
1199
|
process.env.DEXTO_RUN_MODE = opts.mode;
|
|
925
1200
|
// Apply --strict flag to all server configs
|
|
@@ -932,8 +1207,29 @@ program
|
|
|
932
1207
|
// Config is already enriched and validated - ready for agent creation
|
|
933
1208
|
// DextoAgent will parse/validate again (parse-twice pattern)
|
|
934
1209
|
// isInteractiveMode is already defined above for validateAgentConfig
|
|
1210
|
+
const sessionLoggerFactory = ({ baseLogger, agentId, sessionId, }) => {
|
|
1211
|
+
// Sanitize sessionId to prevent path traversal attacks
|
|
1212
|
+
// Allow only alphanumeric, dots, hyphens, and underscores
|
|
1213
|
+
const safeSessionId = sessionId.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
1214
|
+
const logFilePath = getDextoPath('logs', path.join(agentId, `${safeSessionId}.log`));
|
|
1215
|
+
// Standalone per-session file logger.
|
|
1216
|
+
return new DextoLogger({
|
|
1217
|
+
level: baseLogger.getLevel(),
|
|
1218
|
+
agentId,
|
|
1219
|
+
sessionId,
|
|
1220
|
+
component: DextoLogComponent.SESSION,
|
|
1221
|
+
transports: [new FileTransport({ path: logFilePath })],
|
|
1222
|
+
});
|
|
1223
|
+
};
|
|
1224
|
+
const mcpAuthProviderFactory = opts.mode === 'cli'
|
|
1225
|
+
? (await import('./cli/mcp/oauth-factory.js')).createMcpAuthProviderFactory({
|
|
1226
|
+
logger,
|
|
1227
|
+
})
|
|
1228
|
+
: null;
|
|
935
1229
|
agent = new DextoAgent(validatedConfig, resolvedPath, {
|
|
936
1230
|
strict: !isInteractiveMode,
|
|
1231
|
+
sessionLoggerFactory,
|
|
1232
|
+
mcpAuthProviderFactory,
|
|
937
1233
|
});
|
|
938
1234
|
// Start the agent (initialize async services)
|
|
939
1235
|
// - web/server modes: initializeHonoApi will set approval handler and start the agent
|
|
@@ -1104,6 +1400,23 @@ program
|
|
|
1104
1400
|
// Create session eagerly so slash commands work immediately
|
|
1105
1401
|
const session = await agent.createSession();
|
|
1106
1402
|
const cliSessionId = session.id;
|
|
1403
|
+
// Check for updates (will be shown in Ink header)
|
|
1404
|
+
const cliUpdateInfo = await versionCheckPromise;
|
|
1405
|
+
// Check if installed agents differ from bundled and prompt to sync
|
|
1406
|
+
const needsSync = await shouldPromptForSync(pkg.version);
|
|
1407
|
+
if (needsSync) {
|
|
1408
|
+
const shouldSync = await p.confirm({
|
|
1409
|
+
message: 'Agent config updates available. Sync now?',
|
|
1410
|
+
initialValue: true,
|
|
1411
|
+
});
|
|
1412
|
+
if (p.isCancel(shouldSync) || !shouldSync) {
|
|
1413
|
+
await markSyncDismissed(pkg.version);
|
|
1414
|
+
}
|
|
1415
|
+
else {
|
|
1416
|
+
await handleSyncAgentsCommand({ force: true, quiet: true });
|
|
1417
|
+
await clearSyncDismissed();
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1107
1420
|
// Interactive mode - use Ink CLI with session support
|
|
1108
1421
|
// Suppress console output before starting Ink UI
|
|
1109
1422
|
const originalConsole = {
|
|
@@ -1120,7 +1433,9 @@ program
|
|
|
1120
1433
|
let inkError = undefined;
|
|
1121
1434
|
try {
|
|
1122
1435
|
const { startInkCliRefactored } = await import('./cli/ink-cli/InkCLIRefactored.js');
|
|
1123
|
-
await startInkCliRefactored(agent, cliSessionId
|
|
1436
|
+
await startInkCliRefactored(agent, cliSessionId, {
|
|
1437
|
+
updateInfo: cliUpdateInfo ?? undefined,
|
|
1438
|
+
});
|
|
1124
1439
|
}
|
|
1125
1440
|
catch (error) {
|
|
1126
1441
|
inkError = error;
|
|
@@ -1173,6 +1488,11 @@ program
|
|
|
1173
1488
|
// Start single Hono server serving both API and WebUI
|
|
1174
1489
|
await startHonoApiServer(agent, port, agent.config.agentCard || {}, derivedAgentId, webRoot, webUIConfig);
|
|
1175
1490
|
console.log(chalk.green(`✅ Server running at ${serverUrl}`));
|
|
1491
|
+
// Show update notification if available
|
|
1492
|
+
const webUpdateInfo = await versionCheckPromise;
|
|
1493
|
+
if (webUpdateInfo) {
|
|
1494
|
+
displayUpdateNotification(webUpdateInfo);
|
|
1495
|
+
}
|
|
1176
1496
|
// Open WebUI in browser if webRoot is available
|
|
1177
1497
|
if (webRoot) {
|
|
1178
1498
|
try {
|
|
@@ -1204,6 +1524,11 @@ program
|
|
|
1204
1524
|
console.log(' POST /api/reset - Reset conversation');
|
|
1205
1525
|
console.log(' GET /api/mcp/servers - List MCP servers');
|
|
1206
1526
|
console.log(' SSE support available for real-time events');
|
|
1527
|
+
// Show update notification if available
|
|
1528
|
+
const serverUpdateInfo = await versionCheckPromise;
|
|
1529
|
+
if (serverUpdateInfo) {
|
|
1530
|
+
displayUpdateNotification(serverUpdateInfo);
|
|
1531
|
+
}
|
|
1207
1532
|
break;
|
|
1208
1533
|
}
|
|
1209
1534
|
// TODO: Remove if server mode is stable and supports mcp
|
|
@@ -1254,5 +1579,5 @@ program
|
|
|
1254
1579
|
safeExit('main', 1, 'unknown-mode');
|
|
1255
1580
|
}
|
|
1256
1581
|
}, { timeoutMs: 0 }));
|
|
1257
|
-
//
|
|
1582
|
+
// 17) PARSE & EXECUTE
|
|
1258
1583
|
program.parseAsync(process.argv);
|