dexto 1.6.15 → 1.6.16
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/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +645 -80
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +113 -5
- package/dist/cli/utils/image-importer.d.ts +2 -0
- package/dist/cli/utils/image-importer.d.ts.map +1 -0
- package/dist/cli/utils/image-importer.js +10 -0
- package/dist/cli/utils/prompt-helpers.d.ts +6 -0
- package/dist/cli/utils/prompt-helpers.d.ts.map +1 -1
- package/dist/cli/utils/prompt-helpers.js +12 -0
- package/dist/index-main.js +2 -10
- package/package.json +11 -11
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ToolFactoryEntry } from '@dexto/agent-config';
|
|
1
2
|
import type { Command } from 'commander';
|
|
2
3
|
type ScaffoldEntryStatus = 'created' | 'existing';
|
|
3
4
|
type RegistryUpdateStatus = 'created' | 'existing' | 'updated';
|
|
@@ -79,6 +80,11 @@ export interface InitCommandRegisterContext {
|
|
|
79
80
|
type InitAgentCommandOptions = {
|
|
80
81
|
subagent?: boolean;
|
|
81
82
|
primary?: boolean;
|
|
83
|
+
displayName?: string;
|
|
84
|
+
description?: string;
|
|
85
|
+
systemPrompt?: string;
|
|
86
|
+
greeting?: string;
|
|
87
|
+
tools?: ToolFactoryEntry[];
|
|
82
88
|
};
|
|
83
89
|
export declare function createWorkspaceScaffold(workspaceRoot?: string): Promise<WorkspaceScaffoldResult>;
|
|
84
90
|
export declare function createWorkspaceAgentScaffold(agentIdInput: string, options?: InitAgentCommandOptions, workspaceRoot?: string): Promise<WorkspaceAgentScaffoldResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
|
-
import { deriveDisplayName, findProjectRegistryPath as findSharedProjectRegistryPath, getPrimaryApiKeyEnvVar, getProjectRegistryPath as getCanonicalProjectRegistryPath, ProjectRegistrySchema, readProjectRegistry as readSharedProjectRegistry, writeConfigFile, } from '@dexto/agent-management';
|
|
2
|
+
import { createDextoAgentFromConfig, deriveDisplayName, findProjectRegistryPath as findSharedProjectRegistryPath, getPrimaryApiKeyEnvVar, getProjectRegistryPath as getCanonicalProjectRegistryPath, ProjectRegistrySchema, readProjectRegistry as readSharedProjectRegistry, writeConfigFile, } from '@dexto/agent-management';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
4
5
|
import { promises as fs } from 'node:fs';
|
|
6
|
+
import os from 'node:os';
|
|
5
7
|
import path from 'node:path';
|
|
8
|
+
import { z } from 'zod';
|
|
6
9
|
import { ExitSignal, safeExit, withAnalytics } from '../../analytics/wrapper.js';
|
|
10
|
+
import { getEffectiveLLMConfig } from '../../config/effective-llm.js';
|
|
7
11
|
import { getDeployConfigPath, isWorkspaceDeployAgent, loadDeployConfig } from './deploy/config.js';
|
|
8
12
|
import { discoverPrimaryWorkspaceAgent } from './deploy/entry-agent.js';
|
|
9
|
-
import { selectOrExit, textOrExit } from '../utils/prompt-helpers.js';
|
|
13
|
+
import { confirmOrExit, multiselectOrExit, selectOrExit, textOrExit, } from '../utils/prompt-helpers.js';
|
|
14
|
+
import { ensureImageImporterConfigured } from '../utils/image-importer.js';
|
|
10
15
|
const AGENTS_FILENAME = 'AGENTS.md';
|
|
11
16
|
const WORKSPACE_DIRECTORIES = ['agents', 'skills'];
|
|
12
17
|
const DEFAULT_AGENT_PROVIDER = 'openai';
|
|
13
18
|
const DEFAULT_AGENT_MODEL = 'gpt-5.3-codex';
|
|
14
|
-
const DEFAULT_AGENT_VERSION = '0.1.0';
|
|
15
19
|
const DEFAULT_AGENTS_MD = `<!-- dexto-workspace -->
|
|
16
20
|
|
|
17
21
|
# Dexto Workspace
|
|
@@ -27,7 +31,99 @@ This workspace can define project-specific agents and skills.
|
|
|
27
31
|
- If no workspace agent is defined, Dexto uses your global default agent locally
|
|
28
32
|
- Cloud deploys without a workspace agent use the managed cloud default agent
|
|
29
33
|
`;
|
|
34
|
+
const CORE_AGENT_TOOL_ENTRIES = [
|
|
35
|
+
{
|
|
36
|
+
type: 'builtin-tools',
|
|
37
|
+
enabledTools: ['ask_user', 'invoke_skill', 'sleep'],
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
const ALWAYS_ENABLED_AGENT_TOOL_ENTRIES = [
|
|
41
|
+
{ type: 'creator-tools' },
|
|
42
|
+
{ type: 'agent-spawner' },
|
|
43
|
+
];
|
|
44
|
+
const AGENT_TOOL_BUNDLES = [
|
|
45
|
+
{
|
|
46
|
+
id: 'workspace',
|
|
47
|
+
label: 'Filesystem & Terminal',
|
|
48
|
+
hint: 'Read files and run commands in the workspace',
|
|
49
|
+
entries: [{ type: 'filesystem-tools' }, { type: 'process-tools' }],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'research',
|
|
53
|
+
label: 'Research and web',
|
|
54
|
+
hint: 'Search the web, fetch URLs, and gather outside context',
|
|
55
|
+
entries: [
|
|
56
|
+
{
|
|
57
|
+
type: 'builtin-tools',
|
|
58
|
+
enabledTools: ['code_search', 'http_request', 'web_search'],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'planning',
|
|
64
|
+
label: 'Planning and tasks',
|
|
65
|
+
hint: 'Track todos and keep structured plans',
|
|
66
|
+
entries: [{ type: 'todo-tools' }, { type: 'plan-tools' }],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'memory',
|
|
70
|
+
label: 'Memory and history',
|
|
71
|
+
hint: 'Search conversation history, logs, and stored memories',
|
|
72
|
+
entries: [{ type: 'lifecycle-tools' }],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'automation',
|
|
76
|
+
label: 'Automation',
|
|
77
|
+
hint: 'Schedule recurring jobs and proactive work',
|
|
78
|
+
entries: [{ type: 'scheduler-tools' }],
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
const DEFAULT_AGENT_TOOL_BUNDLE_IDS = ['workspace', 'planning'];
|
|
30
82
|
const ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
83
|
+
const PROMPT_GENERATOR_AGENT_ID = 'init-agent-prompt-generator';
|
|
84
|
+
const GeneratedSystemPromptPayloadSchema = z
|
|
85
|
+
.object({
|
|
86
|
+
systemPrompt: z.string().trim().min(1),
|
|
87
|
+
})
|
|
88
|
+
.strict();
|
|
89
|
+
const AGENT_PROMPT_GENERATOR_SYSTEM_PROMPT = [
|
|
90
|
+
'You design production-grade system prompts for Dexto agents.',
|
|
91
|
+
'Your output will be written directly into agent YAML.',
|
|
92
|
+
'',
|
|
93
|
+
'Return only valid JSON with this exact shape:',
|
|
94
|
+
'{"systemPrompt":"..."}',
|
|
95
|
+
'',
|
|
96
|
+
'Prompt requirements:',
|
|
97
|
+
'- Start with "You are <agent name>..."',
|
|
98
|
+
'- Turn the role description into a strong, practical operating prompt',
|
|
99
|
+
'- Keep it general enough to work across different workspaces',
|
|
100
|
+
'- Focus on responsibilities, operating principles, communication style, and constraints',
|
|
101
|
+
'- Instruct the agent to understand the current workspace and context before acting',
|
|
102
|
+
'- Tell the agent to surface risks, assumptions, and follow-up work when relevant',
|
|
103
|
+
'- Mention tools abstractly instead of naming specific tool ids unless explicitly requested',
|
|
104
|
+
'- If the agent is a subagent, include delegation guidance for working on behalf of a parent agent',
|
|
105
|
+
'- Do not include markdown code fences or extra wrapper text',
|
|
106
|
+
].join('\n');
|
|
107
|
+
function buildInteractiveAgentIdentity(nameInput) {
|
|
108
|
+
const trimmed = nameInput.trim();
|
|
109
|
+
if (!trimmed) {
|
|
110
|
+
throw new Error('Agent name is required.');
|
|
111
|
+
}
|
|
112
|
+
const agentId = trimmed
|
|
113
|
+
.toLowerCase()
|
|
114
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
115
|
+
.replace(/^-+|-+$/g, '');
|
|
116
|
+
if (!agentId) {
|
|
117
|
+
throw new Error('Agent name must include letters or numbers.');
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
agentId: normalizeScaffoldId(agentId, 'agent'),
|
|
121
|
+
displayName: /[\sA-Z]/.test(trimmed) ? trimmed : deriveDisplayName(agentId),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function getAgentDisplayName(agentId, options) {
|
|
125
|
+
return options.displayName?.trim() || deriveDisplayName(agentId);
|
|
126
|
+
}
|
|
31
127
|
function isSubagentEntry(entry) {
|
|
32
128
|
return (entry.tags?.includes('subagent') ?? false) || Boolean(entry.parentAgentId);
|
|
33
129
|
}
|
|
@@ -118,6 +214,9 @@ async function loadInitialAgentLlmConfig() {
|
|
|
118
214
|
};
|
|
119
215
|
}
|
|
120
216
|
function buildAgentDescription(agentId, options) {
|
|
217
|
+
if (options.description?.trim()) {
|
|
218
|
+
return options.description.trim();
|
|
219
|
+
}
|
|
121
220
|
if (options.subagent) {
|
|
122
221
|
return `Workspace sub-agent '${agentId}' for delegated tasks.`;
|
|
123
222
|
}
|
|
@@ -126,107 +225,463 @@ function buildAgentDescription(agentId, options) {
|
|
|
126
225
|
}
|
|
127
226
|
return `Workspace agent '${agentId}' for this project.`;
|
|
128
227
|
}
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
description,
|
|
144
|
-
url: `https://example.com/agents/${agentId}`,
|
|
145
|
-
version: DEFAULT_AGENT_VERSION,
|
|
146
|
-
},
|
|
147
|
-
systemPrompt: options.subagent
|
|
228
|
+
function buildDefaultGeneratedPrompt(displayName, options) {
|
|
229
|
+
const roleLabel = options.subagent ? 'specialized workspace subagent' : 'workspace agent';
|
|
230
|
+
return [
|
|
231
|
+
`You are ${displayName}, a ${roleLabel}.`,
|
|
232
|
+
'',
|
|
233
|
+
'Your role and focus:',
|
|
234
|
+
'- Replace this section with the responsibilities you want this agent to own.',
|
|
235
|
+
'',
|
|
236
|
+
'Operating principles:',
|
|
237
|
+
'- Read the relevant files and current state before taking action.',
|
|
238
|
+
'- Keep responses concrete, direct, and grounded in the workspace.',
|
|
239
|
+
'- Make the smallest correct change that moves the task forward.',
|
|
240
|
+
'- Call out assumptions, risks, and follow-up work clearly.',
|
|
241
|
+
...(options.subagent
|
|
148
242
|
? [
|
|
149
|
-
`You are ${displayName}, a specialized sub-agent for this workspace.`,
|
|
150
|
-
'',
|
|
151
|
-
'Complete delegated tasks efficiently and concisely.',
|
|
152
|
-
'Read the relevant files before responding.',
|
|
153
|
-
'Return a clear result to the parent agent with concrete findings or next steps.',
|
|
154
|
-
].join('\n')
|
|
155
|
-
: [
|
|
156
|
-
`You are ${displayName}, the workspace agent for this project.`,
|
|
157
243
|
'',
|
|
158
|
-
'
|
|
159
|
-
'
|
|
160
|
-
|
|
161
|
-
]
|
|
162
|
-
|
|
163
|
-
? `Ready to help as ${displayName}.`
|
|
164
|
-
: 'Ready to work in this workspace.',
|
|
165
|
-
llm,
|
|
166
|
-
permissions: {
|
|
167
|
-
mode: 'manual',
|
|
168
|
-
allowedToolsStorage: 'storage',
|
|
169
|
-
},
|
|
170
|
-
};
|
|
244
|
+
'Delegation guidance:',
|
|
245
|
+
'- Complete delegated work efficiently and return a crisp result to the parent agent.',
|
|
246
|
+
]
|
|
247
|
+
: []),
|
|
248
|
+
].join('\n');
|
|
171
249
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
250
|
+
function buildScaffoldSystemPrompt(displayName, options) {
|
|
251
|
+
if (options.subagent) {
|
|
252
|
+
return [
|
|
253
|
+
`You are ${displayName}, a specialized sub-agent for this workspace.`,
|
|
254
|
+
'',
|
|
255
|
+
'Complete delegated tasks efficiently and concisely.',
|
|
256
|
+
'Read the relevant files before responding.',
|
|
257
|
+
'Return a clear result to the parent agent with concrete findings or next steps.',
|
|
258
|
+
].join('\n');
|
|
259
|
+
}
|
|
260
|
+
return [
|
|
261
|
+
`You are ${displayName}, the workspace agent for this project.`,
|
|
262
|
+
'',
|
|
263
|
+
'Help the user understand, edit, run, and deploy the files in this workspace.',
|
|
264
|
+
'Read relevant files before making changes.',
|
|
265
|
+
'Keep changes focused and explain what changed.',
|
|
266
|
+
].join('\n');
|
|
267
|
+
}
|
|
268
|
+
function buildSystemPromptConfig(systemPrompt) {
|
|
174
269
|
return {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
270
|
+
contributors: [
|
|
271
|
+
{
|
|
272
|
+
id: 'primary',
|
|
273
|
+
type: 'static',
|
|
274
|
+
priority: 0,
|
|
275
|
+
content: systemPrompt,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: 'date',
|
|
279
|
+
type: 'dynamic',
|
|
280
|
+
priority: 10,
|
|
281
|
+
source: 'date',
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: 'env',
|
|
285
|
+
type: 'dynamic',
|
|
286
|
+
priority: 15,
|
|
287
|
+
source: 'env',
|
|
288
|
+
},
|
|
289
|
+
],
|
|
180
290
|
};
|
|
181
291
|
}
|
|
182
|
-
function
|
|
183
|
-
const
|
|
184
|
-
|
|
292
|
+
function extractJsonObjectFromResponse(content) {
|
|
293
|
+
const trimmed = content.trim();
|
|
294
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
295
|
+
return trimmed;
|
|
296
|
+
}
|
|
297
|
+
const fencedMatch = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
298
|
+
if (fencedMatch?.[1]) {
|
|
299
|
+
return fencedMatch[1].trim();
|
|
300
|
+
}
|
|
301
|
+
const firstBrace = trimmed.indexOf('{');
|
|
302
|
+
const lastBrace = trimmed.lastIndexOf('}');
|
|
303
|
+
if (firstBrace >= 0 && lastBrace > firstBrace) {
|
|
304
|
+
return trimmed.slice(firstBrace, lastBrace + 1);
|
|
305
|
+
}
|
|
306
|
+
throw new Error('Prompt generator did not return valid JSON.');
|
|
307
|
+
}
|
|
308
|
+
function parseGeneratedSystemPromptResponse(content) {
|
|
309
|
+
const parsed = JSON.parse(extractJsonObjectFromResponse(content));
|
|
310
|
+
return GeneratedSystemPromptPayloadSchema.parse(parsed).systemPrompt;
|
|
311
|
+
}
|
|
312
|
+
function buildPromptGenerationRequest(displayName, roleDescription, options) {
|
|
313
|
+
return [
|
|
314
|
+
'Generate a Dexto system prompt from this brief.',
|
|
315
|
+
'',
|
|
316
|
+
`Agent name: ${displayName}`,
|
|
317
|
+
`Agent type: ${options.subagent ? 'workspace subagent' : 'workspace agent'}`,
|
|
318
|
+
`Role description: ${normalizeInteractiveDescription(roleDescription)}`,
|
|
319
|
+
'',
|
|
320
|
+
'Additional guidance:',
|
|
321
|
+
'- Make the prompt concrete and opinionated, not generic filler',
|
|
322
|
+
'- Prefer short sections and useful bullet points over long prose',
|
|
323
|
+
'- Assume tool access may vary by workspace, so keep tool guidance capability-based',
|
|
324
|
+
'- Avoid references to specific repositories, companies, or file names',
|
|
325
|
+
'',
|
|
326
|
+
'Return JSON only.',
|
|
327
|
+
].join('\n');
|
|
328
|
+
}
|
|
329
|
+
async function generateAgentSystemPromptFromDescription(displayName, roleDescription, options) {
|
|
330
|
+
await ensureImageImporterConfigured();
|
|
331
|
+
const effectiveLLM = await getEffectiveLLMConfig();
|
|
332
|
+
if (!effectiveLLM) {
|
|
333
|
+
throw new Error('No active LLM configuration is available for prompt generation. Configure one with `dexto setup` first.');
|
|
334
|
+
}
|
|
335
|
+
const spinner = p.spinner();
|
|
336
|
+
spinner.start('Generating system prompt...');
|
|
337
|
+
const generatorAgent = await createDextoAgentFromConfig({
|
|
338
|
+
agentIdOverride: PROMPT_GENERATOR_AGENT_ID,
|
|
339
|
+
enrichOptions: {
|
|
340
|
+
isInteractiveCli: false,
|
|
341
|
+
skipPluginDiscovery: true,
|
|
342
|
+
},
|
|
343
|
+
config: {
|
|
344
|
+
image: '@dexto/image-local',
|
|
345
|
+
systemPrompt: buildSystemPromptConfig(AGENT_PROMPT_GENERATOR_SYSTEM_PROMPT),
|
|
346
|
+
llm: {
|
|
347
|
+
provider: effectiveLLM.provider,
|
|
348
|
+
model: effectiveLLM.model,
|
|
349
|
+
...(effectiveLLM.apiKey ? { apiKey: effectiveLLM.apiKey } : {}),
|
|
350
|
+
...(effectiveLLM.baseURL ? { baseURL: effectiveLLM.baseURL } : {}),
|
|
351
|
+
},
|
|
352
|
+
storage: {
|
|
353
|
+
cache: { type: 'in-memory' },
|
|
354
|
+
database: { type: 'in-memory' },
|
|
355
|
+
blob: { type: 'in-memory' },
|
|
356
|
+
},
|
|
357
|
+
permissions: {
|
|
358
|
+
mode: 'auto-deny',
|
|
359
|
+
allowedToolsStorage: 'memory',
|
|
360
|
+
},
|
|
361
|
+
elicitation: {
|
|
362
|
+
enabled: false,
|
|
363
|
+
},
|
|
364
|
+
tools: [],
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
try {
|
|
368
|
+
await generatorAgent.start();
|
|
369
|
+
const session = await generatorAgent.createSession(PROMPT_GENERATOR_AGENT_ID);
|
|
370
|
+
const response = await generatorAgent.generate(buildPromptGenerationRequest(displayName, roleDescription, options), session.id);
|
|
371
|
+
const systemPrompt = parseGeneratedSystemPromptResponse(response.content);
|
|
372
|
+
spinner.stop(chalk.green('Generated system prompt'));
|
|
373
|
+
return systemPrompt;
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
spinner.stop(chalk.red('Failed to generate system prompt'));
|
|
377
|
+
throw new Error(`Could not generate a system prompt automatically: ${error instanceof Error ? error.message : String(error)}`);
|
|
378
|
+
}
|
|
379
|
+
finally {
|
|
380
|
+
await generatorAgent.stop().catch(() => undefined);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function encodePromptForTextInput(systemPrompt) {
|
|
384
|
+
return systemPrompt.replace(/\n/g, '\\n');
|
|
385
|
+
}
|
|
386
|
+
function decodePromptFromTextInput(systemPrompt) {
|
|
387
|
+
return systemPrompt.replace(/\\n/g, '\n').trim();
|
|
388
|
+
}
|
|
389
|
+
function normalizeInteractiveDescription(description) {
|
|
390
|
+
const trimmed = description.trim().replace(/\s+/g, ' ');
|
|
391
|
+
if (!trimmed) {
|
|
392
|
+
return trimmed;
|
|
393
|
+
}
|
|
394
|
+
return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
|
|
395
|
+
}
|
|
396
|
+
function cloneToolEntry(entry) {
|
|
397
|
+
const maybeEnabledTools = entry.enabledTools;
|
|
185
398
|
return {
|
|
186
399
|
...entry,
|
|
187
|
-
|
|
400
|
+
...(Array.isArray(maybeEnabledTools)
|
|
401
|
+
? {
|
|
402
|
+
enabledTools: maybeEnabledTools.filter((tool) => typeof tool === 'string'),
|
|
403
|
+
}
|
|
404
|
+
: {}),
|
|
188
405
|
};
|
|
189
406
|
}
|
|
190
|
-
function
|
|
191
|
-
|
|
192
|
-
|
|
407
|
+
function mergeToolEntries(entries) {
|
|
408
|
+
const merged = new Map();
|
|
409
|
+
for (const entry of entries) {
|
|
410
|
+
const existing = merged.get(entry.type);
|
|
411
|
+
if (!existing) {
|
|
412
|
+
merged.set(entry.type, cloneToolEntry(entry));
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
const existingEnabledTools = existing.enabledTools;
|
|
416
|
+
const nextEnabledTools = entry.enabledTools;
|
|
417
|
+
if (Array.isArray(existingEnabledTools) && Array.isArray(nextEnabledTools)) {
|
|
418
|
+
merged.set(entry.type, {
|
|
419
|
+
...existing,
|
|
420
|
+
enabledTools: Array.from(new Set([
|
|
421
|
+
...existingEnabledTools.filter((tool) => typeof tool === 'string'),
|
|
422
|
+
...nextEnabledTools.filter((tool) => typeof tool === 'string'),
|
|
423
|
+
])),
|
|
424
|
+
});
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
if (!Array.isArray(existingEnabledTools) && Array.isArray(nextEnabledTools)) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
if (Array.isArray(existingEnabledTools) && !Array.isArray(nextEnabledTools)) {
|
|
431
|
+
merged.set(entry.type, cloneToolEntry(entry));
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
merged.set(entry.type, cloneToolEntry(entry));
|
|
435
|
+
}
|
|
436
|
+
return Array.from(merged.values());
|
|
437
|
+
}
|
|
438
|
+
function buildToolConfigFromBundleIds(bundleIds) {
|
|
439
|
+
const selectedBundleEntries = bundleIds.flatMap((bundleId) => {
|
|
440
|
+
const bundle = AGENT_TOOL_BUNDLES.find((entry) => entry.id === bundleId);
|
|
441
|
+
return bundle?.entries ?? [];
|
|
442
|
+
});
|
|
443
|
+
return mergeToolEntries([
|
|
444
|
+
...CORE_AGENT_TOOL_ENTRIES,
|
|
445
|
+
...ALWAYS_ENABLED_AGENT_TOOL_ENTRIES,
|
|
446
|
+
...selectedBundleEntries,
|
|
447
|
+
]);
|
|
448
|
+
}
|
|
449
|
+
function formatBundleSelection(bundleIds) {
|
|
450
|
+
if (bundleIds.length === 0) {
|
|
451
|
+
return 'Core utilities only';
|
|
452
|
+
}
|
|
453
|
+
return bundleIds
|
|
454
|
+
.map((bundleId) => AGENT_TOOL_BUNDLES.find((bundle) => bundle.id === bundleId)?.label ?? bundleId)
|
|
455
|
+
.join(', ');
|
|
456
|
+
}
|
|
457
|
+
function renderPromptPreview(systemPrompt) {
|
|
458
|
+
console.log(`\n${chalk.cyan.bold('System Prompt Preview')}`);
|
|
459
|
+
console.log(chalk.dim('Review the full prompt before continuing.\n'));
|
|
460
|
+
console.log(systemPrompt);
|
|
461
|
+
console.log();
|
|
462
|
+
}
|
|
463
|
+
function getPreferredEditorCommand() {
|
|
464
|
+
const envEditor = process.env.VISUAL?.trim() || process.env.EDITOR?.trim();
|
|
465
|
+
if (envEditor) {
|
|
466
|
+
return envEditor;
|
|
193
467
|
}
|
|
468
|
+
return process.platform === 'win32' ? 'notepad' : 'vi';
|
|
194
469
|
}
|
|
195
|
-
async function
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
470
|
+
async function openFileInEditor(editorCommand, filePath) {
|
|
471
|
+
await new Promise((resolve, reject) => {
|
|
472
|
+
const child = spawn(editorCommand, [filePath], {
|
|
473
|
+
stdio: 'inherit',
|
|
474
|
+
shell: true,
|
|
475
|
+
});
|
|
476
|
+
child.once('error', reject);
|
|
477
|
+
child.once('exit', (code) => {
|
|
478
|
+
if (code === 0) {
|
|
479
|
+
resolve();
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
reject(new Error(`Editor exited with code ${code ?? 'unknown'}.`));
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
async function editSystemPromptInEditor(displayName, options, initialPrompt) {
|
|
487
|
+
const editorCommand = getPreferredEditorCommand();
|
|
488
|
+
const initialContent = initialPrompt ?? buildDefaultGeneratedPrompt(displayName, options);
|
|
489
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'dexto-agent-prompt-'));
|
|
490
|
+
const promptPath = path.join(tempDir, 'system-prompt.md');
|
|
491
|
+
try {
|
|
492
|
+
await fs.writeFile(promptPath, `${initialContent}\n`, 'utf8');
|
|
493
|
+
p.log.info(`Opening ${chalk.cyan(editorCommand)} for prompt editing. Save and close the editor to continue.`);
|
|
494
|
+
while (true) {
|
|
495
|
+
await openFileInEditor(editorCommand, promptPath);
|
|
496
|
+
const editedPrompt = (await fs.readFile(promptPath, 'utf8')).trim();
|
|
497
|
+
if (editedPrompt) {
|
|
498
|
+
return editedPrompt;
|
|
499
|
+
}
|
|
500
|
+
p.log.warn('System prompt was empty. Reopening the editor.');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
finally {
|
|
504
|
+
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async function promptForAgentName(initialValue) {
|
|
508
|
+
const rawName = await textOrExit({
|
|
509
|
+
message: 'Agent name',
|
|
510
|
+
...(initialValue ? { initialValue } : {}),
|
|
511
|
+
placeholder: 'Review Agent',
|
|
204
512
|
validate(value) {
|
|
205
513
|
try {
|
|
206
|
-
|
|
514
|
+
buildInteractiveAgentIdentity(value);
|
|
207
515
|
return undefined;
|
|
208
516
|
}
|
|
209
517
|
catch (error) {
|
|
210
|
-
return error instanceof Error ? error.message : 'Invalid agent
|
|
518
|
+
return error instanceof Error ? error.message : 'Invalid agent name';
|
|
211
519
|
}
|
|
212
520
|
},
|
|
213
521
|
}, 'Agent initialization cancelled');
|
|
522
|
+
return buildInteractiveAgentIdentity(rawName);
|
|
214
523
|
}
|
|
215
|
-
async function
|
|
216
|
-
|
|
217
|
-
|
|
524
|
+
async function promptForAvailableAgentName(workspaceRoot) {
|
|
525
|
+
const resolvedWorkspaceRoot = path.resolve(workspaceRoot);
|
|
526
|
+
let initialValue;
|
|
527
|
+
while (true) {
|
|
528
|
+
const identity = await promptForAgentName(initialValue);
|
|
529
|
+
const registryState = await loadWorkspaceProjectRegistry(resolvedWorkspaceRoot);
|
|
530
|
+
const existingEntry = getWorkspaceAgentEntry(registryState.registry, identity.agentId);
|
|
531
|
+
if (!existingEntry) {
|
|
532
|
+
return identity;
|
|
533
|
+
}
|
|
534
|
+
p.log.warn(`Agent '${identity.agentId}' already exists in ${path.relative(resolvedWorkspaceRoot, registryState.path)}. Choose a different name or run \`dexto init agent ${identity.agentId}\` to update the existing agent.`);
|
|
535
|
+
initialValue = identity.displayName;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
async function promptForCustomSystemPrompt(displayName, options, initialPrompt) {
|
|
539
|
+
try {
|
|
540
|
+
return await editSystemPromptInEditor(displayName, options, initialPrompt);
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
543
|
+
p.log.warn(`Could not open an editor cleanly. Falling back to inline prompt editing. ${error instanceof Error ? error.message : String(error)}`);
|
|
544
|
+
const rawPrompt = await textOrExit({
|
|
545
|
+
message: 'System prompt (use \\n for line breaks)',
|
|
546
|
+
initialValue: encodePromptForTextInput(initialPrompt ?? buildDefaultGeneratedPrompt(displayName, options)),
|
|
547
|
+
placeholder: encodePromptForTextInput(buildDefaultGeneratedPrompt(displayName, options)),
|
|
548
|
+
validate(value) {
|
|
549
|
+
const decoded = decodePromptFromTextInput(value);
|
|
550
|
+
return decoded ? undefined : 'System prompt is required';
|
|
551
|
+
},
|
|
552
|
+
}, 'Agent initialization cancelled');
|
|
553
|
+
return decodePromptFromTextInput(rawPrompt);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function promptForReviewedCustomSystemPrompt(displayName, options, initialPrompt) {
|
|
557
|
+
let customPrompt = initialPrompt ?? '';
|
|
558
|
+
while (true) {
|
|
559
|
+
customPrompt = await promptForCustomSystemPrompt(displayName, options, customPrompt);
|
|
560
|
+
renderPromptPreview(customPrompt);
|
|
561
|
+
const confirmed = await confirmOrExit({
|
|
562
|
+
message: 'Use this system prompt?',
|
|
563
|
+
initialValue: true,
|
|
564
|
+
}, 'Agent initialization cancelled');
|
|
565
|
+
if (confirmed) {
|
|
566
|
+
return customPrompt;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
async function promptForAgentSystemPrompt(displayName, options) {
|
|
571
|
+
const effectiveLLM = await getEffectiveLLMConfig();
|
|
572
|
+
if (!effectiveLLM) {
|
|
573
|
+
p.log.info('No active LLM configuration found. Opening the prompt editor instead. Run `dexto setup` to enable automatic prompt generation.');
|
|
218
574
|
return {
|
|
219
|
-
|
|
220
|
-
options,
|
|
575
|
+
mode: 'custom',
|
|
576
|
+
systemPrompt: await promptForReviewedCustomSystemPrompt(displayName, options),
|
|
577
|
+
description: null,
|
|
221
578
|
};
|
|
222
579
|
}
|
|
223
|
-
|
|
224
|
-
|
|
580
|
+
const promptMode = await selectOrExit({
|
|
581
|
+
message: 'How do you want to create the system prompt?',
|
|
582
|
+
initialValue: 'generate',
|
|
583
|
+
options: [
|
|
584
|
+
{
|
|
585
|
+
value: 'generate',
|
|
586
|
+
label: 'Generate from description',
|
|
587
|
+
hint: 'Start from a short role description and review the full prompt',
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
value: 'custom',
|
|
591
|
+
label: 'Write custom prompt',
|
|
592
|
+
hint: 'Enter your own prompt text directly',
|
|
593
|
+
},
|
|
594
|
+
],
|
|
595
|
+
}, 'Agent initialization cancelled');
|
|
596
|
+
if (promptMode === 'custom') {
|
|
225
597
|
return {
|
|
226
|
-
|
|
227
|
-
options,
|
|
598
|
+
mode: 'custom',
|
|
599
|
+
systemPrompt: await promptForReviewedCustomSystemPrompt(displayName, options),
|
|
600
|
+
description: null,
|
|
228
601
|
};
|
|
229
602
|
}
|
|
603
|
+
let roleDescription = await textOrExit({
|
|
604
|
+
message: 'Describe this agent’s role',
|
|
605
|
+
placeholder: 'Reviews code changes, finds risks, and suggests focused fixes.',
|
|
606
|
+
validate(value) {
|
|
607
|
+
return value.trim() ? undefined : 'Role description is required';
|
|
608
|
+
},
|
|
609
|
+
}, 'Agent initialization cancelled');
|
|
610
|
+
let systemPrompt = await generateAgentSystemPromptFromDescription(displayName, roleDescription, options);
|
|
611
|
+
while (true) {
|
|
612
|
+
renderPromptPreview(systemPrompt);
|
|
613
|
+
const action = await selectOrExit({
|
|
614
|
+
message: 'What do you want to do with this prompt?',
|
|
615
|
+
initialValue: 'continue',
|
|
616
|
+
options: [
|
|
617
|
+
{
|
|
618
|
+
value: 'continue',
|
|
619
|
+
label: 'Continue',
|
|
620
|
+
hint: 'Use this prompt as-is',
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
value: 'edit',
|
|
624
|
+
label: 'Edit prompt',
|
|
625
|
+
hint: 'Make direct changes to the generated prompt',
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
value: 'regenerate',
|
|
629
|
+
label: 'Regenerate',
|
|
630
|
+
hint: 'Update the role description and rebuild the prompt',
|
|
631
|
+
},
|
|
632
|
+
],
|
|
633
|
+
}, 'Agent initialization cancelled');
|
|
634
|
+
if (action === 'continue') {
|
|
635
|
+
return {
|
|
636
|
+
mode: 'generate',
|
|
637
|
+
systemPrompt,
|
|
638
|
+
description: normalizeInteractiveDescription(roleDescription),
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
if (action === 'edit') {
|
|
642
|
+
systemPrompt = await promptForCustomSystemPrompt(displayName, options, systemPrompt);
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
roleDescription = await textOrExit({
|
|
646
|
+
message: 'Describe this agent’s role',
|
|
647
|
+
initialValue: roleDescription,
|
|
648
|
+
validate(value) {
|
|
649
|
+
return value.trim() ? undefined : 'Role description is required';
|
|
650
|
+
},
|
|
651
|
+
}, 'Agent initialization cancelled');
|
|
652
|
+
systemPrompt = await generateAgentSystemPromptFromDescription(displayName, roleDescription, options);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
async function promptForAgentToolBundles() {
|
|
656
|
+
return await multiselectOrExit({
|
|
657
|
+
message: 'Select tool bundles (agent creation is enabled by default)',
|
|
658
|
+
initialValues: DEFAULT_AGENT_TOOL_BUNDLE_IDS,
|
|
659
|
+
options: AGENT_TOOL_BUNDLES.map((bundle) => ({
|
|
660
|
+
value: bundle.id,
|
|
661
|
+
label: bundle.label,
|
|
662
|
+
hint: bundle.hint,
|
|
663
|
+
})),
|
|
664
|
+
}, 'Agent initialization cancelled');
|
|
665
|
+
}
|
|
666
|
+
async function describePlannedAgentRole(options, workspaceRoot) {
|
|
667
|
+
const registryState = await loadWorkspaceProjectRegistry(path.resolve(workspaceRoot));
|
|
668
|
+
const currentPrimaryAgentId = getEffectiveWorkspacePrimaryAgentId(registryState.registry);
|
|
669
|
+
if (options.subagent) {
|
|
670
|
+
return currentPrimaryAgentId
|
|
671
|
+
? `Subagent (will link to ${currentPrimaryAgentId})`
|
|
672
|
+
: 'Subagent (no primary agent available yet)';
|
|
673
|
+
}
|
|
674
|
+
if (options.primary) {
|
|
675
|
+
return currentPrimaryAgentId
|
|
676
|
+
? `Primary agent (replaces ${currentPrimaryAgentId})`
|
|
677
|
+
: 'Primary agent';
|
|
678
|
+
}
|
|
679
|
+
return currentPrimaryAgentId ? 'Additional agent' : 'Primary agent (first workspace agent)';
|
|
680
|
+
}
|
|
681
|
+
async function resolveInteractiveAgentRoleOptions(options, workspaceRoot) {
|
|
682
|
+
if (options.subagent || options.primary) {
|
|
683
|
+
return options;
|
|
684
|
+
}
|
|
230
685
|
const registryState = await loadWorkspaceProjectRegistry(path.resolve(workspaceRoot));
|
|
231
686
|
const currentPrimaryAgentId = getEffectiveWorkspacePrimaryAgentId(registryState.registry);
|
|
232
687
|
const kind = await selectOrExit({
|
|
@@ -252,10 +707,116 @@ async function resolveInitAgentInput(agentIdInput, options, workspaceRoot) {
|
|
|
252
707
|
},
|
|
253
708
|
],
|
|
254
709
|
}, 'Agent initialization cancelled');
|
|
255
|
-
const resolvedOptions = kind === 'primary' ? { primary: true } : kind === 'subagent' ? { subagent: true } : {};
|
|
256
710
|
return {
|
|
257
|
-
|
|
258
|
-
|
|
711
|
+
...options,
|
|
712
|
+
...(kind === 'primary' ? { primary: true } : {}),
|
|
713
|
+
...(kind === 'subagent' ? { subagent: true } : {}),
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
async function buildAgentConfig(agentId, options) {
|
|
717
|
+
const llmConfig = await loadInitialAgentLlmConfig();
|
|
718
|
+
const displayName = getAgentDisplayName(agentId, options);
|
|
719
|
+
const systemPrompt = options.systemPrompt ?? buildScaffoldSystemPrompt(displayName, options);
|
|
720
|
+
const usesAskUser = options.tools === undefined ||
|
|
721
|
+
options.tools.some((entry) => {
|
|
722
|
+
if (entry.type !== 'builtin-tools' || entry.enabled === false) {
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
const maybeEnabledTools = entry.enabledTools;
|
|
726
|
+
return (!Array.isArray(maybeEnabledTools) ||
|
|
727
|
+
maybeEnabledTools.some((tool) => tool === 'ask_user'));
|
|
728
|
+
});
|
|
729
|
+
const llm = {
|
|
730
|
+
provider: llmConfig.provider,
|
|
731
|
+
model: llmConfig.model,
|
|
732
|
+
apiKey: llmConfig.apiKey,
|
|
733
|
+
};
|
|
734
|
+
return {
|
|
735
|
+
image: '@dexto/image-local',
|
|
736
|
+
systemPrompt: buildSystemPromptConfig(systemPrompt),
|
|
737
|
+
greeting: options.greeting ??
|
|
738
|
+
(options.subagent
|
|
739
|
+
? `Ready to help as ${displayName}.`
|
|
740
|
+
: 'Ready to work in this workspace.'),
|
|
741
|
+
llm,
|
|
742
|
+
...(options.tools ? { tools: options.tools } : {}),
|
|
743
|
+
permissions: {
|
|
744
|
+
mode: 'manual',
|
|
745
|
+
allowedToolsStorage: 'storage',
|
|
746
|
+
},
|
|
747
|
+
...(usesAskUser
|
|
748
|
+
? {
|
|
749
|
+
elicitation: {
|
|
750
|
+
enabled: true,
|
|
751
|
+
},
|
|
752
|
+
}
|
|
753
|
+
: {}),
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function buildRegistryEntry(agentId, options) {
|
|
757
|
+
const description = buildAgentDescription(agentId, options);
|
|
758
|
+
return {
|
|
759
|
+
id: agentId,
|
|
760
|
+
name: getAgentDisplayName(agentId, options),
|
|
761
|
+
description,
|
|
762
|
+
configPath: `./${agentId}/${agentId}.yml`,
|
|
763
|
+
...(options.subagent ? { tags: ['subagent'] } : {}),
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function addSubagentTag(entry) {
|
|
767
|
+
const tags = new Set(entry.tags ?? []);
|
|
768
|
+
tags.add('subagent');
|
|
769
|
+
return {
|
|
770
|
+
...entry,
|
|
771
|
+
tags: Array.from(tags).sort(),
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
function validateInitAgentOptions(options) {
|
|
775
|
+
if (options.primary && options.subagent) {
|
|
776
|
+
throw new Error('A sub-agent cannot also be the primary workspace agent.');
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
async function resolveInitAgentInput(agentIdInput, options, workspaceRoot) {
|
|
780
|
+
validateInitAgentOptions(options);
|
|
781
|
+
if (agentIdInput) {
|
|
782
|
+
return {
|
|
783
|
+
agentId: normalizeScaffoldId(agentIdInput, 'agent'),
|
|
784
|
+
options,
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
const resolvedOptions = await resolveInteractiveAgentRoleOptions(options, workspaceRoot);
|
|
788
|
+
const identity = await promptForAvailableAgentName(workspaceRoot);
|
|
789
|
+
const promptResult = await promptForAgentSystemPrompt(identity.displayName, resolvedOptions);
|
|
790
|
+
const bundleIds = await promptForAgentToolBundles();
|
|
791
|
+
const roleSummary = await describePlannedAgentRole(resolvedOptions, workspaceRoot);
|
|
792
|
+
const selectedTools = buildToolConfigFromBundleIds(bundleIds);
|
|
793
|
+
p.note([
|
|
794
|
+
`${chalk.cyan('Name:')} ${chalk.bold(identity.displayName)}`,
|
|
795
|
+
`${chalk.cyan('Id:')} ${chalk.dim(identity.agentId)}`,
|
|
796
|
+
`${chalk.cyan('Workspace role:')} ${roleSummary}`,
|
|
797
|
+
`${chalk.cyan('System prompt:')} ${promptResult.mode === 'generate' ? 'Generated from description' : 'Custom'}`,
|
|
798
|
+
`${chalk.cyan('Tool bundles:')} ${formatBundleSelection(bundleIds)}`,
|
|
799
|
+
`${chalk.cyan('Core utilities:')} ask_user, invoke_skill, sleep`,
|
|
800
|
+
`${chalk.cyan('Agent creation:')} enabled by default`,
|
|
801
|
+
].join('\n'), 'Agent Summary');
|
|
802
|
+
const confirmed = await confirmOrExit({
|
|
803
|
+
message: 'Create this agent?',
|
|
804
|
+
initialValue: true,
|
|
805
|
+
}, 'Agent initialization cancelled');
|
|
806
|
+
if (!confirmed) {
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
return {
|
|
810
|
+
agentId: identity.agentId,
|
|
811
|
+
options: {
|
|
812
|
+
...resolvedOptions,
|
|
813
|
+
displayName: identity.displayName,
|
|
814
|
+
description: promptResult.description ??
|
|
815
|
+
buildAgentDescription(identity.agentId, resolvedOptions),
|
|
816
|
+
systemPrompt: promptResult.systemPrompt,
|
|
817
|
+
greeting: `Ready to help as ${identity.displayName}.`,
|
|
818
|
+
tools: selectedTools,
|
|
819
|
+
},
|
|
259
820
|
};
|
|
260
821
|
}
|
|
261
822
|
function buildSkillTemplate(skillId) {
|
|
@@ -684,6 +1245,10 @@ export async function handleInitCommand(workspaceRoot = process.cwd()) {
|
|
|
684
1245
|
export async function handleInitAgentCommand(agentIdInput, options = {}, workspaceRoot = process.cwd()) {
|
|
685
1246
|
p.intro(chalk.inverse('Dexto Init Agent'));
|
|
686
1247
|
const resolved = await resolveInitAgentInput(agentIdInput, options, workspaceRoot);
|
|
1248
|
+
if (!resolved) {
|
|
1249
|
+
p.outro(chalk.yellow('Agent initialization cancelled.'));
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
687
1252
|
const result = await createWorkspaceAgentScaffold(resolved.agentId, resolved.options, workspaceRoot);
|
|
688
1253
|
const subagentLinkResult = resolved.options.subagent
|
|
689
1254
|
? await linkWorkspaceSubagentToPrimaryAgent(resolved.agentId, workspaceRoot)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/setup.ts"],"names":[],"mappings":"
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0ClB,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;AAqKtE;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC9F"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
// packages/cli/src/cli/commands/setup.ts
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
2
4
|
import chalk from 'chalk';
|
|
3
5
|
import { z } from 'zod';
|
|
4
6
|
import open from 'open';
|
|
5
7
|
import { acceptsAnyModel, CodexAppServerClient, createCodexBaseURL, getDefaultModelForProvider, getCodexAuthModeLabel, getCodexProviderDisplayName, getCuratedModelsForProvider, getReasoningProfile, getSupportedModels, isCodexBaseURL, LLM_PROVIDERS, LLM_REGISTRY, logger, parseCodexBaseURL, isValidProviderModel, supportsCustomModels, requiresApiKey, resolveApiKeyForProvider, } from '@dexto/core';
|
|
6
|
-
import { createInitialPreferences, saveGlobalPreferences, loadGlobalPreferences, getGlobalPreferencesPath, updateGlobalPreferences, setActiveModel, isDextoAuthEnabled, loadCustomModels, saveCustomModel, deleteCustomModel, globalPreferencesExist, } from '@dexto/agent-management';
|
|
8
|
+
import { createInitialPreferences, saveGlobalPreferences, loadGlobalPreferences, getGlobalPreferencesPath, updateGlobalPreferences, setActiveModel, isDextoAuthEnabled, loadCustomModels, saveCustomModel, deleteCustomModel, globalPreferencesExist, getDextoGlobalPath, } from '@dexto/agent-management';
|
|
7
9
|
import { interactiveApiKeySetup, hasApiKeyConfigured } from '../utils/api-key-setup.js';
|
|
8
10
|
import { selectProvider, getProviderDisplayName, getProviderEnvVar, providerRequiresBaseURL, getDefaultModel, } from '../utils/provider-setup.js';
|
|
9
11
|
import { setupLocalModels, setupOllamaModels, hasSelectedModel, getModelFromResult, } from '../utils/local-model-setup.js';
|
|
12
|
+
import { executeCommand } from '../utils/self-management.js';
|
|
10
13
|
import { requiresSetup } from '../utils/setup-utils.js';
|
|
11
14
|
import { canUseDextoProvider } from '../utils/dexto-setup.js';
|
|
12
15
|
import { handleAutoLogin } from './auth/login.js';
|
|
@@ -69,6 +72,13 @@ const REASONING_VARIANT_HINTS = {
|
|
|
69
72
|
max: 'Maximum reasoning within provider limits',
|
|
70
73
|
xhigh: 'Extra high reasoning',
|
|
71
74
|
};
|
|
75
|
+
const OPENAI_CODEX_PACKAGE = '@openai/codex';
|
|
76
|
+
const DEXTO_DEPS_PACKAGE_JSON = {
|
|
77
|
+
name: 'dexto-deps',
|
|
78
|
+
version: '1.0.0',
|
|
79
|
+
private: true,
|
|
80
|
+
description: 'Managed dependencies for Dexto',
|
|
81
|
+
};
|
|
72
82
|
function toReasoningVariantLabel(variant, defaultVariant) {
|
|
73
83
|
const normalized = variant.toLowerCase();
|
|
74
84
|
const withKnownCasing = normalized === 'xhigh'
|
|
@@ -420,6 +430,104 @@ function getConfiguredProviderDisplayName(provider, baseURL) {
|
|
|
420
430
|
function isCodexConfigured(provider, baseURL) {
|
|
421
431
|
return provider === 'openai-compatible' && isCodexBaseURL(baseURL);
|
|
422
432
|
}
|
|
433
|
+
async function ensureDextoDepsPackageJson() {
|
|
434
|
+
const depsDir = getDextoGlobalPath('deps');
|
|
435
|
+
await fs.mkdir(depsDir, { recursive: true });
|
|
436
|
+
const packageJsonPath = path.join(depsDir, 'package.json');
|
|
437
|
+
try {
|
|
438
|
+
await fs.access(packageJsonPath);
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
if (error.code !== 'ENOENT') {
|
|
442
|
+
throw error;
|
|
443
|
+
}
|
|
444
|
+
await fs.writeFile(packageJsonPath, JSON.stringify(DEXTO_DEPS_PACKAGE_JSON, null, 2), 'utf-8');
|
|
445
|
+
}
|
|
446
|
+
return depsDir;
|
|
447
|
+
}
|
|
448
|
+
function isMissingCodexCliError(error) {
|
|
449
|
+
if (!(error instanceof Error)) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
const code = error.code;
|
|
453
|
+
return (error.message.includes('Codex CLI not found on PATH') ||
|
|
454
|
+
error.message.includes('spawn codex ENOENT') ||
|
|
455
|
+
(code === 'ENOENT' && error.message.includes('spawn')));
|
|
456
|
+
}
|
|
457
|
+
function getCodexSetupErrorMessage(error) {
|
|
458
|
+
if (isMissingCodexCliError(error)) {
|
|
459
|
+
return 'Codex CLI not found on PATH. Install Codex to use ChatGPT Login in Dexto.';
|
|
460
|
+
}
|
|
461
|
+
return error instanceof Error ? error.message : String(error);
|
|
462
|
+
}
|
|
463
|
+
async function resolveCodexInstaller() {
|
|
464
|
+
const candidates = [
|
|
465
|
+
{
|
|
466
|
+
command: 'npm',
|
|
467
|
+
args: ['install', OPENAI_CODEX_PACKAGE, '--no-audit', '--no-fund'],
|
|
468
|
+
label: 'npm',
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
command: 'pnpm',
|
|
472
|
+
args: ['add', OPENAI_CODEX_PACKAGE],
|
|
473
|
+
label: 'pnpm',
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
command: 'bun',
|
|
477
|
+
args: ['add', OPENAI_CODEX_PACKAGE],
|
|
478
|
+
label: 'bun',
|
|
479
|
+
},
|
|
480
|
+
];
|
|
481
|
+
for (const candidate of candidates) {
|
|
482
|
+
const probe = await executeCommand(candidate.command, ['--version']);
|
|
483
|
+
if (probe.code === 0) {
|
|
484
|
+
return candidate;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
function getCodexInstallerFailureMessage(installer, result) {
|
|
490
|
+
const details = `${result.stderr}\n${result.stdout}`
|
|
491
|
+
.split(/\r?\n/)
|
|
492
|
+
.map((line) => line.trim())
|
|
493
|
+
.filter((line) => line.length > 0);
|
|
494
|
+
const lastLine = details.at(-1);
|
|
495
|
+
return lastLine
|
|
496
|
+
? `Failed to install the OpenAI Codex CLI via ${installer.label}: ${lastLine}`
|
|
497
|
+
: `Failed to install the OpenAI Codex CLI via ${installer.label}.`;
|
|
498
|
+
}
|
|
499
|
+
async function installManagedCodexCli() {
|
|
500
|
+
const depsDir = await ensureDextoDepsPackageJson();
|
|
501
|
+
const installer = await resolveCodexInstaller();
|
|
502
|
+
if (!installer) {
|
|
503
|
+
throw new Error('Could not find npm, pnpm, or bun to install the OpenAI Codex CLI automatically.');
|
|
504
|
+
}
|
|
505
|
+
const result = await executeCommand(installer.command, installer.args, { cwd: depsDir });
|
|
506
|
+
if (result.code !== 0) {
|
|
507
|
+
throw new Error(getCodexInstallerFailureMessage(installer, result));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
async function createCodexClientForSetup() {
|
|
511
|
+
try {
|
|
512
|
+
return await CodexAppServerClient.create();
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
if (!isMissingCodexCliError(error)) {
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
const spinner = p.spinner();
|
|
519
|
+
spinner.start('Installing OpenAI Codex CLI...');
|
|
520
|
+
try {
|
|
521
|
+
await installManagedCodexCli();
|
|
522
|
+
spinner.stop('OpenAI Codex CLI installed');
|
|
523
|
+
}
|
|
524
|
+
catch (installError) {
|
|
525
|
+
spinner.stop('OpenAI Codex CLI installation failed');
|
|
526
|
+
throw installError;
|
|
527
|
+
}
|
|
528
|
+
return await CodexAppServerClient.create();
|
|
529
|
+
}
|
|
530
|
+
}
|
|
423
531
|
async function ensureCodexChatGPTLogin(client) {
|
|
424
532
|
const spinner = p.spinner();
|
|
425
533
|
spinner.start('Starting ChatGPT login with Codex...');
|
|
@@ -510,7 +618,7 @@ async function handleCodexProviderSetup(options = {}) {
|
|
|
510
618
|
console.log(chalk.cyan('\nChatGPT Login Setup\n'));
|
|
511
619
|
let client = null;
|
|
512
620
|
try {
|
|
513
|
-
client = await
|
|
621
|
+
client = await createCodexClientForSetup();
|
|
514
622
|
const account = await ensureCodexChatGPTSession(client);
|
|
515
623
|
if (!account || account.account?.type !== 'chatgpt') {
|
|
516
624
|
return abort('Setup cancelled');
|
|
@@ -579,7 +687,7 @@ async function handleCodexProviderSetup(options = {}) {
|
|
|
579
687
|
return true;
|
|
580
688
|
}
|
|
581
689
|
catch (error) {
|
|
582
|
-
const errorMessage =
|
|
690
|
+
const errorMessage = getCodexSetupErrorMessage(error);
|
|
583
691
|
p.log.error(`ChatGPT Login setup failed: ${errorMessage}`);
|
|
584
692
|
return abort('Setup cancelled', 1);
|
|
585
693
|
}
|
|
@@ -601,7 +709,7 @@ async function handleCodexChatGPTLoginRefresh(options = {}) {
|
|
|
601
709
|
console.log(chalk.cyan('\nChatGPT Login\n'));
|
|
602
710
|
let client = null;
|
|
603
711
|
try {
|
|
604
|
-
client = await
|
|
712
|
+
client = await createCodexClientForSetup();
|
|
605
713
|
const account = await ensureCodexChatGPTSession(client);
|
|
606
714
|
if (!account || account.account?.type !== 'chatgpt') {
|
|
607
715
|
return abort('ChatGPT login cancelled');
|
|
@@ -610,7 +718,7 @@ async function handleCodexChatGPTLoginRefresh(options = {}) {
|
|
|
610
718
|
return true;
|
|
611
719
|
}
|
|
612
720
|
catch (error) {
|
|
613
|
-
const errorMessage =
|
|
721
|
+
const errorMessage = getCodexSetupErrorMessage(error);
|
|
614
722
|
p.log.error(`ChatGPT Login failed: ${errorMessage}`);
|
|
615
723
|
return abort('ChatGPT login cancelled', 1);
|
|
616
724
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-importer.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/image-importer.ts"],"names":[],"mappings":"AAKA,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAOnE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { setImageImporter } from '@dexto/agent-config';
|
|
2
|
+
import { importImageModule } from './image-store.js';
|
|
3
|
+
let imageImporterConfigured = false;
|
|
4
|
+
export async function ensureImageImporterConfigured() {
|
|
5
|
+
if (imageImporterConfigured) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
setImageImporter((specifier) => importImageModule(specifier));
|
|
9
|
+
imageImporterConfigured = true;
|
|
10
|
+
}
|
|
@@ -8,6 +8,7 @@ import * as p from '@clack/prompts';
|
|
|
8
8
|
type SelectOptions = Parameters<typeof p.select>[0];
|
|
9
9
|
type TextOptions = Parameters<typeof p.text>[0];
|
|
10
10
|
type ConfirmOptions = Parameters<typeof p.confirm>[0];
|
|
11
|
+
type MultiselectOptions = Parameters<typeof p.multiselect>[0];
|
|
11
12
|
/**
|
|
12
13
|
* Select prompt that exits on cancel.
|
|
13
14
|
* Use for linear flows where cancel should abort the entire operation.
|
|
@@ -43,5 +44,10 @@ export declare function textOrExit(options: TextOptions, cancelMessage?: string)
|
|
|
43
44
|
* }, 'Operation cancelled');
|
|
44
45
|
*/
|
|
45
46
|
export declare function confirmOrExit(options: ConfirmOptions, cancelMessage?: string): Promise<boolean>;
|
|
47
|
+
/**
|
|
48
|
+
* Multiselect prompt that exits on cancel.
|
|
49
|
+
* Use for linear flows where cancel should abort the entire operation.
|
|
50
|
+
*/
|
|
51
|
+
export declare function multiselectOrExit<T extends string>(options: MultiselectOptions, cancelMessage?: string): Promise<T[]>;
|
|
46
52
|
export {};
|
|
47
53
|
//# sourceMappingURL=prompt-helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-helpers.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/prompt-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AAMpC,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAChD,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"prompt-helpers.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/prompt-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AAMpC,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAChD,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAM9D;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC/C,OAAO,EAAE,aAAa,EACtB,aAAa,SAAc,GAC5B,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC5B,OAAO,EAAE,WAAW,EACpB,aAAa,SAAc,GAC5B,OAAO,CAAC,MAAM,CAAC,CAOjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAC/B,OAAO,EAAE,cAAc,EACvB,aAAa,SAAc,GAC5B,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EACpD,OAAO,EAAE,kBAAkB,EAC3B,aAAa,SAAc,GAC5B,OAAO,CAAC,CAAC,EAAE,CAAC,CAOd"}
|
|
@@ -64,3 +64,15 @@ export async function confirmOrExit(options, cancelMessage = 'Cancelled') {
|
|
|
64
64
|
}
|
|
65
65
|
return result;
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Multiselect prompt that exits on cancel.
|
|
69
|
+
* Use for linear flows where cancel should abort the entire operation.
|
|
70
|
+
*/
|
|
71
|
+
export async function multiselectOrExit(options, cancelMessage = 'Cancelled') {
|
|
72
|
+
const result = await p.multiselect(options);
|
|
73
|
+
if (p.isCancel(result)) {
|
|
74
|
+
p.cancel(cancelMessage);
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
package/dist/index-main.js
CHANGED
|
@@ -44,7 +44,7 @@ const cliVersion = resolveCliVersion();
|
|
|
44
44
|
// Set CLI version for Dexto Gateway usage tracking
|
|
45
45
|
process.env.DEXTO_CLI_VERSION = cliVersion;
|
|
46
46
|
import { logger, getProviderFromModel, getAllSupportedModels, startLlmRegistryAutoUpdate, DextoAgent, isPath, resolveApiKeyForProvider, getPrimaryApiKeyEnvVar, } from '@dexto/core';
|
|
47
|
-
import { applyImageDefaults, cleanNullValues, AgentConfigSchema, loadImage, resolveServicesFromConfig,
|
|
47
|
+
import { applyImageDefaults, cleanNullValues, AgentConfigSchema, loadImage, resolveServicesFromConfig, toDextoAgentOptions, } from '@dexto/agent-config';
|
|
48
48
|
import { getDextoPackageRoot, resolveAgentPath, loadAgentConfig, findDextoProjectRoot, globalPreferencesExist, loadGlobalPreferences, resolveBundledScript, enrichAgentConfig, isDextoAuthEnabled, } from '@dexto/agent-management';
|
|
49
49
|
import { validateCliOptions, handleCliOptionsError } from './cli/utils/options.js';
|
|
50
50
|
import { validateAgentConfig } from './cli/utils/config-validation.js';
|
|
@@ -60,19 +60,11 @@ import { registerPluginCommand } from './cli/commands/plugin/register.js';
|
|
|
60
60
|
import { registerAgentsCommand } from './cli/commands/agents/register.js';
|
|
61
61
|
import { registerDeployCommand } from './cli/commands/deploy/register.js';
|
|
62
62
|
import { registerInitCommand } from './cli/commands/init.js';
|
|
63
|
+
import { ensureImageImporterConfigured } from './cli/utils/image-importer.js';
|
|
63
64
|
const program = new Command();
|
|
64
|
-
let imageImporterConfigured = false;
|
|
65
65
|
let dextoApiKeyBootstrapped = false;
|
|
66
66
|
let versionCheckPromise = null;
|
|
67
67
|
let llmRegistryAutoUpdateStarted = false;
|
|
68
|
-
async function ensureImageImporterConfigured() {
|
|
69
|
-
if (imageImporterConfigured) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const { importImageModule } = await import('./cli/utils/image-store.js');
|
|
73
|
-
setImageImporter((specifier) => importImageModule(specifier));
|
|
74
|
-
imageImporterConfigured = true;
|
|
75
|
-
}
|
|
76
68
|
async function ensureDextoApiKeyBootstrap() {
|
|
77
69
|
if (dextoApiKeyBootstrapped) {
|
|
78
70
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dexto",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dexto": "./dist/index.js"
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"ws": "^8.18.1",
|
|
35
35
|
"yaml": "^2.7.1",
|
|
36
36
|
"zod": "^3.25.0",
|
|
37
|
-
"@dexto/agent-config": "1.6.
|
|
38
|
-
"@dexto/agent-management": "1.6.
|
|
39
|
-
"@dexto/analytics": "1.6.
|
|
40
|
-
"@dexto/core": "1.6.
|
|
41
|
-
"@dexto/image-local": "1.6.
|
|
42
|
-
"@dexto/image-logger-agent": "1.6.
|
|
43
|
-
"@dexto/registry": "1.6.
|
|
44
|
-
"@dexto/server": "1.6.
|
|
45
|
-
"@dexto/storage": "1.6.
|
|
46
|
-
"@dexto/tui": "1.6.
|
|
37
|
+
"@dexto/agent-config": "1.6.16",
|
|
38
|
+
"@dexto/agent-management": "1.6.16",
|
|
39
|
+
"@dexto/analytics": "1.6.16",
|
|
40
|
+
"@dexto/core": "1.6.16",
|
|
41
|
+
"@dexto/image-local": "1.6.16",
|
|
42
|
+
"@dexto/image-logger-agent": "1.6.16",
|
|
43
|
+
"@dexto/registry": "1.6.16",
|
|
44
|
+
"@dexto/server": "1.6.16",
|
|
45
|
+
"@dexto/storage": "1.6.16",
|
|
46
|
+
"@dexto/tui": "1.6.16"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/ws": "^8.5.11",
|