dexto 1.1.9 → 1.1.11
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 +4 -2
- package/dist/agents/agent-registry.json +30 -1
- package/dist/agents/database-agent/database-agent.yml +3 -0
- package/dist/agents/default-agent.yml +102 -12
- package/dist/agents/github-agent/github-agent.yml +3 -0
- package/dist/agents/image-editor-agent/image-editor-agent.yml +3 -0
- package/dist/agents/music-agent/music-agent.yml +3 -0
- package/dist/agents/nano-banana-agent/nano-banana-agent.yml +3 -0
- package/dist/agents/podcast-agent/podcast-agent.yml +3 -0
- package/dist/agents/product-name-researcher/product-name-researcher.yml +3 -0
- package/dist/agents/talk2pdf-agent/talk2pdf-agent.yml +3 -0
- package/dist/agents/triage-demo/triage-agent.yml +3 -0
- package/dist/api/mcp/tool-aggregation-handler.d.ts.map +1 -1
- package/dist/api/mcp/tool-aggregation-handler.js +34 -42
- package/dist/api/memory/memory-handler.d.ts +15 -0
- package/dist/api/memory/memory-handler.d.ts.map +1 -0
- package/dist/api/memory/memory-handler.js +129 -0
- package/dist/api/server.d.ts +2 -2
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +987 -231
- package/dist/api/webhook-subscriber.d.ts.map +1 -1
- package/dist/api/webhook-subscriber.js +2 -1
- package/dist/api/websocket-subscriber.d.ts.map +1 -1
- package/dist/api/websocket-subscriber.js +61 -10
- package/dist/cli/cli-subscriber.d.ts +2 -1
- package/dist/cli/cli-subscriber.d.ts.map +1 -1
- package/dist/cli/cli-subscriber.js +11 -3
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +1 -0
- package/dist/cli/commands/install.d.ts +3 -3
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +223 -41
- package/dist/cli/commands/interactive-commands/prompt-commands.d.ts +8 -1
- package/dist/cli/commands/interactive-commands/prompt-commands.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/prompt-commands.js +252 -4
- package/dist/cli/commands/list-agents.d.ts.map +1 -1
- package/dist/cli/commands/list-agents.js +22 -3
- package/dist/cli/commands/setup.d.ts +4 -4
- package/dist/cli/commands/uninstall.d.ts +1 -1
- package/dist/cli/tool-confirmation/cli-confirmation-handler.d.ts +36 -7
- package/dist/cli/tool-confirmation/cli-confirmation-handler.d.ts.map +1 -1
- package/dist/cli/tool-confirmation/cli-confirmation-handler.js +314 -34
- package/dist/index.js +57 -48
- package/dist/webui/.next/standalone/.next/static/VoeDi3iuGmMdZu_kx-R2X/_buildManifest.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/419-5526a47c95a2fa60.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/429-838829c1391e496d.js +25 -0
- package/dist/webui/.next/standalone/.next/static/chunks/459-62011998b002cbf6.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/711-76a7d2bf4d6f69e5.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/854-8cad9404fc78e0cc.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/935-07f9df196b13275e.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/app/chat/[sessionId]/page-b8acc47b0d8c5c0a.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/app/layout-f4a6ee5a028899d1.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/app/page-e117ae372850d25f.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/app/playground/page-09340fb6b3f4caa2.js +1 -0
- package/dist/webui/.next/standalone/.next/static/chunks/{webpack-7c234e7e7e272295.js → webpack-7229fd0786f0483c.js} +1 -1
- package/dist/webui/.next/standalone/.next/static/css/21e6c142ca3cdc42.css +1 -0
- package/dist/webui/.next/standalone/.next/static/css/de70bee13400563f.css +1 -0
- package/dist/webui/.next/standalone/package.json +6 -2
- package/dist/webui/.next/standalone/packages/webui/.next/BUILD_ID +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/app-build-manifest.json +30 -15
- package/dist/webui/.next/standalone/packages/webui/.next/app-path-routes-manifest.json +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/build-manifest.json +5 -5
- package/dist/webui/.next/standalone/packages/webui/.next/prerender-manifest.json +3 -3
- package/dist/webui/.next/standalone/packages/webui/.next/required-server-files.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/routes-manifest.json +10 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/chat/[sessionId]/page.js +2 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/chat/[sessionId]/page.js.nft.json +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/chat/[sessionId]/page_client-reference-manifest.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/page.js +2 -11
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/page.js.nft.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page.js +4 -4
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page.js.nft.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page_client-reference-manifest.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/app-paths-manifest.json +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/1.js +12 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/419.js +25 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/{619.js → 426.js} +2 -2
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/43.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/654.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/71.js +5 -0
- package/dist/webui/.next/standalone/packages/webui/.next/server/middleware-build-manifest.js +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/pages/500.html +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/pages-manifest.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/server-reference-manifest.json +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/VoeDi3iuGmMdZu_kx-R2X/_buildManifest.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/419-5526a47c95a2fa60.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/429-838829c1391e496d.js +25 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/459-62011998b002cbf6.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/711-76a7d2bf4d6f69e5.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/854-8cad9404fc78e0cc.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/935-07f9df196b13275e.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/chat/[sessionId]/page-b8acc47b0d8c5c0a.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/layout-f4a6ee5a028899d1.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/page-e117ae372850d25f.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/playground/page-09340fb6b3f4caa2.js +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/{webpack-7c234e7e7e272295.js → webpack-7229fd0786f0483c.js} +1 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/css/21e6c142ca3cdc42.css +1 -0
- package/dist/webui/.next/standalone/packages/webui/.next/static/css/de70bee13400563f.css +1 -0
- package/dist/webui/.next/standalone/packages/webui/package.json +7 -4
- package/dist/webui/.next/standalone/packages/webui/server.js +1 -1
- package/dist/webui/.next/static/VoeDi3iuGmMdZu_kx-R2X/_buildManifest.js +1 -0
- package/dist/webui/.next/static/chunks/419-5526a47c95a2fa60.js +1 -0
- package/dist/webui/.next/static/chunks/429-838829c1391e496d.js +25 -0
- package/dist/webui/.next/static/chunks/459-62011998b002cbf6.js +1 -0
- package/dist/webui/.next/static/chunks/711-76a7d2bf4d6f69e5.js +1 -0
- package/dist/webui/.next/static/chunks/854-8cad9404fc78e0cc.js +1 -0
- package/dist/webui/.next/static/chunks/935-07f9df196b13275e.js +1 -0
- package/dist/webui/.next/static/chunks/app/chat/[sessionId]/page-b8acc47b0d8c5c0a.js +1 -0
- package/dist/webui/.next/static/chunks/app/layout-f4a6ee5a028899d1.js +1 -0
- package/dist/webui/.next/static/chunks/app/page-e117ae372850d25f.js +1 -0
- package/dist/webui/.next/static/chunks/app/playground/page-09340fb6b3f4caa2.js +1 -0
- package/dist/webui/.next/static/chunks/{webpack-7c234e7e7e272295.js → webpack-7229fd0786f0483c.js} +1 -1
- package/dist/webui/.next/static/css/21e6c142ca3cdc42.css +1 -0
- package/dist/webui/.next/static/css/de70bee13400563f.css +1 -0
- package/dist/webui/package.json +7 -4
- package/package.json +5 -4
- package/dist/webui/.next/standalone/.next/static/aL3Gzk-qN3SiSHuntvic-/_buildManifest.js +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/179-78abc2eacbc41da9.js +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/442-b1916bec348454b3.js +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/544-c4a8f278ed1a25d7.js +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/854-2a6d5a5297a15d52.js +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/app/layout-dde711766eda096b.js +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/app/page-5e94d5a49dc718d0.js +0 -1
- package/dist/webui/.next/standalone/.next/static/chunks/app/playground/page-9ae40e0b219583e3.js +0 -1
- package/dist/webui/.next/standalone/.next/static/css/045cc65741e38fbd.css +0 -3
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/549.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/950.js +0 -5
- package/dist/webui/.next/standalone/packages/webui/.next/static/aL3Gzk-qN3SiSHuntvic-/_buildManifest.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/179-78abc2eacbc41da9.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/442-b1916bec348454b3.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/544-c4a8f278ed1a25d7.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/854-2a6d5a5297a15d52.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/layout-dde711766eda096b.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/page-5e94d5a49dc718d0.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/playground/page-9ae40e0b219583e3.js +0 -1
- package/dist/webui/.next/standalone/packages/webui/.next/static/css/045cc65741e38fbd.css +0 -3
- package/dist/webui/.next/static/aL3Gzk-qN3SiSHuntvic-/_buildManifest.js +0 -1
- package/dist/webui/.next/static/chunks/179-78abc2eacbc41da9.js +0 -1
- package/dist/webui/.next/static/chunks/442-b1916bec348454b3.js +0 -1
- package/dist/webui/.next/static/chunks/544-c4a8f278ed1a25d7.js +0 -1
- package/dist/webui/.next/static/chunks/854-2a6d5a5297a15d52.js +0 -1
- package/dist/webui/.next/static/chunks/app/layout-dde711766eda096b.js +0 -1
- package/dist/webui/.next/static/chunks/app/page-5e94d5a49dc718d0.js +0 -1
- package/dist/webui/.next/static/chunks/app/playground/page-9ae40e0b219583e3.js +0 -1
- package/dist/webui/.next/static/css/045cc65741e38fbd.css +0 -3
- /package/dist/webui/.next/standalone/.next/static/{aL3Gzk-qN3SiSHuntvic- → VoeDi3iuGmMdZu_kx-R2X}/_ssgManifest.js +0 -0
- /package/dist/webui/.next/standalone/packages/webui/.next/static/{aL3Gzk-qN3SiSHuntvic- → VoeDi3iuGmMdZu_kx-R2X}/_ssgManifest.js +0 -0
- /package/dist/webui/.next/static/{aL3Gzk-qN3SiSHuntvic- → VoeDi3iuGmMdZu_kx-R2X}/_ssgManifest.js +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { logger } from '@dexto/core';
|
|
1
|
+
import { logger, ApprovalType, ApprovalStatus } from '@dexto/core';
|
|
2
2
|
import * as readline from 'readline';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import boxen from 'boxen';
|
|
5
5
|
/**
|
|
6
|
-
* CLI-specific subscriber for
|
|
7
|
-
* Implements EventSubscriber pattern to listen to AgentEventBus for
|
|
6
|
+
* CLI-specific subscriber for approval events
|
|
7
|
+
* Implements EventSubscriber pattern to listen to AgentEventBus for approval requests
|
|
8
8
|
*/
|
|
9
9
|
export class CLIToolConfirmationSubscriber {
|
|
10
10
|
agentEventBus;
|
|
@@ -12,54 +12,334 @@ export class CLIToolConfirmationSubscriber {
|
|
|
12
12
|
// No configuration needed - CLI always shows interactive prompts
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Subscribe to
|
|
15
|
+
* Subscribe to approval events on the AgentEventBus
|
|
16
16
|
*/
|
|
17
17
|
subscribe(eventBus) {
|
|
18
18
|
this.agentEventBus = eventBus;
|
|
19
|
-
this.agentEventBus.on('dexto:
|
|
19
|
+
this.agentEventBus.on('dexto:approvalRequest', this.handleApprovalRequest.bind(this));
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Handle
|
|
22
|
+
* Handle approval request events from the AgentEventBus
|
|
23
23
|
*/
|
|
24
|
-
async
|
|
24
|
+
async handleApprovalRequest(event) {
|
|
25
25
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
approved,
|
|
35
|
-
rememberChoice: false, // CLI won't persist choice
|
|
36
|
-
...(event.sessionId && { sessionId: event.sessionId }),
|
|
37
|
-
};
|
|
38
|
-
this.sendConfirmationResponse(response);
|
|
39
|
-
if (!approved) {
|
|
40
|
-
logger.warn(`Tool '${event.toolName}' execution denied`);
|
|
26
|
+
if (event.type === ApprovalType.TOOL_CONFIRMATION) {
|
|
27
|
+
await this.handleToolConfirmation(event);
|
|
28
|
+
}
|
|
29
|
+
else if (event.type === ApprovalType.ELICITATION) {
|
|
30
|
+
await this.handleElicitation(event);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
logger.debug(`[CLI] Ignoring unsupported approval type: ${event.type}`);
|
|
41
34
|
}
|
|
42
35
|
}
|
|
43
36
|
catch (error) {
|
|
44
|
-
|
|
37
|
+
// Use structured logging to preserve error stack and metadata
|
|
38
|
+
logger.error(`Error handling approval request: ${error instanceof Error ? error.message : String(error)}`, error instanceof Error ? { error } : undefined);
|
|
45
39
|
// Send denial response on error
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
const errorResponse = {
|
|
41
|
+
approvalId: event.approvalId,
|
|
42
|
+
status: ApprovalStatus.DENIED,
|
|
43
|
+
};
|
|
44
|
+
if (event.sessionId !== undefined) {
|
|
45
|
+
errorResponse.sessionId = event.sessionId;
|
|
46
|
+
}
|
|
47
|
+
this.sendApprovalResponse(errorResponse);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Handle tool confirmation approval
|
|
52
|
+
*/
|
|
53
|
+
async handleToolConfirmation(event) {
|
|
54
|
+
const toolMetadata = event.metadata;
|
|
55
|
+
logger.info(`Handling tool confirmation request for ${toolMetadata.toolName}, approvalId: ${event.approvalId}`);
|
|
56
|
+
// Display tool call using the logger's built-in method
|
|
57
|
+
logger.toolCall(toolMetadata.toolName, toolMetadata.args);
|
|
58
|
+
// Collect user input with arrow key navigation
|
|
59
|
+
const approved = await this.collectArrowKeyInput();
|
|
60
|
+
// Send response back via AgentEventBus
|
|
61
|
+
const response = {
|
|
62
|
+
approvalId: event.approvalId,
|
|
63
|
+
status: approved ? ApprovalStatus.APPROVED : ApprovalStatus.DENIED,
|
|
64
|
+
data: {
|
|
65
|
+
rememberChoice: false, // CLI won't persist choice
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
if (event.sessionId !== undefined) {
|
|
69
|
+
response.sessionId = event.sessionId;
|
|
70
|
+
}
|
|
71
|
+
this.sendApprovalResponse(response);
|
|
72
|
+
if (!approved) {
|
|
73
|
+
logger.warn(`Tool '${toolMetadata.toolName}' execution denied`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Handle elicitation approval (form-based input from MCP servers)
|
|
78
|
+
*/
|
|
79
|
+
async handleElicitation(event) {
|
|
80
|
+
const elicitationMetadata = event.metadata;
|
|
81
|
+
logger.info(`Handling elicitation request from MCP server '${elicitationMetadata.serverName}', approvalId: ${event.approvalId}`);
|
|
82
|
+
// Display the elicitation prompt
|
|
83
|
+
console.log('\n' +
|
|
84
|
+
boxen(chalk.cyan.bold('📝 Information Request from MCP Server'), {
|
|
85
|
+
padding: 1,
|
|
86
|
+
margin: 1,
|
|
87
|
+
borderStyle: 'round',
|
|
88
|
+
borderColor: 'cyan',
|
|
89
|
+
}));
|
|
90
|
+
console.log(chalk.white(`Server: ${chalk.yellow(elicitationMetadata.serverName)}`));
|
|
91
|
+
console.log(chalk.white(`Message: ${chalk.white(elicitationMetadata.prompt)}\n`));
|
|
92
|
+
// Collect form data based on schema
|
|
93
|
+
const formData = await this.collectFormData(elicitationMetadata.schema);
|
|
94
|
+
if (formData === null) {
|
|
95
|
+
// User cancelled
|
|
96
|
+
const cancelResponse = {
|
|
97
|
+
approvalId: event.approvalId,
|
|
98
|
+
status: ApprovalStatus.CANCELLED,
|
|
99
|
+
};
|
|
100
|
+
if (event.sessionId !== undefined) {
|
|
101
|
+
cancelResponse.sessionId = event.sessionId;
|
|
102
|
+
}
|
|
103
|
+
this.sendApprovalResponse(cancelResponse);
|
|
104
|
+
logger.info('Elicitation cancelled by user');
|
|
51
105
|
}
|
|
106
|
+
else {
|
|
107
|
+
// User provided data
|
|
108
|
+
const response = {
|
|
109
|
+
approvalId: event.approvalId,
|
|
110
|
+
status: ApprovalStatus.APPROVED,
|
|
111
|
+
data: { formData },
|
|
112
|
+
};
|
|
113
|
+
if (event.sessionId !== undefined) {
|
|
114
|
+
response.sessionId = event.sessionId;
|
|
115
|
+
}
|
|
116
|
+
this.sendApprovalResponse(response);
|
|
117
|
+
logger.info('Elicitation completed successfully');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Collect form data based on JSON Schema
|
|
122
|
+
* Returns null if user cancels
|
|
123
|
+
*/
|
|
124
|
+
async collectFormData(schema) {
|
|
125
|
+
const formData = {};
|
|
126
|
+
if (!schema.properties || typeof schema.properties !== 'object') {
|
|
127
|
+
logger.warn('Invalid schema: no properties found');
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const properties = schema.properties;
|
|
131
|
+
const required = schema.required || [];
|
|
132
|
+
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
133
|
+
const field = fieldSchema;
|
|
134
|
+
const isRequired = required.includes(fieldName);
|
|
135
|
+
const fieldType = field.type || 'string';
|
|
136
|
+
// Display field prompt
|
|
137
|
+
const requiredLabel = isRequired ? chalk.red('*') : '';
|
|
138
|
+
console.log(chalk.cyan(`${fieldName}${requiredLabel}`) +
|
|
139
|
+
(field.description ? chalk.gray(` - ${field.description}`) : ''));
|
|
140
|
+
// Collect input based on field type
|
|
141
|
+
let value;
|
|
142
|
+
if (fieldType === 'boolean') {
|
|
143
|
+
value = await this.collectBooleanInput(fieldName, isRequired);
|
|
144
|
+
}
|
|
145
|
+
else if (fieldType === 'number' || fieldType === 'integer') {
|
|
146
|
+
value = await this.collectNumberInput(fieldName, fieldType === 'integer', isRequired);
|
|
147
|
+
}
|
|
148
|
+
else if (field.enum && Array.isArray(field.enum)) {
|
|
149
|
+
value = await this.collectEnumInput(fieldName, field.enum, isRequired);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// Default to string input
|
|
153
|
+
value = await this.collectStringInput(fieldName, isRequired);
|
|
154
|
+
}
|
|
155
|
+
if (value === null) {
|
|
156
|
+
// User cancelled
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
// Only assign if value is not undefined (allows skipping optional fields)
|
|
160
|
+
if (value !== undefined) {
|
|
161
|
+
formData[fieldName] = value;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return formData;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Collect string input from user
|
|
168
|
+
*/
|
|
169
|
+
collectStringInput(fieldName, required) {
|
|
170
|
+
return new Promise((resolve) => {
|
|
171
|
+
const rl = readline.createInterface({
|
|
172
|
+
input: process.stdin,
|
|
173
|
+
output: process.stdout,
|
|
174
|
+
});
|
|
175
|
+
const prompt = required
|
|
176
|
+
? `${fieldName}: `
|
|
177
|
+
: `${fieldName} (optional, press Enter to skip): `;
|
|
178
|
+
rl.question(prompt, (answer) => {
|
|
179
|
+
rl.close();
|
|
180
|
+
if (answer.toLowerCase() === 'cancel') {
|
|
181
|
+
resolve(null);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (!answer && required) {
|
|
185
|
+
console.log(chalk.red('This field is required'));
|
|
186
|
+
resolve(this.collectStringInput(fieldName, required));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// Return undefined for optional skipped fields, empty string for provided empty input
|
|
190
|
+
if (!answer && !required) {
|
|
191
|
+
resolve(undefined);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
resolve(answer);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Collect boolean input from user (yes/no)
|
|
200
|
+
*/
|
|
201
|
+
collectBooleanInput(fieldName, required) {
|
|
202
|
+
return new Promise((resolve) => {
|
|
203
|
+
const rl = readline.createInterface({
|
|
204
|
+
input: process.stdin,
|
|
205
|
+
output: process.stdout,
|
|
206
|
+
});
|
|
207
|
+
const prompt = required
|
|
208
|
+
? `${fieldName} (yes/no): `
|
|
209
|
+
: `${fieldName} (yes/no, press Enter to skip): `;
|
|
210
|
+
rl.question(prompt, (answer) => {
|
|
211
|
+
rl.close();
|
|
212
|
+
if (answer.toLowerCase() === 'cancel') {
|
|
213
|
+
resolve(null);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const normalized = answer.toLowerCase().trim();
|
|
217
|
+
// Allow skipping optional fields
|
|
218
|
+
if (!normalized && !required) {
|
|
219
|
+
resolve(undefined);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (!normalized && required) {
|
|
223
|
+
console.log(chalk.red('This field is required'));
|
|
224
|
+
resolve(this.collectBooleanInput(fieldName, required));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (normalized === 'yes' || normalized === 'y' || normalized === 'true') {
|
|
228
|
+
resolve(true);
|
|
229
|
+
}
|
|
230
|
+
else if (normalized === 'no' || normalized === 'n' || normalized === 'false') {
|
|
231
|
+
resolve(false);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
console.log(chalk.red('Please answer yes or no'));
|
|
235
|
+
resolve(this.collectBooleanInput(fieldName, required));
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Collect number input from user
|
|
242
|
+
*/
|
|
243
|
+
collectNumberInput(fieldName, integer, required) {
|
|
244
|
+
return new Promise((resolve) => {
|
|
245
|
+
const rl = readline.createInterface({
|
|
246
|
+
input: process.stdin,
|
|
247
|
+
output: process.stdout,
|
|
248
|
+
});
|
|
249
|
+
const type = integer ? 'integer' : 'number';
|
|
250
|
+
const prompt = required
|
|
251
|
+
? `${fieldName} (${type}): `
|
|
252
|
+
: `${fieldName} (${type}, press Enter to skip): `;
|
|
253
|
+
rl.question(prompt, (answer) => {
|
|
254
|
+
rl.close();
|
|
255
|
+
if (answer.toLowerCase() === 'cancel') {
|
|
256
|
+
resolve(null);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// Allow skipping optional fields
|
|
260
|
+
if (!answer.trim() && !required) {
|
|
261
|
+
resolve(undefined);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (!answer.trim() && required) {
|
|
265
|
+
console.log(chalk.red('This field is required'));
|
|
266
|
+
resolve(this.collectNumberInput(fieldName, integer, required));
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const num = Number(answer);
|
|
270
|
+
if (isNaN(num)) {
|
|
271
|
+
console.log(chalk.red(`Please enter a valid ${type}`));
|
|
272
|
+
resolve(this.collectNumberInput(fieldName, integer, required));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (integer && !Number.isInteger(num)) {
|
|
276
|
+
console.log(chalk.red('Please enter an integer'));
|
|
277
|
+
resolve(this.collectNumberInput(fieldName, integer, required));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
resolve(num);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Collect enum input from user (select from list)
|
|
286
|
+
*/
|
|
287
|
+
collectEnumInput(fieldName, options, required) {
|
|
288
|
+
return new Promise((resolve) => {
|
|
289
|
+
console.log(chalk.gray('Available options:'));
|
|
290
|
+
options.forEach((option, index) => {
|
|
291
|
+
console.log(chalk.gray(` ${index + 1}. ${option}`));
|
|
292
|
+
});
|
|
293
|
+
const rl = readline.createInterface({
|
|
294
|
+
input: process.stdin,
|
|
295
|
+
output: process.stdout,
|
|
296
|
+
});
|
|
297
|
+
const prompt = required
|
|
298
|
+
? `${fieldName} (enter number or value): `
|
|
299
|
+
: `${fieldName} (enter number or value, press Enter to skip): `;
|
|
300
|
+
rl.question(prompt, (answer) => {
|
|
301
|
+
rl.close();
|
|
302
|
+
if (answer.toLowerCase() === 'cancel') {
|
|
303
|
+
resolve(null);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
// Allow skipping optional fields
|
|
307
|
+
if (!answer.trim() && !required) {
|
|
308
|
+
resolve(undefined);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (!answer.trim() && required) {
|
|
312
|
+
console.log(chalk.red('This field is required'));
|
|
313
|
+
resolve(this.collectEnumInput(fieldName, options, required));
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
// Try as index first
|
|
317
|
+
const index = parseInt(answer) - 1;
|
|
318
|
+
if (!isNaN(index) && index >= 0 && index < options.length) {
|
|
319
|
+
resolve(options[index]);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// Try as direct value
|
|
323
|
+
const directMatch = options.find((opt) => String(opt) === answer);
|
|
324
|
+
if (directMatch !== undefined) {
|
|
325
|
+
resolve(directMatch);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
console.log(chalk.red('Invalid selection'));
|
|
329
|
+
resolve(this.collectEnumInput(fieldName, options, required));
|
|
330
|
+
});
|
|
331
|
+
});
|
|
52
332
|
}
|
|
53
333
|
/**
|
|
54
|
-
* Send
|
|
334
|
+
* Send approval response via AgentEventBus
|
|
55
335
|
*/
|
|
56
|
-
|
|
336
|
+
sendApprovalResponse(response) {
|
|
57
337
|
if (!this.agentEventBus) {
|
|
58
|
-
logger.error('AgentEventBus not available for sending
|
|
338
|
+
logger.error('AgentEventBus not available for sending approval response');
|
|
59
339
|
return;
|
|
60
340
|
}
|
|
61
|
-
logger.debug(`CLI sending
|
|
62
|
-
this.agentEventBus.emit('dexto:
|
|
341
|
+
logger.debug(`CLI sending approvalResponse for approvalId ${response.approvalId}, status=${response.status}, sessionId=${response.sessionId ?? 'global'}`);
|
|
342
|
+
this.agentEventBus.emit('dexto:approvalResponse', response);
|
|
63
343
|
}
|
|
64
344
|
/**
|
|
65
345
|
* Collect user input with arrow key navigation
|
|
@@ -155,7 +435,7 @@ export class CLIToolConfirmationSubscriber {
|
|
|
155
435
|
*/
|
|
156
436
|
cleanup() {
|
|
157
437
|
if (this.agentEventBus) {
|
|
158
|
-
this.agentEventBus.removeAllListeners('dexto:
|
|
438
|
+
this.agentEventBus.removeAllListeners('dexto:approvalRequest');
|
|
159
439
|
}
|
|
160
440
|
}
|
|
161
441
|
}
|
package/dist/index.js
CHANGED
|
@@ -40,11 +40,12 @@ program
|
|
|
40
40
|
.name('dexto')
|
|
41
41
|
.description('AI-powered CLI and WebUI for interacting with MCP servers')
|
|
42
42
|
.version(pkg.version, '-v, --version', 'output the current version')
|
|
43
|
-
.option('-a, --agent <
|
|
43
|
+
.option('-a, --agent <id|path>', 'Agent ID or path to agent config file')
|
|
44
44
|
.option('-p, --prompt <text>', 'Run prompt and exit. Alternatively provide a single quoted string as positional argument.')
|
|
45
45
|
.option('-s, --strict', 'Require all server connections to succeed')
|
|
46
46
|
.option('--no-verbose', 'Disable verbose output')
|
|
47
47
|
.option('--no-interactive', 'Disable interactive prompts and API key setup')
|
|
48
|
+
.option('--skip-setup', 'Skip global setup validation (useful for MCP mode, automation)')
|
|
48
49
|
.option('-m, --model <model>', 'Specify the LLM model to use')
|
|
49
50
|
.option('--router <router>', 'Specify the LLM router to use (vercel or in-built)')
|
|
50
51
|
.option('--auto-approve', 'Always approve tool executions without confirmation prompts')
|
|
@@ -155,10 +156,17 @@ program
|
|
|
155
156
|
// 5) `install` SUB-COMMAND
|
|
156
157
|
program
|
|
157
158
|
.command('install [agents...]')
|
|
158
|
-
.description('Install agents from
|
|
159
|
+
.description('Install agents from registry or custom YAML files/directories')
|
|
159
160
|
.option('--all', 'Install all available agents from registry')
|
|
160
161
|
.option('--no-inject-preferences', 'Skip injecting global preferences into installed agents')
|
|
161
162
|
.option('--force', 'Force reinstall even if agent is already installed')
|
|
163
|
+
.addHelpText('after', `
|
|
164
|
+
Examples:
|
|
165
|
+
$ dexto install default-agent Install agent from registry
|
|
166
|
+
$ dexto install agent1 agent2 Install multiple registry agents
|
|
167
|
+
$ dexto install --all Install all available registry agents
|
|
168
|
+
$ dexto install ./my-agent.yml Install custom agent from YAML file
|
|
169
|
+
$ dexto install ./my-agent-dir/ Install custom agent from directory (interactive)`)
|
|
162
170
|
.action(withAnalytics('install', async (agents = [], options) => {
|
|
163
171
|
try {
|
|
164
172
|
await handleInstallCommand(agents, options);
|
|
@@ -230,7 +238,7 @@ async function bootstrapAgentFromGlobalOpts() {
|
|
|
230
238
|
const resolvedPath = await resolveAgentPath(globalOpts.agent, globalOpts.autoInstall !== false, true);
|
|
231
239
|
const rawConfig = await loadAgentConfig(resolvedPath);
|
|
232
240
|
const mergedConfig = applyCLIOverrides(rawConfig, globalOpts);
|
|
233
|
-
const agent = new DextoAgent(mergedConfig,
|
|
241
|
+
const agent = new DextoAgent(mergedConfig, resolvedPath);
|
|
234
242
|
await agent.start();
|
|
235
243
|
// Register graceful shutdown
|
|
236
244
|
const shutdown = async () => {
|
|
@@ -501,10 +509,11 @@ program
|
|
|
501
509
|
}
|
|
502
510
|
}
|
|
503
511
|
// Check setup state and auto-trigger if needed
|
|
504
|
-
if (
|
|
512
|
+
// Skip if --skip-setup flag is set (for MCP mode, automation, etc.)
|
|
513
|
+
if (!opts.skipSetup && (await requiresSetup())) {
|
|
505
514
|
if (opts.interactive === false) {
|
|
506
515
|
console.error('❌ Setup required but --no-interactive flag is set.');
|
|
507
|
-
console.error('💡 Run `dexto setup` to
|
|
516
|
+
console.error('💡 Run `dexto setup` first, or use --skip-setup to bypass global setup.');
|
|
508
517
|
safeExit('main', 1, 'setup-required-non-interactive');
|
|
509
518
|
}
|
|
510
519
|
await handleSetupCommand({ interactive: true });
|
|
@@ -539,51 +548,9 @@ program
|
|
|
539
548
|
}
|
|
540
549
|
}
|
|
541
550
|
// DextoAgent will parse/validate again (parse-twice pattern)
|
|
542
|
-
agent = new DextoAgent(validatedConfig,
|
|
551
|
+
agent = new DextoAgent(validatedConfig, resolvedPath);
|
|
543
552
|
// Start the agent (initialize async services)
|
|
544
553
|
await agent.start();
|
|
545
|
-
// Handle session options - simplified logic
|
|
546
|
-
if (opts.resume) {
|
|
547
|
-
try {
|
|
548
|
-
// Resume specific session by ID
|
|
549
|
-
await agent.loadSessionAsDefault(opts.resume);
|
|
550
|
-
logger.info(`Resumed session: ${opts.resume}`, null, 'cyan');
|
|
551
|
-
}
|
|
552
|
-
catch (err) {
|
|
553
|
-
console.error(`❌ Failed to resume session '${opts.resume}': ${err instanceof Error ? err.message : String(err)}`);
|
|
554
|
-
console.error('💡 Use `dexto session list` to see available sessions');
|
|
555
|
-
safeExit('main', 1, 'resume-failed');
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
else if (opts.continue) {
|
|
559
|
-
try {
|
|
560
|
-
// Continue from most recent session
|
|
561
|
-
await loadMostRecentSession(agent);
|
|
562
|
-
// If no sessions existed, create a new one to honor default-new invariant
|
|
563
|
-
const sessionsAfter = await agent.listSessions();
|
|
564
|
-
if (sessionsAfter.length === 0) {
|
|
565
|
-
const session = await agent.createSession();
|
|
566
|
-
await agent.loadSessionAsDefault(session.id);
|
|
567
|
-
logger.info(`Created new session: ${session.id}`, null, 'green');
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
catch (err) {
|
|
571
|
-
console.error(`❌ Failed to continue session: ${err instanceof Error ? err.message : String(err)}`);
|
|
572
|
-
safeExit('main', 1, 'continue-failed');
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
else {
|
|
576
|
-
// Default behavior: create new session
|
|
577
|
-
try {
|
|
578
|
-
const session = await agent.createSession();
|
|
579
|
-
await agent.loadSessionAsDefault(session.id);
|
|
580
|
-
logger.info(`Created new session: ${session.id}`, null, 'green');
|
|
581
|
-
}
|
|
582
|
-
catch (err) {
|
|
583
|
-
console.error(`❌ Failed to create new session: ${err instanceof Error ? err.message : String(err)}`);
|
|
584
|
-
safeExit('main', 1, 'create-session-failed');
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
554
|
}
|
|
588
555
|
catch (err) {
|
|
589
556
|
if (err instanceof ExitSignal)
|
|
@@ -595,6 +562,48 @@ program
|
|
|
595
562
|
// ——— Dispatch based on --mode ———
|
|
596
563
|
switch (opts.mode) {
|
|
597
564
|
case 'cli': {
|
|
565
|
+
// Handle session options - only for CLI mode
|
|
566
|
+
if (opts.resume) {
|
|
567
|
+
try {
|
|
568
|
+
// Resume specific session by ID
|
|
569
|
+
await agent.loadSessionAsDefault(opts.resume);
|
|
570
|
+
logger.info(`Resumed session: ${opts.resume}`, null, 'cyan');
|
|
571
|
+
}
|
|
572
|
+
catch (err) {
|
|
573
|
+
console.error(`❌ Failed to resume session '${opts.resume}': ${err instanceof Error ? err.message : String(err)}`);
|
|
574
|
+
console.error('💡 Use `dexto session list` to see available sessions');
|
|
575
|
+
safeExit('main', 1, 'resume-failed');
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
else if (opts.continue) {
|
|
579
|
+
try {
|
|
580
|
+
// Continue from most recent session
|
|
581
|
+
await loadMostRecentSession(agent);
|
|
582
|
+
// If no sessions existed, create a new one to honor default-new invariant
|
|
583
|
+
const sessionsAfter = await agent.listSessions();
|
|
584
|
+
if (sessionsAfter.length === 0) {
|
|
585
|
+
const session = await agent.createSession();
|
|
586
|
+
await agent.loadSessionAsDefault(session.id);
|
|
587
|
+
logger.info(`Created new session: ${session.id}`, null, 'green');
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
catch (err) {
|
|
591
|
+
console.error(`❌ Failed to continue session: ${err instanceof Error ? err.message : String(err)}`);
|
|
592
|
+
safeExit('main', 1, 'continue-failed');
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
// Default behavior: create new session for CLI mode
|
|
597
|
+
try {
|
|
598
|
+
const session = await agent.createSession();
|
|
599
|
+
await agent.loadSessionAsDefault(session.id);
|
|
600
|
+
logger.info(`Created new session: ${session.id}`, null, 'green');
|
|
601
|
+
}
|
|
602
|
+
catch (err) {
|
|
603
|
+
console.error(`❌ Failed to create new session: ${err instanceof Error ? err.message : String(err)}`);
|
|
604
|
+
safeExit('main', 1, 'create-session-failed');
|
|
605
|
+
}
|
|
606
|
+
}
|
|
598
607
|
const toolConfirmationMode = agent.getEffectiveConfig().toolConfirmation?.mode ?? 'event-based';
|
|
599
608
|
if (toolConfirmationMode === 'event-based') {
|
|
600
609
|
// Set up CLI tool confirmation subscriber
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
self.__BUILD_MANIFEST=function(e,r,t,s,_){return{__rewrites:{afterFiles:[{has:t,source:"/api/:path*",destination:t}],beforeFiles:[],fallback:[]},__routerFilterStatic:{numItems:3,errorRate:1e-4,numBits:58,numHashes:14,bitArray:[1,1,e,e,0,e,e,0,r,e,r,e,r,e,e,e,r,e,e,e,r,r,e,r,r,r,r,r,e,e,e,e,e,e,r,e,e,r,e,e,r,r,e,r,e,r,r,e,e,e,r,r,r,r,e,r,r,e]},__routerFilterDynamic:{numItems:e,errorRate:1e-4,numBits:20,numHashes:14,bitArray:[r,r,r,r,e,e,e,r,e,e,r,r,r,r,e,r,r,e,e,e]},"/_error":["static/chunks/pages/_error-162d61367b59fad2.js"],sortedPages:["/_app","/_error"]}}(1,0,void 0,1e-4,14),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|