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.
Files changed (226) hide show
  1. package/README.md +13 -0
  2. package/dist/agents/coding-agent/coding-agent.yml +17 -0
  3. package/dist/agents/coding-agent/skills/code-review.md +46 -0
  4. package/dist/agents/explore-agent/explore-agent.yml +2 -0
  5. package/dist/analytics/events.d.ts +1 -1
  6. package/dist/analytics/events.d.ts.map +1 -1
  7. package/dist/api/server-hono.d.ts.map +1 -1
  8. package/dist/api/server-hono.js +55 -10
  9. package/dist/cli/assets/dexto-logo.svg +31 -0
  10. package/dist/cli/auth/api-client.d.ts +49 -0
  11. package/dist/cli/auth/api-client.d.ts.map +1 -0
  12. package/dist/cli/auth/api-client.js +127 -0
  13. package/dist/cli/auth/constants.d.ts +27 -0
  14. package/dist/cli/auth/constants.d.ts.map +1 -0
  15. package/dist/cli/auth/constants.js +28 -0
  16. package/dist/cli/auth/index.d.ts +5 -0
  17. package/dist/cli/auth/index.d.ts.map +1 -0
  18. package/dist/cli/auth/index.js +6 -0
  19. package/dist/cli/auth/oauth.d.ts +26 -0
  20. package/dist/cli/auth/oauth.d.ts.map +1 -0
  21. package/dist/cli/auth/oauth.js +327 -0
  22. package/dist/cli/auth/service.d.ts +20 -0
  23. package/dist/cli/auth/service.d.ts.map +1 -0
  24. package/dist/cli/auth/service.js +147 -0
  25. package/dist/cli/commands/auth/index.d.ts +4 -0
  26. package/dist/cli/commands/auth/index.d.ts.map +1 -0
  27. package/dist/cli/commands/auth/index.js +4 -0
  28. package/dist/cli/commands/auth/login.d.ts +9 -0
  29. package/dist/cli/commands/auth/login.d.ts.map +1 -0
  30. package/dist/cli/commands/auth/login.js +255 -0
  31. package/dist/cli/commands/auth/logout.d.ts +5 -0
  32. package/dist/cli/commands/auth/logout.d.ts.map +1 -0
  33. package/dist/cli/commands/auth/logout.js +51 -0
  34. package/dist/cli/commands/auth/status.d.ts +2 -0
  35. package/dist/cli/commands/auth/status.d.ts.map +1 -0
  36. package/dist/cli/commands/auth/status.js +22 -0
  37. package/dist/cli/commands/billing/index.d.ts +2 -0
  38. package/dist/cli/commands/billing/index.d.ts.map +1 -0
  39. package/dist/cli/commands/billing/index.js +2 -0
  40. package/dist/cli/commands/billing/status.d.ts +8 -0
  41. package/dist/cli/commands/billing/status.d.ts.map +1 -0
  42. package/dist/cli/commands/billing/status.js +82 -0
  43. package/dist/cli/commands/index.d.ts +3 -0
  44. package/dist/cli/commands/index.d.ts.map +1 -1
  45. package/dist/cli/commands/index.js +8 -0
  46. package/dist/cli/commands/interactive-commands/auth/index.d.ts +12 -0
  47. package/dist/cli/commands/interactive-commands/auth/index.d.ts.map +1 -0
  48. package/dist/cli/commands/interactive-commands/auth/index.js +20 -0
  49. package/dist/cli/commands/interactive-commands/command-parser.d.ts +5 -0
  50. package/dist/cli/commands/interactive-commands/command-parser.d.ts.map +1 -1
  51. package/dist/cli/commands/interactive-commands/command-parser.js +6 -0
  52. package/dist/cli/commands/interactive-commands/commands.d.ts +1 -0
  53. package/dist/cli/commands/interactive-commands/commands.d.ts.map +1 -1
  54. package/dist/cli/commands/interactive-commands/commands.js +8 -0
  55. package/dist/cli/commands/interactive-commands/mcp/index.d.ts +2 -2
  56. package/dist/cli/commands/interactive-commands/mcp/index.d.ts.map +1 -1
  57. package/dist/cli/commands/interactive-commands/mcp/index.js +4 -7
  58. package/dist/cli/commands/interactive-commands/model/index.d.ts +2 -2
  59. package/dist/cli/commands/interactive-commands/model/index.d.ts.map +1 -1
  60. package/dist/cli/commands/interactive-commands/model/index.js +4 -7
  61. package/dist/cli/commands/interactive-commands/plugin/index.d.ts +13 -0
  62. package/dist/cli/commands/interactive-commands/plugin/index.d.ts.map +1 -0
  63. package/dist/cli/commands/interactive-commands/plugin/index.js +18 -0
  64. package/dist/cli/commands/interactive-commands/prompt-commands.d.ts +3 -1
  65. package/dist/cli/commands/interactive-commands/prompt-commands.d.ts.map +1 -1
  66. package/dist/cli/commands/interactive-commands/prompt-commands.js +88 -36
  67. package/dist/cli/commands/list-agents.d.ts.map +1 -1
  68. package/dist/cli/commands/list-agents.js +3 -2
  69. package/dist/cli/commands/plugin.d.ts +161 -0
  70. package/dist/cli/commands/plugin.d.ts.map +1 -0
  71. package/dist/cli/commands/plugin.js +376 -0
  72. package/dist/cli/commands/setup.d.ts +9 -9
  73. package/dist/cli/commands/setup.d.ts.map +1 -1
  74. package/dist/cli/commands/setup.js +944 -176
  75. package/dist/cli/ink-cli/InkCLIRefactored.d.ts.map +1 -1
  76. package/dist/cli/ink-cli/InkCLIRefactored.js +13 -2
  77. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts +1 -1
  78. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
  79. package/dist/cli/ink-cli/components/ApprovalPrompt.js +80 -12
  80. package/dist/cli/ink-cli/components/BackgroundTasksPanel.d.ts +18 -0
  81. package/dist/cli/ink-cli/components/BackgroundTasksPanel.d.ts.map +1 -0
  82. package/dist/cli/ink-cli/components/BackgroundTasksPanel.js +48 -0
  83. package/dist/cli/ink-cli/components/ErrorBoundary.js +1 -1
  84. package/dist/cli/ink-cli/components/Footer.d.ts +2 -1
  85. package/dist/cli/ink-cli/components/Footer.d.ts.map +1 -1
  86. package/dist/cli/ink-cli/components/Footer.js +6 -2
  87. package/dist/cli/ink-cli/components/ResourceAutocomplete.d.ts.map +1 -1
  88. package/dist/cli/ink-cli/components/ResourceAutocomplete.js +150 -41
  89. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.d.ts.map +1 -1
  90. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.js +15 -7
  91. package/dist/cli/ink-cli/components/StatusBar.d.ts +7 -1
  92. package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
  93. package/dist/cli/ink-cli/components/StatusBar.js +18 -2
  94. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  95. package/dist/cli/ink-cli/components/chat/MessageItem.js +23 -6
  96. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  97. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +5 -1
  98. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  99. package/dist/cli/ink-cli/components/modes/StaticCLI.js +5 -1
  100. package/dist/cli/ink-cli/components/overlays/ContextStatsOverlay.js +1 -1
  101. package/dist/cli/ink-cli/components/overlays/CustomModelWizard.d.ts.map +1 -1
  102. package/dist/cli/ink-cli/components/overlays/CustomModelWizard.js +8 -4
  103. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts +1 -0
  104. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts.map +1 -1
  105. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +31 -20
  106. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.d.ts +20 -0
  107. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.d.ts.map +1 -0
  108. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.js +81 -0
  109. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.d.ts +31 -0
  110. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.d.ts.map +1 -0
  111. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.js +297 -0
  112. package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.d.ts.map +1 -1
  113. package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.js +8 -2
  114. package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts +1 -1
  115. package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts.map +1 -1
  116. package/dist/cli/ink-cli/components/overlays/McpServerActions.js +9 -0
  117. package/dist/cli/ink-cli/components/overlays/McpServerList.d.ts.map +1 -1
  118. package/dist/cli/ink-cli/components/overlays/McpServerList.js +9 -2
  119. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts +1 -0
  120. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
  121. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +150 -34
  122. package/dist/cli/ink-cli/components/overlays/PluginActions.d.ts +27 -0
  123. package/dist/cli/ink-cli/components/overlays/PluginActions.d.ts.map +1 -0
  124. package/dist/cli/ink-cli/components/overlays/PluginActions.js +66 -0
  125. package/dist/cli/ink-cli/components/overlays/PluginList.d.ts +21 -0
  126. package/dist/cli/ink-cli/components/overlays/PluginList.d.ts.map +1 -0
  127. package/dist/cli/ink-cli/components/overlays/PluginList.js +70 -0
  128. package/dist/cli/ink-cli/components/overlays/PluginManager.d.ts +21 -0
  129. package/dist/cli/ink-cli/components/overlays/PluginManager.d.ts.map +1 -0
  130. package/dist/cli/ink-cli/components/overlays/PluginManager.js +63 -0
  131. package/dist/cli/ink-cli/components/overlays/PromptList.d.ts.map +1 -1
  132. package/dist/cli/ink-cli/components/overlays/PromptList.js +4 -1
  133. package/dist/cli/ink-cli/components/overlays/ToolBrowser.d.ts +1 -0
  134. package/dist/cli/ink-cli/components/overlays/ToolBrowser.d.ts.map +1 -1
  135. package/dist/cli/ink-cli/components/overlays/ToolBrowser.js +281 -39
  136. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts +10 -1
  137. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts.map +1 -1
  138. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.js +87 -2
  139. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts +4 -2
  140. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts.map +1 -1
  141. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.js +4 -4
  142. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  143. package/dist/cli/ink-cli/containers/InputContainer.js +32 -5
  144. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  145. package/dist/cli/ink-cli/containers/OverlayContainer.js +384 -28
  146. package/dist/cli/ink-cli/hooks/index.d.ts +1 -0
  147. package/dist/cli/ink-cli/hooks/index.d.ts.map +1 -1
  148. package/dist/cli/ink-cli/hooks/index.js +1 -0
  149. package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts.map +1 -1
  150. package/dist/cli/ink-cli/hooks/useAgentEvents.js +61 -0
  151. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  152. package/dist/cli/ink-cli/hooks/useCLIState.js +5 -0
  153. package/dist/cli/ink-cli/hooks/useGitBranch.d.ts +13 -0
  154. package/dist/cli/ink-cli/hooks/useGitBranch.d.ts.map +1 -0
  155. package/dist/cli/ink-cli/hooks/useGitBranch.js +35 -0
  156. package/dist/cli/ink-cli/hooks/useInputOrchestrator.d.ts.map +1 -1
  157. package/dist/cli/ink-cli/hooks/useInputOrchestrator.js +53 -6
  158. package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
  159. package/dist/cli/ink-cli/services/processStream.js +92 -12
  160. package/dist/cli/ink-cli/state/initialState.d.ts.map +1 -1
  161. package/dist/cli/ink-cli/state/initialState.js +5 -0
  162. package/dist/cli/ink-cli/state/types.d.ts +12 -1
  163. package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
  164. package/dist/cli/ink-cli/utils/commandOverlays.d.ts.map +1 -1
  165. package/dist/cli/ink-cli/utils/commandOverlays.js +1 -0
  166. package/dist/cli/ink-cli/utils/llm-provider-display.d.ts +3 -0
  167. package/dist/cli/ink-cli/utils/llm-provider-display.d.ts.map +1 -0
  168. package/dist/cli/ink-cli/utils/llm-provider-display.js +22 -0
  169. package/dist/cli/ink-cli/utils/messageFormatting.d.ts +18 -7
  170. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  171. package/dist/cli/ink-cli/utils/messageFormatting.js +143 -15
  172. package/dist/cli/ink-cli/utils/toolUtils.d.ts +11 -0
  173. package/dist/cli/ink-cli/utils/toolUtils.d.ts.map +1 -1
  174. package/dist/cli/ink-cli/utils/toolUtils.js +17 -0
  175. package/dist/cli/mcp/index.d.ts +8 -0
  176. package/dist/cli/mcp/index.d.ts.map +1 -0
  177. package/dist/cli/mcp/index.js +7 -0
  178. package/dist/cli/mcp/oauth-factory.d.ts +6 -0
  179. package/dist/cli/mcp/oauth-factory.d.ts.map +1 -0
  180. package/dist/cli/mcp/oauth-factory.js +25 -0
  181. package/dist/cli/mcp/oauth-provider.d.ts +10 -0
  182. package/dist/cli/mcp/oauth-provider.d.ts.map +1 -0
  183. package/dist/cli/mcp/oauth-provider.js +77 -0
  184. package/dist/cli/mcp/oauth-redirect.d.ts +3 -0
  185. package/dist/cli/mcp/oauth-redirect.d.ts.map +1 -0
  186. package/dist/cli/mcp/oauth-redirect.js +4 -0
  187. package/dist/cli/mcp/oauth-server.d.ts +2 -0
  188. package/dist/cli/mcp/oauth-server.d.ts.map +1 -0
  189. package/dist/cli/mcp/oauth-server.js +70 -0
  190. package/dist/cli/mcp/oauth-store.d.ts +10 -0
  191. package/dist/cli/mcp/oauth-store.d.ts.map +1 -0
  192. package/dist/cli/mcp/oauth-store.js +27 -0
  193. package/dist/cli/mcp/oauth-ui.d.ts +2 -0
  194. package/dist/cli/mcp/oauth-ui.d.ts.map +1 -0
  195. package/dist/cli/mcp/oauth-ui.js +12 -0
  196. package/dist/cli/mcp/oauth-utils.d.ts +2 -0
  197. package/dist/cli/mcp/oauth-utils.d.ts.map +1 -0
  198. package/dist/cli/mcp/oauth-utils.js +17 -0
  199. package/dist/cli/utils/api-key-verification.d.ts.map +1 -1
  200. package/dist/cli/utils/api-key-verification.js +36 -0
  201. package/dist/cli/utils/config-validation.d.ts +3 -1
  202. package/dist/cli/utils/config-validation.d.ts.map +1 -1
  203. package/dist/cli/utils/config-validation.js +42 -19
  204. package/dist/cli/utils/dexto-auth-check.d.ts +53 -0
  205. package/dist/cli/utils/dexto-auth-check.d.ts.map +1 -0
  206. package/dist/cli/utils/dexto-auth-check.js +104 -0
  207. package/dist/cli/utils/dexto-setup.d.ts +8 -0
  208. package/dist/cli/utils/dexto-setup.d.ts.map +1 -0
  209. package/dist/cli/utils/dexto-setup.js +17 -0
  210. package/dist/cli/utils/options.d.ts.map +1 -1
  211. package/dist/cli/utils/options.js +5 -1
  212. package/dist/cli/utils/provider-setup.d.ts +5 -1
  213. package/dist/cli/utils/provider-setup.d.ts.map +1 -1
  214. package/dist/cli/utils/provider-setup.js +29 -1
  215. package/dist/config/cli-overrides.d.ts +17 -8
  216. package/dist/config/cli-overrides.d.ts.map +1 -1
  217. package/dist/config/cli-overrides.js +36 -22
  218. package/dist/config/effective-llm.d.ts +123 -0
  219. package/dist/config/effective-llm.d.ts.map +1 -0
  220. package/dist/config/effective-llm.js +171 -0
  221. package/dist/index.js +404 -99
  222. package/dist/webui/assets/index-C9JXwpvo.css +1 -0
  223. package/dist/webui/assets/{index-DVQWNLpT.js → index-Cz2z7NQ8.js} +218 -218
  224. package/dist/webui/index.html +2 -2
  225. package/package.json +9 -8
  226. 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
