dexto 1.6.20 → 1.6.22
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 +5 -4
- package/dist/agents/coding-agent/coding-agent.yml +1 -0
- package/dist/api/server-hono.d.ts.map +1 -1
- package/dist/api/server-hono.js +7 -3
- package/dist/cli/commands/helpers/formatters.d.ts.map +1 -1
- package/dist/cli/commands/helpers/formatters.js +2 -0
- package/dist/cli/commands/setup.d.ts +5 -0
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +33 -12
- package/dist/cli/modes/cli.d.ts.map +1 -1
- package/dist/cli/modes/cli.js +90 -16
- package/dist/cli/utils/dexto-setup.d.ts +1 -1
- package/dist/cli/utils/dexto-setup.js +1 -1
- package/dist/cli/utils/setup-utils.d.ts +2 -3
- package/dist/cli/utils/setup-utils.d.ts.map +1 -1
- package/dist/cli/utils/setup-utils.js +34 -12
- package/dist/config/cli-overrides.d.ts +9 -1
- package/dist/config/cli-overrides.d.ts.map +1 -1
- package/dist/config/cli-overrides.js +57 -0
- package/dist/index-main.js +49 -33
- package/dist/webui/assets/index-DlW-GIHG.js +1613 -0
- package/dist/webui/assets/index-cNwNJ0w6.css +1 -0
- package/dist/webui/index.html +2 -2
- package/package.json +12 -12
- package/dist/webui/assets/index-4kADt2zR.css +0 -1
- package/dist/webui/assets/index-DTNr4Gaw.js +0 -1613
package/README.md
CHANGED
|
@@ -116,7 +116,7 @@ Upgrade/uninstall and migration troubleshooting live in docs:
|
|
|
116
116
|
### Run
|
|
117
117
|
|
|
118
118
|
```bash
|
|
119
|
-
# Start Dexto
|
|
119
|
+
# Start Dexto
|
|
120
120
|
dexto
|
|
121
121
|
```
|
|
122
122
|
|
|
@@ -132,6 +132,8 @@ dexto --help # Explore all options
|
|
|
132
132
|
|
|
133
133
|
**Inside the interactive CLI**, type `/` to explore commands—switch models, manage sessions, configure tools, and more.
|
|
134
134
|
|
|
135
|
+
If Dexto has not been set up yet, the first interactive launch opens the generic `dexto setup` flow before starting. Existing provider keys in your environment are detected there, so you can keep startup simple without inheriting the wrong bundled provider by accident.
|
|
136
|
+
|
|
135
137
|
### Manage Settings
|
|
136
138
|
|
|
137
139
|
```bash
|
|
@@ -595,9 +597,8 @@ Test tools before deploying:
|
|
|
595
597
|
Usage: dexto [options] [command] [prompt...]
|
|
596
598
|
|
|
597
599
|
Basic Usage:
|
|
598
|
-
dexto
|
|
599
|
-
dexto "query"
|
|
600
|
-
dexto --mode cli Interactive CLI
|
|
600
|
+
dexto or dexto --mode cli Start interactive CLI (default)
|
|
601
|
+
dexto "query" Run one-shot query
|
|
601
602
|
|
|
602
603
|
Session Management:
|
|
603
604
|
dexto -c Continue last conversation
|
|
@@ -118,6 +118,7 @@ permissions:
|
|
|
118
118
|
alwaysAllow:
|
|
119
119
|
- ask_user
|
|
120
120
|
- read_file # Read files without approval
|
|
121
|
+
- read_media_file # Read files without approval
|
|
121
122
|
- glob_files # Search for files without approval
|
|
122
123
|
- grep_content # Search within files without approval
|
|
123
124
|
- bash_output # Check background process output
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-hono.d.ts","sourceRoot":"","sources":["../../src/api/server-hono.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAuC,MAAM,aAAa,CAAC;AAY9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EACH,cAAc,EACd,gBAAgB,
|
|
1
|
+
{"version":3,"file":"server-hono.d.ts","sourceRoot":"","sources":["../../src/api/server-hono.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAuC,MAAM,aAAa,CAAC;AAY9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EACH,cAAc,EACd,gBAAgB,EAWhB,KAAK,kBAAkB,EAC1B,MAAM,eAAe,CAAC;AAmGvB,MAAM,MAAM,wBAAwB,GAAG;IACnC,GAAG,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;IACvC,MAAM,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC;IACtD,iBAAiB,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC1F,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/E,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7E,oBAAoB,EAAE,MAAM,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CAC9C,CAAC;AAGF,wBAAsB,iBAAiB,CACnC,KAAK,EAAE,UAAU,EACjB,iBAAiB,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,EACtC,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAChB,cAAc,CAAC,EAAE,MAAM,EACvB,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC,wBAAwB,CAAC,CAiYnC;AAED,wBAAsB,kBAAkB,CACpC,KAAK,EAAE,UAAU,EACjB,IAAI,SAAO,EACX,iBAAiB,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,EACtC,OAAO,CAAC,EAAE,MAAM,EAChB,cAAc,CAAC,EAAE,MAAM,EACvB,aAAa,CAAC,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,kBAAkB,GACjC,OAAO,CAAC;IACP,MAAM,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,QAAQ,CAAC,CAAC;IACtD,iBAAiB,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;CAC7F,CAAC,CAkCD"}
|
package/dist/api/server-hono.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createAgentCard, logger, AgentError } from '@dexto/core';
|
|
|
3
3
|
import { loadAgentConfig, deriveDisplayName, getAgentRegistry, AgentFactory, globalPreferencesExist, loadGlobalPreferences, createDextoAgentFromConfig, } from '@dexto/agent-management';
|
|
4
4
|
import { applyUserPreferences } from '../config/cli-overrides.js';
|
|
5
5
|
import { createFileSessionLoggerFactory } from '../utils/session-logger-factory.js';
|
|
6
|
-
import { createDextoApp, createNodeServer, createMcpTransport as createServerMcpTransport, createMcpHttpHandlers, initializeMcpServer as initializeServerMcpServer, createManualApprovalHandler, WebhookEventSubscriber, A2ASseEventSubscriber, ApprovalCoordinator, } from '@dexto/server';
|
|
6
|
+
import { createDextoApp, createNodeServer, createMcpTransport as createServerMcpTransport, createMcpHttpHandlers, initializeMcpServer as initializeServerMcpServer, createManualApprovalHandler, WebhookEventSubscriber, A2ASseEventSubscriber, SessionSseEventSubscriber, ApprovalCoordinator, wireApprovalCoordinatorToAgent, } from '@dexto/server';
|
|
7
7
|
import { registerGracefulShutdown } from '../utils/graceful-shutdown.js';
|
|
8
8
|
import { applyWorkspaceToAgent } from '../utils/workspace.js';
|
|
9
9
|
const DEFAULT_AGENT_VERSION = '1.0.0';
|
|
@@ -95,7 +95,9 @@ export async function initializeHonoApi(agent, agentCardOverride, listenPort, ag
|
|
|
95
95
|
// Create event subscribers and approval coordinator (shared across agent switches)
|
|
96
96
|
const webhookSubscriber = new WebhookEventSubscriber();
|
|
97
97
|
const sseSubscriber = new A2ASseEventSubscriber();
|
|
98
|
+
const sessionSseSubscriber = new SessionSseEventSubscriber();
|
|
98
99
|
const approvalCoordinator = new ApprovalCoordinator();
|
|
100
|
+
let approvalEventBridge = null;
|
|
99
101
|
/**
|
|
100
102
|
* Wire services (SSE subscribers) to an agent.
|
|
101
103
|
* Called for agent switching to re-subscribe to the new agent's event bus.
|
|
@@ -103,11 +105,12 @@ export async function initializeHonoApi(agent, agentCardOverride, listenPort, ag
|
|
|
103
105
|
*/
|
|
104
106
|
async function wireServicesToAgent(agent) {
|
|
105
107
|
logger.debug('Wiring services to agent...');
|
|
108
|
+
approvalEventBridge?.abort();
|
|
109
|
+
approvalEventBridge = wireApprovalCoordinatorToAgent(agent, approvalCoordinator);
|
|
106
110
|
// Register subscribers (DextoAgent handles (re-)subscription on start/restart)
|
|
107
111
|
agent.registerSubscriber(webhookSubscriber);
|
|
108
112
|
agent.registerSubscriber(sseSubscriber);
|
|
109
|
-
|
|
110
|
-
// It's a separate coordination channel between handler and server
|
|
113
|
+
agent.registerSubscriber(sessionSseSubscriber);
|
|
111
114
|
}
|
|
112
115
|
/**
|
|
113
116
|
* Helper to resolve agent ID to { id, name } by looking up in registry
|
|
@@ -304,6 +307,7 @@ export async function initializeHonoApi(agent, agentCardOverride, listenPort, ag
|
|
|
304
307
|
approvalCoordinator,
|
|
305
308
|
webhookSubscriber,
|
|
306
309
|
sseSubscriber,
|
|
310
|
+
sessionSseSubscriber,
|
|
307
311
|
...(webRoot ? { webRoot } : {}),
|
|
308
312
|
...(webUIConfig ? { webUIConfig } : {}),
|
|
309
313
|
agentsContext: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/helpers/formatters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAY,MAAM,aAAa,CAAC;AAG9E;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,eAAe,EAC1B,SAAS,GAAE,OAAe,GAC3B,MAAM,CAiCR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/helpers/formatters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAY,MAAM,aAAa,CAAC;AAG9E;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,eAAe,EAC1B,SAAS,GAAE,OAAe,GAC3B,MAAM,CAiCR;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA+DpF"}
|
|
@@ -79,6 +79,8 @@ export function formatHistoryMessage(message, index) {
|
|
|
79
79
|
return '[Image]';
|
|
80
80
|
if (part.type === 'file')
|
|
81
81
|
return `[File: ${part.filename || 'unknown'}]`;
|
|
82
|
+
if (part.type === 'resource')
|
|
83
|
+
return `[Resource: ${part.name || part.uri}]`;
|
|
82
84
|
return '[Unknown content]';
|
|
83
85
|
})
|
|
84
86
|
.join(' ');
|
|
@@ -5,6 +5,7 @@ declare const SetupCommandSchema: z.ZodEffects<z.ZodObject<{
|
|
|
5
5
|
defaultAgent: z.ZodDefault<z.ZodString>;
|
|
6
6
|
interactive: z.ZodDefault<z.ZodBoolean>;
|
|
7
7
|
force: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
defaultMode: z.ZodOptional<z.ZodEnum<["cli", "web", "server", "discord", "telegram", "mcp"]>>;
|
|
8
9
|
quickStart: z.ZodDefault<z.ZodBoolean>;
|
|
9
10
|
}, "strict", z.ZodTypeAny, {
|
|
10
11
|
interactive: boolean;
|
|
@@ -13,12 +14,14 @@ declare const SetupCommandSchema: z.ZodEffects<z.ZodObject<{
|
|
|
13
14
|
quickStart: boolean;
|
|
14
15
|
provider?: "dexto-nova" | "openai" | "openai-compatible" | "anthropic" | "google" | "groq" | "xai" | "cohere" | "minimax" | "glm" | "openrouter" | "litellm" | "glama" | "vertex" | "bedrock" | "local" | "ollama" | undefined;
|
|
15
16
|
model?: string | undefined;
|
|
17
|
+
defaultMode?: "cli" | "web" | "server" | "discord" | "telegram" | "mcp" | undefined;
|
|
16
18
|
}, {
|
|
17
19
|
interactive?: boolean | undefined;
|
|
18
20
|
provider?: "dexto-nova" | "openai" | "openai-compatible" | "anthropic" | "google" | "groq" | "xai" | "cohere" | "minimax" | "glm" | "openrouter" | "litellm" | "glama" | "vertex" | "bedrock" | "local" | "ollama" | undefined;
|
|
19
21
|
model?: string | undefined;
|
|
20
22
|
force?: boolean | undefined;
|
|
21
23
|
defaultAgent?: string | undefined;
|
|
24
|
+
defaultMode?: "cli" | "web" | "server" | "discord" | "telegram" | "mcp" | undefined;
|
|
22
25
|
quickStart?: boolean | undefined;
|
|
23
26
|
}>, {
|
|
24
27
|
interactive: boolean;
|
|
@@ -27,12 +30,14 @@ declare const SetupCommandSchema: z.ZodEffects<z.ZodObject<{
|
|
|
27
30
|
quickStart: boolean;
|
|
28
31
|
provider?: "dexto-nova" | "openai" | "openai-compatible" | "anthropic" | "google" | "groq" | "xai" | "cohere" | "minimax" | "glm" | "openrouter" | "litellm" | "glama" | "vertex" | "bedrock" | "local" | "ollama" | undefined;
|
|
29
32
|
model?: string | undefined;
|
|
33
|
+
defaultMode?: "cli" | "web" | "server" | "discord" | "telegram" | "mcp" | undefined;
|
|
30
34
|
}, {
|
|
31
35
|
interactive?: boolean | undefined;
|
|
32
36
|
provider?: "dexto-nova" | "openai" | "openai-compatible" | "anthropic" | "google" | "groq" | "xai" | "cohere" | "minimax" | "glm" | "openrouter" | "litellm" | "glama" | "vertex" | "bedrock" | "local" | "ollama" | undefined;
|
|
33
37
|
model?: string | undefined;
|
|
34
38
|
force?: boolean | undefined;
|
|
35
39
|
defaultAgent?: string | undefined;
|
|
40
|
+
defaultMode?: "cli" | "web" | "server" | "discord" | "telegram" | "mcp" | undefined;
|
|
36
41
|
quickStart?: boolean | undefined;
|
|
37
42
|
}>;
|
|
38
43
|
export type CLISetupOptions = z.output<typeof SetupCommandSchema>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/setup.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA+DxB,QAAA,MAAM,kBAAkB
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/setup.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA+DxB,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8ClB,CAAC;AAEP,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAClE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAsKtE;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC9F"}
|
|
@@ -39,6 +39,10 @@ const SetupCommandSchema = z
|
|
|
39
39
|
.boolean()
|
|
40
40
|
.default(false)
|
|
41
41
|
.describe('Overwrite existing setup when already configured'),
|
|
42
|
+
defaultMode: z
|
|
43
|
+
.enum(['cli', 'web', 'server', 'discord', 'telegram', 'mcp'])
|
|
44
|
+
.optional()
|
|
45
|
+
.describe('Preferred default mode for interactive setup flows'),
|
|
42
46
|
quickStart: z
|
|
43
47
|
.boolean()
|
|
44
48
|
.default(false)
|
|
@@ -189,7 +193,10 @@ export async function handleSetupCommand(options) {
|
|
|
189
193
|
}
|
|
190
194
|
// Handle quick start
|
|
191
195
|
if (validated.quickStart) {
|
|
192
|
-
await handleQuickStart({
|
|
196
|
+
await handleQuickStart({
|
|
197
|
+
onCancel: 'exit',
|
|
198
|
+
preferredDefaultMode: validated.defaultMode,
|
|
199
|
+
});
|
|
193
200
|
return;
|
|
194
201
|
}
|
|
195
202
|
// Handle interactive full setup
|
|
@@ -260,7 +267,9 @@ async function handleQuickStart(options = { onCancel: 'exit' }) {
|
|
|
260
267
|
}
|
|
261
268
|
continue;
|
|
262
269
|
}
|
|
263
|
-
const defaultMode = useCli
|
|
270
|
+
const defaultMode = useCli
|
|
271
|
+
? 'cli'
|
|
272
|
+
: await selectDefaultMode(options.preferredDefaultMode);
|
|
264
273
|
if (defaultMode === null) {
|
|
265
274
|
if (options.onCancel === 'exit') {
|
|
266
275
|
p.cancel('Setup cancelled');
|
|
@@ -383,7 +392,7 @@ async function handleQuickStart(options = { onCancel: 'exit' }) {
|
|
|
383
392
|
}
|
|
384
393
|
continue;
|
|
385
394
|
}
|
|
386
|
-
const defaultMode = useCli ? 'cli' : await selectDefaultMode();
|
|
395
|
+
const defaultMode = useCli ? 'cli' : await selectDefaultMode(options.preferredDefaultMode);
|
|
387
396
|
// Handle cancellation
|
|
388
397
|
if (defaultMode === null) {
|
|
389
398
|
if (options.onCancel === 'exit') {
|
|
@@ -934,11 +943,14 @@ async function getCreditsBalance() {
|
|
|
934
943
|
* Full interactive setup flow with wizard navigation.
|
|
935
944
|
* Users can go back to previous steps to change their selections.
|
|
936
945
|
*/
|
|
937
|
-
async function handleInteractiveSetup(
|
|
946
|
+
async function handleInteractiveSetup(options) {
|
|
938
947
|
console.log(chalk.cyan('\nDexto Setup\n'));
|
|
939
948
|
p.intro(chalk.cyan("Let's configure your AI agent"));
|
|
940
949
|
// Initialize wizard state
|
|
941
|
-
let state = {
|
|
950
|
+
let state = {
|
|
951
|
+
step: 'setupType',
|
|
952
|
+
preferredDefaultMode: options.defaultMode,
|
|
953
|
+
};
|
|
942
954
|
// Wizard loop - process steps until complete
|
|
943
955
|
while (state.step !== 'complete') {
|
|
944
956
|
switch (state.step) {
|
|
@@ -1016,7 +1028,10 @@ async function wizardStepSetupType(state) {
|
|
|
1016
1028
|
}
|
|
1017
1029
|
if (setupType === 'quick') {
|
|
1018
1030
|
// Quick start bypasses the wizard - handle it directly
|
|
1019
|
-
const result = await handleQuickStart({
|
|
1031
|
+
const result = await handleQuickStart({
|
|
1032
|
+
onCancel: 'back',
|
|
1033
|
+
preferredDefaultMode: state.preferredDefaultMode,
|
|
1034
|
+
});
|
|
1020
1035
|
if (result === 'cancelled') {
|
|
1021
1036
|
return { ...state, step: 'setupType' };
|
|
1022
1037
|
}
|
|
@@ -1161,7 +1176,7 @@ async function wizardStepMode(state) {
|
|
|
1161
1176
|
const isLocalProvider = provider === 'local' || provider === 'ollama';
|
|
1162
1177
|
const hasReasoningStep = getReasoningProfile(provider, model).capable;
|
|
1163
1178
|
showStepProgress('mode', provider, model);
|
|
1164
|
-
const mode = await selectDefaultModeWithBack();
|
|
1179
|
+
const mode = await selectDefaultModeWithBack(state.preferredDefaultMode);
|
|
1165
1180
|
if (mode === '_back') {
|
|
1166
1181
|
// Go back to the previous *interactive* step. Some steps (like apiKey) may be
|
|
1167
1182
|
// auto-skipped when not required or already configured, so "back" from mode
|
|
@@ -1550,7 +1565,7 @@ async function promptCustomModelValues(initialModel, providerOverride) {
|
|
|
1550
1565
|
...(reasoningPreset ? { reasoningPreset } : {}),
|
|
1551
1566
|
};
|
|
1552
1567
|
}
|
|
1553
|
-
async function selectDefaultModeWithBack() {
|
|
1568
|
+
async function selectDefaultModeWithBack(preferredDefaultMode) {
|
|
1554
1569
|
const result = await p.select({
|
|
1555
1570
|
message: 'How do you want to use Dexto by default?',
|
|
1556
1571
|
options: [
|
|
@@ -1571,6 +1586,7 @@ async function selectDefaultModeWithBack() {
|
|
|
1571
1586
|
},
|
|
1572
1587
|
{ value: '_back', label: chalk.gray('← Back'), hint: 'Go to previous step' },
|
|
1573
1588
|
],
|
|
1589
|
+
...(preferredDefaultMode ? { initialValue: preferredDefaultMode } : {}),
|
|
1574
1590
|
});
|
|
1575
1591
|
if (p.isCancel(result)) {
|
|
1576
1592
|
return '_back';
|
|
@@ -1645,13 +1661,17 @@ async function handleNonInteractiveSetup(options) {
|
|
|
1645
1661
|
}
|
|
1646
1662
|
const apiKeyVar = getProviderEnvVar(provider);
|
|
1647
1663
|
const hadApiKeyBefore = Boolean(resolveApiKeyForProvider(provider));
|
|
1648
|
-
const
|
|
1664
|
+
const preferencesOptions = {
|
|
1649
1665
|
provider,
|
|
1650
1666
|
model,
|
|
1651
1667
|
apiKeyVar,
|
|
1652
1668
|
defaultAgent: options.defaultAgent,
|
|
1653
1669
|
setupCompleted: true,
|
|
1654
|
-
}
|
|
1670
|
+
};
|
|
1671
|
+
if (options.defaultMode) {
|
|
1672
|
+
preferencesOptions.defaultMode = options.defaultMode;
|
|
1673
|
+
}
|
|
1674
|
+
const preferences = createInitialPreferences(preferencesOptions);
|
|
1655
1675
|
await saveGlobalPreferences(preferences);
|
|
1656
1676
|
// For local provider, sync the active model in state.json
|
|
1657
1677
|
if (provider === 'local') {
|
|
@@ -1733,7 +1753,7 @@ async function showSettingsMenu() {
|
|
|
1733
1753
|
{
|
|
1734
1754
|
value: 'mode',
|
|
1735
1755
|
label: 'Change default mode',
|
|
1736
|
-
hint: `Currently: ${currentPrefs?.defaults.defaultMode || '
|
|
1756
|
+
hint: `Currently: ${currentPrefs?.defaults.defaultMode || 'cli'}`,
|
|
1737
1757
|
},
|
|
1738
1758
|
{
|
|
1739
1759
|
value: 'auth',
|
|
@@ -2090,7 +2110,7 @@ function showPreferencesFilePath() {
|
|
|
2090
2110
|
* Select default mode interactively
|
|
2091
2111
|
* Returns null if user cancels
|
|
2092
2112
|
*/
|
|
2093
|
-
async function selectDefaultMode() {
|
|
2113
|
+
async function selectDefaultMode(preferredDefaultMode) {
|
|
2094
2114
|
const mode = await p.select({
|
|
2095
2115
|
message: 'How do you want to use Dexto by default?',
|
|
2096
2116
|
options: [
|
|
@@ -2110,6 +2130,7 @@ async function selectDefaultMode() {
|
|
|
2110
2130
|
hint: 'REST API for integrations',
|
|
2111
2131
|
},
|
|
2112
2132
|
],
|
|
2133
|
+
...(preferredDefaultMode ? { initialValue: preferredDefaultMode } : {}),
|
|
2113
2134
|
});
|
|
2114
2135
|
if (p.isCancel(mode)) {
|
|
2115
2136
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/cli/modes/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/cli/modes/cli.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAuBpD,wBAAsB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAuQxE"}
|
package/dist/cli/modes/cli.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
2
3
|
import { logger } from '@dexto/core';
|
|
3
4
|
import { safeExit, ExitSignal } from '../../analytics/wrapper.js';
|
|
5
|
+
import { hasUsableCredentials } from '../../config/cli-overrides.js';
|
|
4
6
|
import { applyWorkspaceToAgent } from '../../utils/workspace.js';
|
|
5
7
|
async function getMostRecentSessionId(agent) {
|
|
6
8
|
const sessionIds = await agent.listSessions();
|
|
@@ -31,27 +33,99 @@ export async function runCliMode(context) {
|
|
|
31
33
|
await agent.start();
|
|
32
34
|
await applyWorkspaceToAgent(agent, workspaceRoot);
|
|
33
35
|
const llmConfig = agent.getCurrentLLMConfig();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
if (!hasUsableCredentials(llmConfig.provider, llmConfig)) {
|
|
37
|
+
const { globalPreferencesExist, loadGlobalPreferences } = await import('@dexto/agent-management');
|
|
36
38
|
const { interactiveApiKeySetup } = await import('../utils/api-key-setup.js');
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const { getProviderDisplayName, getProviderEnvVar } = await import('../utils/provider-setup.js');
|
|
40
|
+
let hasCompletedSetup = false;
|
|
41
|
+
if (globalPreferencesExist()) {
|
|
42
|
+
try {
|
|
43
|
+
const preferences = await loadGlobalPreferences();
|
|
44
|
+
hasCompletedSetup = preferences.setup.completed;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
hasCompletedSetup = false;
|
|
48
|
+
}
|
|
44
49
|
}
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
console.log(chalk.yellow(`\n⚠️ API key required for provider '${getProviderDisplayName(llmConfig.provider)}'\n`));
|
|
51
|
+
if (!hasCompletedSetup) {
|
|
52
|
+
console.log(chalk.gray(`Dexto started with the bundled coding-agent defaults. ` +
|
|
53
|
+
`Set ${getProviderEnvVar(llmConfig.provider)} or run ${chalk.cyan('dexto setup')} to choose a different provider.`));
|
|
47
54
|
}
|
|
48
|
-
|
|
49
|
-
await
|
|
50
|
-
|
|
55
|
+
const runProviderKeySetup = async () => {
|
|
56
|
+
const setupResult = await interactiveApiKeySetup(llmConfig.provider, {
|
|
57
|
+
exitOnCancel: false,
|
|
51
58
|
model: llmConfig.model,
|
|
52
|
-
apiKey: setupResult.apiKey,
|
|
53
59
|
});
|
|
54
|
-
|
|
60
|
+
if (setupResult.cancelled) {
|
|
61
|
+
safeExit('main', 0, 'api-key-setup-cancelled');
|
|
62
|
+
}
|
|
63
|
+
if (setupResult.skipped) {
|
|
64
|
+
safeExit('main', 0, 'api-key-pending');
|
|
65
|
+
}
|
|
66
|
+
if (setupResult.success && setupResult.apiKey) {
|
|
67
|
+
await agent.switchLLM({
|
|
68
|
+
provider: llmConfig.provider,
|
|
69
|
+
model: llmConfig.model,
|
|
70
|
+
apiKey: setupResult.apiKey,
|
|
71
|
+
});
|
|
72
|
+
logger.info('API key configured successfully, continuing...');
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
if (hasCompletedSetup) {
|
|
76
|
+
await runProviderKeySetup();
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const action = await p.select({
|
|
80
|
+
message: 'How would you like to continue?',
|
|
81
|
+
options: [
|
|
82
|
+
{
|
|
83
|
+
value: 'key',
|
|
84
|
+
label: `Paste a ${getProviderDisplayName(llmConfig.provider)} key`,
|
|
85
|
+
hint: 'Continue in this session',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
value: 'setup',
|
|
89
|
+
label: 'Run dexto setup',
|
|
90
|
+
hint: 'Choose a different provider or save defaults',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
value: 'exit',
|
|
94
|
+
label: 'Exit',
|
|
95
|
+
hint: 'Configure later',
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
if (p.isCancel(action) || action === 'exit') {
|
|
100
|
+
safeExit('main', 0, 'api-key-setup-cancelled');
|
|
101
|
+
}
|
|
102
|
+
if (action === 'setup') {
|
|
103
|
+
const { handleSetupCommand } = await import('../commands/setup.js');
|
|
104
|
+
await handleSetupCommand({ interactive: true, force: true });
|
|
105
|
+
let preferences;
|
|
106
|
+
try {
|
|
107
|
+
preferences = await loadGlobalPreferences();
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
safeExit('main', 0, 'setup-incomplete');
|
|
111
|
+
}
|
|
112
|
+
if (!preferences.setup.completed) {
|
|
113
|
+
safeExit('main', 0, 'setup-incomplete');
|
|
114
|
+
}
|
|
115
|
+
if (preferences.setup.apiKeyPending) {
|
|
116
|
+
safeExit('main', 0, 'api-key-pending');
|
|
117
|
+
}
|
|
118
|
+
await agent.switchLLM({
|
|
119
|
+
provider: preferences.llm.provider,
|
|
120
|
+
model: preferences.llm.model,
|
|
121
|
+
...(preferences.llm.apiKey && { apiKey: preferences.llm.apiKey }),
|
|
122
|
+
...(preferences.llm.baseURL && { baseURL: preferences.llm.baseURL }),
|
|
123
|
+
});
|
|
124
|
+
logger.info('Provider configured successfully, continuing...');
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
await runProviderKeySetup();
|
|
128
|
+
}
|
|
55
129
|
}
|
|
56
130
|
}
|
|
57
131
|
let cliSessionId;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Check if user can use Dexto provider.
|
|
3
3
|
* Requires BOTH:
|
|
4
|
-
* 1. User
|
|
4
|
+
* 1. User has usable Dexto auth state from `dexto login`
|
|
5
5
|
* 2. Has DEXTO_API_KEY (from auth config or environment)
|
|
6
6
|
*/
|
|
7
7
|
export declare function canUseDextoProvider(): Promise<boolean>;
|
|
@@ -3,7 +3,7 @@ import { getDextoApiKey, isAuthenticated } from '../auth/index.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Check if user can use Dexto provider.
|
|
5
5
|
* Requires BOTH:
|
|
6
|
-
* 1. User
|
|
6
|
+
* 1. User has usable Dexto auth state from `dexto login`
|
|
7
7
|
* 2. Has DEXTO_API_KEY (from auth config or environment)
|
|
8
8
|
*/
|
|
9
9
|
export async function canUseDextoProvider() {
|
|
@@ -23,9 +23,8 @@ export declare function getSetupState(): Promise<SetupState>;
|
|
|
23
23
|
* Check if user requires setup (missing, corrupted, or incomplete preferences)
|
|
24
24
|
* Context-aware:
|
|
25
25
|
* - Dev mode (source + DEXTO_DEV_MODE): Skip setup, uses repo configs
|
|
26
|
-
* -
|
|
27
|
-
* -
|
|
28
|
-
* - Has preferences (source/global-cli): Validate them
|
|
26
|
+
* - First-time user: Require setup
|
|
27
|
+
* - Has preferences: Validate them
|
|
29
28
|
* @returns true if setup is required
|
|
30
29
|
*/
|
|
31
30
|
export declare function requiresSetup(): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup-utils.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/setup-utils.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"setup-utils.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/setup-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAKH,KAAK,iBAAiB,EACzB,MAAM,yBAAyB,CAAC;AAgBjC;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACzC;AAaD;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAkFzD;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAGtD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC,CAqB/D"}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
// packages/cli/src/cli/utils/setup-utils.ts
|
|
2
|
-
import { globalPreferencesExist, loadGlobalPreferences, } from '@dexto/agent-management';
|
|
2
|
+
import { findDextoProjectRoot, findProjectRegistryPathSync, globalPreferencesExist, loadGlobalPreferences, } from '@dexto/agent-management';
|
|
3
3
|
import { getExecutionContext } from '@dexto/core';
|
|
4
|
+
import { existsSync, statSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
const PROJECT_LOCAL_CODING_AGENT_RELATIVE_PATHS = [
|
|
7
|
+
path.join('agents', 'coding-agent', 'coding-agent.yml'),
|
|
8
|
+
path.join('agents', 'coding-agent', 'coding-agent.yaml'),
|
|
9
|
+
'coding-agent.yml',
|
|
10
|
+
'coding-agent.yaml',
|
|
11
|
+
path.join('agents', 'coding-agent.yml'),
|
|
12
|
+
path.join('agents', 'coding-agent.yaml'),
|
|
13
|
+
path.join('src', 'dexto', 'agents', 'coding-agent.yml'),
|
|
14
|
+
path.join('src', 'dexto', 'agents', 'coding-agent.yaml'),
|
|
15
|
+
];
|
|
4
16
|
/**
|
|
5
17
|
* Check if this is a first-time user (no preferences file exists)
|
|
6
18
|
* @returns true if user has never run setup
|
|
@@ -8,6 +20,15 @@ import { getExecutionContext } from '@dexto/core';
|
|
|
8
20
|
export function isFirstTimeUser() {
|
|
9
21
|
return !globalPreferencesExist();
|
|
10
22
|
}
|
|
23
|
+
function hasProjectLocalStartupConfig(projectRoot) {
|
|
24
|
+
if (findProjectRegistryPathSync(projectRoot)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return PROJECT_LOCAL_CODING_AGENT_RELATIVE_PATHS.some((relativePath) => {
|
|
28
|
+
const absolutePath = path.join(projectRoot, relativePath);
|
|
29
|
+
return existsSync(absolutePath) && statSync(absolutePath).isFile();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
11
32
|
/**
|
|
12
33
|
* Get detailed setup state including pending items
|
|
13
34
|
* @returns Setup state with detailed flags
|
|
@@ -24,15 +45,17 @@ export async function getSetupState() {
|
|
|
24
45
|
preferences: null,
|
|
25
46
|
};
|
|
26
47
|
}
|
|
27
|
-
// Project context: skip (might have project-local config)
|
|
28
48
|
if (context === 'dexto-project') {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
const projectRoot = findDextoProjectRoot();
|
|
50
|
+
if (projectRoot && hasProjectLocalStartupConfig(projectRoot)) {
|
|
51
|
+
return {
|
|
52
|
+
needsSetup: false,
|
|
53
|
+
isFirstTime: false,
|
|
54
|
+
apiKeyPending: false,
|
|
55
|
+
baseURLPending: false,
|
|
56
|
+
preferences: null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
36
59
|
}
|
|
37
60
|
// First-time user (no preferences)
|
|
38
61
|
if (isFirstTimeUser()) {
|
|
@@ -91,9 +114,8 @@ export async function getSetupState() {
|
|
|
91
114
|
* Check if user requires setup (missing, corrupted, or incomplete preferences)
|
|
92
115
|
* Context-aware:
|
|
93
116
|
* - Dev mode (source + DEXTO_DEV_MODE): Skip setup, uses repo configs
|
|
94
|
-
* -
|
|
95
|
-
* -
|
|
96
|
-
* - Has preferences (source/global-cli): Validate them
|
|
117
|
+
* - First-time user: Require setup
|
|
118
|
+
* - Has preferences: Validate them
|
|
97
119
|
* @returns true if setup is required
|
|
98
120
|
*/
|
|
99
121
|
export async function requiresSetup() {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* - Merge strategy configuration for non-LLM fields
|
|
20
20
|
*/
|
|
21
21
|
import type { AgentConfig } from '@dexto/agent-config';
|
|
22
|
-
import type
|
|
22
|
+
import { type LLMConfig, type LLMProvider } from '@dexto/core';
|
|
23
23
|
import type { GlobalPreferences } from '@dexto/agent-management';
|
|
24
24
|
/**
|
|
25
25
|
* CLI config override type for fields that can be overridden via CLI
|
|
@@ -30,6 +30,12 @@ export interface CLIConfigOverrides extends Partial<Pick<LLMConfig, 'provider' |
|
|
|
30
30
|
/** When false (via --no-elicitation), disables elicitation */
|
|
31
31
|
elicitation?: boolean;
|
|
32
32
|
}
|
|
33
|
+
export interface StartupLLMFallbackOptions {
|
|
34
|
+
hasCompletedSetup: boolean;
|
|
35
|
+
hasExplicitProviderOverride: boolean;
|
|
36
|
+
hasExplicitModelOverride: boolean;
|
|
37
|
+
hasExplicitApiKeyOverride: boolean;
|
|
38
|
+
}
|
|
33
39
|
/**
|
|
34
40
|
* Applies CLI overrides to an agent configuration
|
|
35
41
|
* This merges CLI arguments into the base config without validation.
|
|
@@ -52,6 +58,8 @@ export declare function applyCLIOverrides(baseConfig: AgentConfig, cliOverrides?
|
|
|
52
58
|
* @returns Merged configuration with user preferences applied
|
|
53
59
|
*/
|
|
54
60
|
export declare function applyUserPreferences(baseConfig: AgentConfig, preferences: Partial<GlobalPreferences>): AgentConfig;
|
|
61
|
+
export declare function applyStartupLLMFallback(baseConfig: AgentConfig, options: StartupLLMFallbackOptions): AgentConfig;
|
|
62
|
+
export declare function hasUsableCredentials(provider: LLMProvider, llmConfig?: Pick<AgentConfig['llm'], 'apiKey' | 'baseURL'>): boolean;
|
|
55
63
|
/**
|
|
56
64
|
* Result of agent compatibility check
|
|
57
65
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-overrides.d.ts","sourceRoot":"","sources":["../../src/config/cli-overrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"cli-overrides.d.ts","sourceRoot":"","sources":["../../src/config/cli-overrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAOH,KAAK,SAAS,EACd,KAAK,WAAW,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,kBACb,SAAQ,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC;IACjE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACtC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,2BAA2B,EAAE,OAAO,CAAC;IACrC,wBAAwB,EAAE,OAAO,CAAC;IAClC,yBAAyB,EAAE,OAAO,CAAC;CACtC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC7B,UAAU,EAAE,WAAW,EACvB,YAAY,CAAC,EAAE,kBAAkB,GAClC,WAAW,CAuCb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAChC,UAAU,EAAE,WAAW,EACvB,WAAW,EAAE,OAAO,CAAC,iBAAiB,CAAC,GACxC,WAAW,CAwBb;AAED,wBAAgB,uBAAuB,CACnC,UAAU,EAAE,WAAW,EACvB,OAAO,EAAE,yBAAyB,GACnC,WAAW,CAyCb;AAcD,wBAAgB,oBAAoB,CAChC,QAAQ,EAAE,WAAW,EACrB,SAAS,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC,GAC3D,OAAO,CAgBT;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,WAAW,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,SAAS,CAAC;IACtC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACnC,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,iBAAiB,GAAG,IAAI,EACrC,cAAc,EAAE,MAAM,GAAG,SAAS,GACnC,wBAAwB,CAwC1B"}
|