@defai.digital/ax-cli 3.15.18 ā 3.15.20
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 +36 -2
- package/config-defaults/prompts.yaml +32 -0
- package/dist/agent/execution/tool-executor.d.ts +0 -12
- package/dist/agent/execution/tool-executor.js +18 -47
- package/dist/agent/execution/tool-executor.js.map +1 -1
- package/dist/commands/setup.js +215 -210
- package/dist/commands/setup.js.map +1 -1
- package/dist/llm/tools.js +36 -48
- package/dist/llm/tools.js.map +1 -1
- package/dist/tools/ax-agent.d.ts +39 -0
- package/dist/tools/ax-agent.js +173 -0
- package/dist/tools/ax-agent.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/setup.js
CHANGED
|
@@ -2,13 +2,13 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { existsSync, mkdirSync } from 'fs';
|
|
3
3
|
import { dirname } from 'path';
|
|
4
4
|
import { execSync, spawnSync } from 'child_process';
|
|
5
|
-
import
|
|
5
|
+
import * as prompts from '@clack/prompts';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import { validateProviderSetup } from '../utils/setup-validator.js';
|
|
8
8
|
import { getSettingsManager } from '../utils/settings-manager.js';
|
|
9
9
|
import { extractErrorMessage } from '../utils/error-handler.js';
|
|
10
10
|
import { CONFIG_PATHS } from '../constants.js';
|
|
11
|
-
import { detectZAIServices, getRecommendedServers, generateZAIServerConfig,
|
|
11
|
+
import { detectZAIServices, getRecommendedServers, generateZAIServerConfig, } from '../mcp/index.js';
|
|
12
12
|
import { addMCPServer, removeMCPServer } from '../mcp/config.js';
|
|
13
13
|
/**
|
|
14
14
|
* Check if AutomatosX (ax) is installed
|
|
@@ -77,17 +77,6 @@ async function installAutomatosX() {
|
|
|
77
77
|
return false;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
/** Prompt for a new API key with validation */
|
|
81
|
-
async function promptForApiKey(providerDisplayName, website) {
|
|
82
|
-
console.log(chalk.dim(`\nGet your API key from: ${website}\n`));
|
|
83
|
-
const response = await enquirer.prompt({
|
|
84
|
-
type: 'password',
|
|
85
|
-
name: 'apiKey',
|
|
86
|
-
message: `Enter your ${providerDisplayName} API key:`,
|
|
87
|
-
validate: (value) => value?.trim().length > 0 || 'API key is required',
|
|
88
|
-
});
|
|
89
|
-
return response.apiKey.trim();
|
|
90
|
-
}
|
|
91
80
|
const PROVIDERS = {
|
|
92
81
|
'z.ai': {
|
|
93
82
|
name: 'z.ai',
|
|
@@ -151,15 +140,14 @@ function getProviderFromBaseURL(baseURL) {
|
|
|
151
140
|
*/
|
|
152
141
|
export function createSetupCommand() {
|
|
153
142
|
const setupCommand = new Command('setup');
|
|
154
|
-
const STEP_TOTAL = 5;
|
|
155
|
-
const step = (index, title) => chalk.cyan(`\nStep ${index}/${STEP_TOTAL} ā ${title}\n`);
|
|
156
143
|
setupCommand
|
|
157
144
|
.description('Initialize AX CLI configuration with AI provider selection')
|
|
158
145
|
.option('--force', 'Overwrite existing configuration')
|
|
159
146
|
.option('--no-validate', 'Skip validation of API endpoint and credentials')
|
|
160
147
|
.action(async (options) => {
|
|
161
148
|
try {
|
|
162
|
-
|
|
149
|
+
// Show intro
|
|
150
|
+
prompts.intro(chalk.cyan('AX CLI Setup'));
|
|
163
151
|
// Always use the NEW path ~/.ax-cli/config.json
|
|
164
152
|
const configPath = CONFIG_PATHS.USER_CONFIG;
|
|
165
153
|
const configDir = dirname(configPath);
|
|
@@ -167,95 +155,108 @@ export function createSetupCommand() {
|
|
|
167
155
|
// Ensure config directory exists
|
|
168
156
|
if (!existsSync(configDir)) {
|
|
169
157
|
mkdirSync(configDir, { recursive: true });
|
|
170
|
-
console.log(chalk.green(`ā Created config directory: ${configDir}`));
|
|
171
158
|
}
|
|
172
159
|
// Load existing config to check for existing API key BEFORE provider selection
|
|
173
|
-
// Use settings manager to load decrypted config (REQ-SEC-003)
|
|
174
160
|
let existingConfig = null;
|
|
175
161
|
let existingProviderKey = null;
|
|
176
162
|
if (existsSync(configPath)) {
|
|
177
163
|
try {
|
|
178
|
-
// Load settings through settings manager (handles decryption)
|
|
179
164
|
existingConfig = settingsManager.loadUserSettings();
|
|
180
|
-
// Determine which provider the existing config is using
|
|
181
165
|
if (existingConfig.baseURL) {
|
|
182
166
|
existingProviderKey = getProviderFromBaseURL(existingConfig.baseURL);
|
|
183
167
|
}
|
|
184
|
-
// Show existing configuration
|
|
185
168
|
if (existingProviderKey && !options.force) {
|
|
186
169
|
const existingProvider = PROVIDERS[existingProviderKey];
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
console.log(chalk.dim(` Location: ${configPath}\n`));
|
|
170
|
+
await prompts.note(`Provider: ${existingProvider?.displayName || 'Unknown'}\n` +
|
|
171
|
+
`Location: ${configPath}`, 'Existing Configuration Found');
|
|
190
172
|
}
|
|
191
173
|
}
|
|
192
174
|
catch (error) {
|
|
193
|
-
|
|
194
|
-
// Continue with setup even if loading existing config fails
|
|
175
|
+
prompts.log.warn(`Failed to load existing config: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
195
176
|
}
|
|
196
177
|
}
|
|
197
|
-
//
|
|
198
|
-
|
|
178
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
179
|
+
// STEP 1: Provider Selection
|
|
180
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
181
|
+
prompts.log.step(chalk.bold('Step 1/5 ā Choose Provider'));
|
|
199
182
|
const providerChoices = Object.entries(PROVIDERS).map(([key, provider]) => ({
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
hint: provider.
|
|
183
|
+
value: key,
|
|
184
|
+
label: provider.displayName,
|
|
185
|
+
hint: provider.description,
|
|
203
186
|
}));
|
|
204
|
-
const
|
|
205
|
-
type: 'select',
|
|
206
|
-
name: 'provider',
|
|
187
|
+
const providerKey = await prompts.select({
|
|
207
188
|
message: 'Select your AI provider:',
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
? providerChoices.findIndex(choice => choice.name === existingProviderKey)
|
|
211
|
-
: 0,
|
|
189
|
+
options: providerChoices,
|
|
190
|
+
initialValue: existingProviderKey || 'z.ai',
|
|
212
191
|
});
|
|
213
|
-
|
|
214
|
-
|
|
192
|
+
if (prompts.isCancel(providerKey)) {
|
|
193
|
+
prompts.cancel('Setup cancelled.');
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
const selectedProvider = PROVIDERS[providerKey];
|
|
197
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
198
|
+
// STEP 2: API Key
|
|
199
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
200
|
+
prompts.log.step(chalk.bold('Step 2/5 ā API Key'));
|
|
215
201
|
let apiKey = '';
|
|
216
202
|
if (selectedProvider.requiresApiKey) {
|
|
217
|
-
|
|
218
|
-
const isSameProvider = existingProviderKey === providerResponse.provider;
|
|
219
|
-
// existingConfig.apiKey is already decrypted string (loaded via loadUserSettings)
|
|
203
|
+
const isSameProvider = existingProviderKey === providerKey;
|
|
220
204
|
const hasExistingKey = existingConfig?.apiKey && typeof existingConfig.apiKey === 'string' && existingConfig.apiKey.trim().length > 0;
|
|
221
|
-
// Same provider with existing API key - ask if they want to reuse it
|
|
222
205
|
if (isSameProvider && hasExistingKey && existingConfig?.apiKey) {
|
|
223
|
-
console.log(chalk.green(`\nā Found existing API key for ${selectedProvider.displayName}`));
|
|
224
|
-
// Display masked API key (handle short keys gracefully)
|
|
225
|
-
// apiKey is guaranteed to be a string here (type guard in condition)
|
|
226
206
|
const key = existingConfig.apiKey;
|
|
227
207
|
const maskedKey = key.length > 12
|
|
228
208
|
? `${key.substring(0, 8)}...${key.substring(key.length - 4)}`
|
|
229
209
|
: `${key.substring(0, Math.min(4, key.length))}...`;
|
|
230
|
-
|
|
231
|
-
const
|
|
232
|
-
type: 'confirm',
|
|
233
|
-
name: 'reuseKey',
|
|
210
|
+
await prompts.note(`Key: ${maskedKey}`, `Existing API Key for ${selectedProvider.displayName}`);
|
|
211
|
+
const reuseKey = await prompts.confirm({
|
|
234
212
|
message: 'Use existing API key?',
|
|
235
|
-
|
|
213
|
+
initialValue: true,
|
|
236
214
|
});
|
|
237
|
-
if (
|
|
215
|
+
if (prompts.isCancel(reuseKey)) {
|
|
216
|
+
prompts.cancel('Setup cancelled.');
|
|
217
|
+
process.exit(0);
|
|
218
|
+
}
|
|
219
|
+
if (reuseKey) {
|
|
238
220
|
apiKey = existingConfig.apiKey;
|
|
239
|
-
|
|
221
|
+
prompts.log.success('Using existing API key');
|
|
240
222
|
}
|
|
241
223
|
else {
|
|
242
|
-
|
|
224
|
+
prompts.log.info(`Get your API key from: ${selectedProvider.website}`);
|
|
225
|
+
const newKey = await prompts.password({
|
|
226
|
+
message: `Enter new ${selectedProvider.displayName} API key:`,
|
|
227
|
+
validate: (value) => value?.trim().length > 0 ? undefined : 'API key is required',
|
|
228
|
+
});
|
|
229
|
+
if (prompts.isCancel(newKey)) {
|
|
230
|
+
prompts.cancel('Setup cancelled.');
|
|
231
|
+
process.exit(0);
|
|
232
|
+
}
|
|
233
|
+
apiKey = newKey.trim();
|
|
243
234
|
}
|
|
244
235
|
}
|
|
245
236
|
else {
|
|
246
|
-
// Different provider or no existing key - just ask for new key
|
|
247
237
|
if (hasExistingKey && !isSameProvider && existingProviderKey) {
|
|
248
238
|
const previousProvider = PROVIDERS[existingProviderKey];
|
|
249
|
-
|
|
239
|
+
prompts.log.warn(`Switching from ${previousProvider?.displayName || 'previous provider'} to ${selectedProvider.displayName}`);
|
|
240
|
+
}
|
|
241
|
+
prompts.log.info(`Get your API key from: ${selectedProvider.website}`);
|
|
242
|
+
const newKey = await prompts.password({
|
|
243
|
+
message: `Enter your ${selectedProvider.displayName} API key:`,
|
|
244
|
+
validate: (value) => value?.trim().length > 0 ? undefined : 'API key is required',
|
|
245
|
+
});
|
|
246
|
+
if (prompts.isCancel(newKey)) {
|
|
247
|
+
prompts.cancel('Setup cancelled.');
|
|
248
|
+
process.exit(0);
|
|
250
249
|
}
|
|
251
|
-
apiKey =
|
|
250
|
+
apiKey = newKey.trim();
|
|
252
251
|
}
|
|
253
252
|
}
|
|
254
253
|
else {
|
|
255
|
-
|
|
254
|
+
prompts.log.success(`${selectedProvider.displayName} doesn't require an API key`);
|
|
256
255
|
}
|
|
257
|
-
//
|
|
258
|
-
|
|
256
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
257
|
+
// STEP 3: Model Selection
|
|
258
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
259
|
+
prompts.log.step(chalk.bold('Step 3/5 ā Choose Model'));
|
|
259
260
|
const existingModel = existingConfig?.defaultModel || existingConfig?.currentModel;
|
|
260
261
|
const baseModelOptions = Array.from(new Set([
|
|
261
262
|
selectedProvider.defaultModel,
|
|
@@ -263,71 +264,97 @@ export function createSetupCommand() {
|
|
|
263
264
|
existingModel,
|
|
264
265
|
].filter(Boolean)));
|
|
265
266
|
const modelChoices = baseModelOptions.map(model => ({
|
|
266
|
-
|
|
267
|
-
|
|
267
|
+
value: model,
|
|
268
|
+
label: model === selectedProvider.defaultModel ? `${model} (default)` : model,
|
|
268
269
|
}));
|
|
269
|
-
modelChoices.push({
|
|
270
|
-
const
|
|
271
|
-
type: 'select',
|
|
272
|
-
name: 'model',
|
|
270
|
+
modelChoices.push({ value: '__custom__', label: 'Other (enter manually)' });
|
|
271
|
+
const modelSelection = await prompts.select({
|
|
273
272
|
message: 'Select default model:',
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
options: modelChoices,
|
|
274
|
+
initialValue: existingModel || selectedProvider.defaultModel,
|
|
276
275
|
});
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
276
|
+
if (prompts.isCancel(modelSelection)) {
|
|
277
|
+
prompts.cancel('Setup cancelled.');
|
|
278
|
+
process.exit(0);
|
|
279
|
+
}
|
|
280
|
+
let chosenModel = modelSelection;
|
|
281
|
+
if (modelSelection === '__custom__') {
|
|
282
|
+
const manualModel = await prompts.text({
|
|
282
283
|
message: 'Enter model ID:',
|
|
283
|
-
|
|
284
|
-
validate: (value) => value
|
|
284
|
+
initialValue: selectedProvider.defaultModel,
|
|
285
|
+
validate: (value) => value?.trim().length > 0 ? undefined : 'Model is required',
|
|
285
286
|
});
|
|
286
|
-
|
|
287
|
+
if (prompts.isCancel(manualModel)) {
|
|
288
|
+
prompts.cancel('Setup cancelled.');
|
|
289
|
+
process.exit(0);
|
|
290
|
+
}
|
|
291
|
+
chosenModel = manualModel.trim();
|
|
287
292
|
}
|
|
288
|
-
//
|
|
289
|
-
|
|
293
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
294
|
+
// STEP 4: Validate Connection
|
|
295
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
296
|
+
prompts.log.step(chalk.bold('Step 4/5 ā Validate Connection'));
|
|
297
|
+
const spinner = prompts.spinner();
|
|
298
|
+
spinner.start('Validating connection...');
|
|
290
299
|
const validationResult = await validateProviderSetup({
|
|
291
300
|
baseURL: selectedProvider.baseURL,
|
|
292
301
|
apiKey: apiKey,
|
|
293
302
|
model: chosenModel,
|
|
294
303
|
providerName: selectedProvider.name,
|
|
295
|
-
}, !options.validate
|
|
296
|
-
);
|
|
304
|
+
}, !options.validate);
|
|
305
|
+
spinner.stop('Validation complete');
|
|
297
306
|
// If validator returned models list, offer quick re-pick
|
|
298
307
|
if (validationResult.availableModels && validationResult.availableModels.length > 0) {
|
|
299
308
|
const uniqueAvailable = Array.from(new Set(validationResult.availableModels));
|
|
300
309
|
const availableChoices = uniqueAvailable.map(model => ({
|
|
301
|
-
|
|
302
|
-
|
|
310
|
+
value: model,
|
|
311
|
+
label: model,
|
|
303
312
|
}));
|
|
304
|
-
availableChoices.push({
|
|
305
|
-
const
|
|
306
|
-
type: 'select',
|
|
307
|
-
name: 'altModel',
|
|
313
|
+
availableChoices.push({ value: chosenModel, label: `${chosenModel} (keep current)` });
|
|
314
|
+
const altModel = await prompts.select({
|
|
308
315
|
message: 'Select a validated model (or keep current):',
|
|
309
|
-
|
|
310
|
-
|
|
316
|
+
options: availableChoices,
|
|
317
|
+
initialValue: chosenModel,
|
|
311
318
|
});
|
|
312
|
-
|
|
319
|
+
if (prompts.isCancel(altModel)) {
|
|
320
|
+
prompts.cancel('Setup cancelled.');
|
|
321
|
+
process.exit(0);
|
|
322
|
+
}
|
|
323
|
+
chosenModel = altModel;
|
|
324
|
+
}
|
|
325
|
+
if (validationResult.success) {
|
|
326
|
+
prompts.log.success('Connection validated successfully');
|
|
313
327
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const proceedAnyway = await enquirer.prompt({
|
|
318
|
-
type: 'confirm',
|
|
319
|
-
name: 'proceed',
|
|
328
|
+
else if (options.validate !== false) {
|
|
329
|
+
prompts.log.warn('Validation failed, but you can still save the configuration.');
|
|
330
|
+
const proceedAnyway = await prompts.confirm({
|
|
320
331
|
message: 'Save configuration anyway?',
|
|
321
|
-
|
|
332
|
+
initialValue: false,
|
|
322
333
|
});
|
|
323
|
-
if (!proceedAnyway
|
|
324
|
-
|
|
325
|
-
|
|
334
|
+
if (prompts.isCancel(proceedAnyway) || !proceedAnyway) {
|
|
335
|
+
prompts.cancel('Setup cancelled. Please check your settings and try again.');
|
|
336
|
+
process.exit(0);
|
|
326
337
|
}
|
|
327
338
|
}
|
|
328
|
-
//
|
|
329
|
-
//
|
|
339
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
340
|
+
// STEP 5: Review & Save
|
|
341
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
342
|
+
prompts.log.step(chalk.bold('Step 5/5 ā Review & Save'));
|
|
330
343
|
const maxTokens = (selectedProvider.name === 'z.ai' || selectedProvider.name === 'z.ai-free') ? 32768 : 8192;
|
|
344
|
+
await prompts.note(`Provider: ${selectedProvider.displayName}\n` +
|
|
345
|
+
`Base URL: ${selectedProvider.baseURL}\n` +
|
|
346
|
+
`Model: ${chosenModel}\n` +
|
|
347
|
+
`Max Tokens: ${existingConfig?.maxTokens ?? maxTokens}\n` +
|
|
348
|
+
`Config path: ${configPath}`, 'Configuration Summary');
|
|
349
|
+
const confirmSave = await prompts.confirm({
|
|
350
|
+
message: 'Save these settings?',
|
|
351
|
+
initialValue: true,
|
|
352
|
+
});
|
|
353
|
+
if (prompts.isCancel(confirmSave) || !confirmSave) {
|
|
354
|
+
prompts.cancel('Setup cancelled. No changes saved.');
|
|
355
|
+
process.exit(0);
|
|
356
|
+
}
|
|
357
|
+
// Create configuration object
|
|
331
358
|
const mergedConfig = {
|
|
332
359
|
...(existingConfig || {}),
|
|
333
360
|
apiKey: apiKey,
|
|
@@ -340,38 +367,23 @@ export function createSetupCommand() {
|
|
|
340
367
|
_provider: selectedProvider.displayName,
|
|
341
368
|
_website: selectedProvider.website,
|
|
342
369
|
};
|
|
343
|
-
console.log(step(5, 'Review & save'));
|
|
344
|
-
console.log(chalk.white('Provider: ') + chalk.green(selectedProvider.displayName));
|
|
345
|
-
console.log(chalk.white('Base URL: ') + chalk.green(selectedProvider.baseURL));
|
|
346
|
-
console.log(chalk.white('Model: ') + chalk.green(chosenModel));
|
|
347
|
-
console.log(chalk.white('Max Tokens: ') + chalk.green((mergedConfig.maxTokens || maxTokens).toString()));
|
|
348
|
-
console.log(chalk.white('Config path: ') + chalk.green(configPath));
|
|
349
|
-
const confirmSave = await enquirer.prompt({
|
|
350
|
-
type: 'confirm',
|
|
351
|
-
name: 'save',
|
|
352
|
-
message: 'Save these settings?',
|
|
353
|
-
initial: true,
|
|
354
|
-
});
|
|
355
|
-
if (!confirmSave.save) {
|
|
356
|
-
console.log(chalk.yellow('\nā ļø Setup cancelled. No changes saved.\n'));
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
370
|
// Persist using settings manager to ensure encryption + permissions
|
|
360
371
|
settingsManager.saveUserSettings(mergedConfig);
|
|
361
|
-
|
|
362
|
-
//
|
|
372
|
+
prompts.log.success('Configuration saved successfully!');
|
|
373
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
374
|
+
// Z.AI MCP Integration
|
|
375
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
363
376
|
if (selectedProvider.name === 'z.ai' || selectedProvider.name === 'z.ai-free') {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
377
|
+
await prompts.note('Enabling Z.AI MCP servers for enhanced capabilities:\n' +
|
|
378
|
+
'⢠Web Search - Real-time web search\n' +
|
|
379
|
+
'⢠Web Reader - Extract content from web pages\n' +
|
|
380
|
+
'⢠Vision - Image/video analysis (Node.js 22+)', 'Z.AI MCP Integration');
|
|
381
|
+
const mcpSpinner = prompts.spinner();
|
|
382
|
+
mcpSpinner.start('Configuring Z.AI MCP servers...');
|
|
369
383
|
try {
|
|
370
384
|
const status = await detectZAIServices();
|
|
371
385
|
const serversToAdd = getRecommendedServers(status);
|
|
372
|
-
// Remove existing Z.AI MCP servers first
|
|
373
|
-
// This ensures headers and other config options are always up-to-date
|
|
374
|
-
console.log(chalk.blue('Refreshing Z.AI MCP servers...'));
|
|
386
|
+
// Remove existing Z.AI MCP servers first
|
|
375
387
|
for (const serverName of serversToAdd) {
|
|
376
388
|
try {
|
|
377
389
|
removeMCPServer(serverName);
|
|
@@ -382,142 +394,135 @@ export function createSetupCommand() {
|
|
|
382
394
|
}
|
|
383
395
|
let successCount = 0;
|
|
384
396
|
for (const serverName of serversToAdd) {
|
|
385
|
-
const template = ZAI_MCP_TEMPLATES[serverName];
|
|
386
397
|
try {
|
|
387
398
|
const config = generateZAIServerConfig(serverName, apiKey);
|
|
388
|
-
// Only save config during setup - don't connect (server will be connected when ax-cli runs)
|
|
389
399
|
addMCPServer(config);
|
|
390
|
-
console.log(chalk.green(`ā ${template.displayName}`));
|
|
391
400
|
successCount++;
|
|
392
401
|
}
|
|
393
402
|
catch {
|
|
394
|
-
|
|
403
|
+
// Skip failed servers
|
|
395
404
|
}
|
|
396
405
|
}
|
|
397
|
-
|
|
398
|
-
console.log(chalk.green(`\n⨠${successCount} Z.AI MCP server${successCount !== 1 ? 's' : ''} configured!\n`));
|
|
399
|
-
}
|
|
406
|
+
mcpSpinner.stop(`${successCount} Z.AI MCP server${successCount !== 1 ? 's' : ''} configured`);
|
|
400
407
|
}
|
|
401
408
|
catch (error) {
|
|
402
|
-
|
|
403
|
-
|
|
409
|
+
mcpSpinner.stop('Could not set up Z.AI MCP servers');
|
|
410
|
+
prompts.log.warn(`${extractErrorMessage(error)}`);
|
|
411
|
+
prompts.log.info('You can enable them later with: ax-cli mcp add-zai');
|
|
404
412
|
}
|
|
405
413
|
}
|
|
414
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
406
415
|
// AutomatosX Integration
|
|
407
|
-
|
|
408
|
-
|
|
416
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
417
|
+
await prompts.note('Multi-agent AI orchestration with persistent memory and collaboration.', 'AutomatosX Agent Orchestration');
|
|
409
418
|
const axInstalled = isAutomatosXInstalled();
|
|
410
419
|
if (axInstalled) {
|
|
411
420
|
const currentVersion = getAutomatosXVersion();
|
|
412
|
-
|
|
413
|
-
|
|
421
|
+
prompts.log.success(`AutomatosX detected${currentVersion ? ` (v${currentVersion})` : ''}`);
|
|
422
|
+
const axSpinner = prompts.spinner();
|
|
423
|
+
axSpinner.start('Checking for updates...');
|
|
414
424
|
const updated = await updateAutomatosX();
|
|
415
425
|
if (updated) {
|
|
416
426
|
const newVersion = getAutomatosXVersion();
|
|
417
|
-
|
|
427
|
+
axSpinner.stop(`AutomatosX updated${newVersion ? ` to v${newVersion}` : ''}`);
|
|
418
428
|
}
|
|
419
429
|
else {
|
|
420
|
-
|
|
430
|
+
axSpinner.stop('Could not update AutomatosX');
|
|
431
|
+
prompts.log.info('Run manually: ax update -y');
|
|
421
432
|
}
|
|
422
433
|
}
|
|
423
434
|
else {
|
|
424
|
-
// Wrap in try-catch to handle non-interactive environments (e.g., CI/tests)
|
|
425
435
|
try {
|
|
426
|
-
const installResponse = await
|
|
427
|
-
type: 'confirm',
|
|
428
|
-
name: 'install',
|
|
436
|
+
const installResponse = await prompts.confirm({
|
|
429
437
|
message: 'Install AutomatosX for multi-agent AI orchestration?',
|
|
430
|
-
|
|
438
|
+
initialValue: true,
|
|
431
439
|
});
|
|
432
|
-
if (installResponse
|
|
433
|
-
|
|
440
|
+
if (!prompts.isCancel(installResponse) && installResponse) {
|
|
441
|
+
const installSpinner = prompts.spinner();
|
|
442
|
+
installSpinner.start('Installing AutomatosX...');
|
|
434
443
|
const installed = await installAutomatosX();
|
|
435
444
|
if (installed) {
|
|
436
|
-
|
|
437
|
-
|
|
445
|
+
installSpinner.stop('AutomatosX installed successfully!');
|
|
446
|
+
prompts.log.info('Run `ax list agents` to see available AI agents.');
|
|
438
447
|
}
|
|
439
448
|
else {
|
|
440
|
-
|
|
441
|
-
|
|
449
|
+
installSpinner.stop('Could not install AutomatosX');
|
|
450
|
+
prompts.log.info('Install manually: npm install -g @defai.digital/automatosx');
|
|
442
451
|
}
|
|
443
452
|
}
|
|
444
|
-
else {
|
|
445
|
-
|
|
453
|
+
else if (!prompts.isCancel(installResponse)) {
|
|
454
|
+
prompts.log.info('You can install AutomatosX later: npm install -g @defai.digital/automatosx');
|
|
446
455
|
}
|
|
447
456
|
}
|
|
448
457
|
catch {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
console.log(chalk.dim(' Install manually: npm install -g @defai.digital/automatosx\n'));
|
|
458
|
+
prompts.log.info('Skipping AutomatosX setup (non-interactive mode).');
|
|
459
|
+
prompts.log.info('Install manually: npm install -g @defai.digital/automatosx');
|
|
452
460
|
}
|
|
453
461
|
}
|
|
454
462
|
// Agent-First Mode Configuration (only ask if AutomatosX is available)
|
|
455
463
|
const axAvailable = isAutomatosXInstalled();
|
|
456
464
|
if (axAvailable) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
console.log(chalk.dim(' When disabled (default), you use the direct LLM and can invoke agents explicitly.\n'));
|
|
465
|
+
await prompts.note('When enabled, ax-cli automatically routes tasks to specialized agents\n' +
|
|
466
|
+
'based on keywords (e.g., "test" ā testing agent, "refactor" ā refactoring agent).\n' +
|
|
467
|
+
'When disabled (default), you use the direct LLM and can invoke agents explicitly.', 'Agent-First Mode');
|
|
461
468
|
try {
|
|
462
|
-
const
|
|
463
|
-
type: 'confirm',
|
|
464
|
-
name: 'enableAgentFirst',
|
|
469
|
+
const enableAgentFirst = await prompts.confirm({
|
|
465
470
|
message: 'Enable agent-first mode (auto-route to specialized agents)?',
|
|
466
|
-
|
|
467
|
-
});
|
|
468
|
-
// Save agent-first setting
|
|
469
|
-
const currentSettings = settingsManager.loadUserSettings();
|
|
470
|
-
settingsManager.saveUserSettings({
|
|
471
|
-
...currentSettings,
|
|
472
|
-
agentFirst: {
|
|
473
|
-
enabled: agentFirstResponse.enableAgentFirst,
|
|
474
|
-
confidenceThreshold: 0.6,
|
|
475
|
-
showAgentIndicator: true,
|
|
476
|
-
defaultAgent: 'standard',
|
|
477
|
-
excludedAgents: [],
|
|
478
|
-
},
|
|
471
|
+
initialValue: false,
|
|
479
472
|
});
|
|
480
|
-
if (
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
473
|
+
if (!prompts.isCancel(enableAgentFirst)) {
|
|
474
|
+
const currentSettings = settingsManager.loadUserSettings();
|
|
475
|
+
settingsManager.saveUserSettings({
|
|
476
|
+
...currentSettings,
|
|
477
|
+
agentFirst: {
|
|
478
|
+
enabled: enableAgentFirst,
|
|
479
|
+
confidenceThreshold: 0.6,
|
|
480
|
+
showAgentIndicator: true,
|
|
481
|
+
defaultAgent: 'standard',
|
|
482
|
+
excludedAgents: [],
|
|
483
|
+
},
|
|
484
|
+
});
|
|
485
|
+
if (enableAgentFirst) {
|
|
486
|
+
prompts.log.success('Agent-first mode enabled');
|
|
487
|
+
prompts.log.info('Tasks will be automatically routed to specialized agents.');
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
prompts.log.success('Agent-first mode disabled (default)');
|
|
491
|
+
prompts.log.info('Use direct LLM. Invoke agents with --agent flag when needed.');
|
|
492
|
+
}
|
|
487
493
|
}
|
|
488
494
|
}
|
|
489
495
|
catch {
|
|
490
|
-
|
|
491
|
-
console.log(chalk.dim(' Skipping agent-first configuration (non-interactive mode).\n'));
|
|
496
|
+
prompts.log.info('Skipping agent-first configuration (non-interactive mode).');
|
|
492
497
|
}
|
|
493
498
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
499
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
500
|
+
// Completion Summary
|
|
501
|
+
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
502
|
+
await prompts.note(`Location: ${configPath}\n` +
|
|
503
|
+
`Provider: ${selectedProvider.displayName}\n` +
|
|
504
|
+
`Base URL: ${selectedProvider.baseURL}\n` +
|
|
505
|
+
`Model: ${chosenModel}\n` +
|
|
506
|
+
`Max Tokens: ${mergedConfig.maxTokens || maxTokens}\n` +
|
|
507
|
+
`Temperature: ${mergedConfig.temperature ?? 0.7}`, 'Configuration Details');
|
|
508
|
+
await prompts.note('1. Start interactive mode:\n' +
|
|
509
|
+
' $ ax-cli\n\n' +
|
|
510
|
+
'2. Run a quick test:\n' +
|
|
511
|
+
' $ ax-cli -p "Hello, introduce yourself"\n\n' +
|
|
512
|
+
'3. Initialize a project:\n' +
|
|
513
|
+
' $ ax-cli init', 'Next Steps');
|
|
514
|
+
await prompts.note(`⢠Edit config manually: ${configPath}\n` +
|
|
515
|
+
'⢠See example configs: Check "_examples" in config file\n' +
|
|
516
|
+
'⢠View help: ax-cli --help\n' +
|
|
517
|
+
'⢠Documentation: https://github.com/defai-digital/ax-cli', 'Tips');
|
|
518
|
+
prompts.outro(chalk.green('Setup complete! Happy coding!'));
|
|
513
519
|
}
|
|
514
520
|
catch (error) {
|
|
515
521
|
if (error?.message === 'canceled' || error?.name === 'canceled') {
|
|
516
|
-
|
|
522
|
+
prompts.cancel('Setup cancelled by user.');
|
|
517
523
|
process.exit(0);
|
|
518
524
|
}
|
|
519
|
-
|
|
520
|
-
console.error(chalk.dim(' ') + extractErrorMessage(error) + '\n');
|
|
525
|
+
prompts.log.error(`Setup failed: ${extractErrorMessage(error)}`);
|
|
521
526
|
process.exit(1);
|
|
522
527
|
}
|
|
523
528
|
});
|