- import { logger, getProviderFromModel, getAllSupportedModels, DextoAgent, isPath, resolveApiKeyForProvider, getPrimaryApiKeyEnvVar, } from '@dexto/core';
18
- import { resolveAgentPath, loadAgentConfig, globalPreferencesExist, loadGlobalPreferences, resolveBundledScript, } from '@dexto/agent-management';
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, checkAgentCompatibility, } from './config/cli-overrides.js';
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, } from './cli/commands/index.js';
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
- const enrichedConfig = enrichAgentConfig(mergedConfig, resolvedPath, {
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
- enrichedConfig.image || // image field in agent config
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) `mcp` SUB-COMMAND
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
- // 14) Main dexto CLI - Interactive/One shot (CLI/HEADLESS) or run in other modes (--mode web/server/mcp)
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
- // For coding-agent (no explicit agent specified): Apply user preferences
720
- // For specific agents: Check compatibility and warn if needed
721
- const isDefaultAgent = !opts.agent;
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
- if (isDefaultAgent &&
734
- preferences?.setup?.apiKeyPending &&
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
- if (isDefaultAgent && preferences) {
766
- // Default-agent: Apply user's LLM preferences at runtime
767
- // This ensures the base agent always uses user's preferred model/provider
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('Applied user preferences to coding-agent', {
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
- // Enrich config with per-agent paths BEFORE validation
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 !== imageName) {
879
- console.error(`❌ Config specifies image '${validatedConfig.image}' but '${imageName}' was loaded instead`);
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
- // 15) PARSE & EXECUTE
1591
+ // 17) PARSE & EXECUTE
1287
1592
  program.parseAsync(process.argv);