ciscollm-cli 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/cli/commands/dashboardCommand.d.ts +1 -0
  2. package/dist/cli/commands/dashboardCommand.js +16 -0
  3. package/dist/cli/commands/dashboardCommand.js.map +1 -0
  4. package/dist/cli/commands/monitorCommand.d.ts +4 -0
  5. package/dist/cli/commands/monitorCommand.js +132 -0
  6. package/dist/cli/commands/monitorCommand.js.map +1 -0
  7. package/dist/cli/commands/runCommand.d.ts +6 -0
  8. package/dist/cli/commands/runCommand.js +635 -0
  9. package/dist/cli/commands/runCommand.js.map +1 -0
  10. package/dist/cli/commands/serverCommand.d.ts +1 -0
  11. package/dist/cli/commands/serverCommand.js +11 -0
  12. package/dist/cli/commands/serverCommand.js.map +1 -0
  13. package/dist/cli/commands/shellCommand.d.ts +1 -0
  14. package/dist/cli/commands/shellCommand.js +44 -0
  15. package/dist/cli/commands/shellCommand.js.map +1 -0
  16. package/dist/core/agent/AgentLoop.d.ts +0 -4
  17. package/dist/core/agent/AgentLoop.js +1 -158
  18. package/dist/core/agent/AgentLoop.js.map +1 -1
  19. package/dist/core/agent/AutoHealer.d.ts +12 -0
  20. package/dist/core/agent/AutoHealer.js +106 -3
  21. package/dist/core/agent/AutoHealer.js.map +1 -1
  22. package/dist/core/agent/PromptEngine.js +27 -63
  23. package/dist/core/agent/PromptEngine.js.map +1 -1
  24. package/dist/index.js +24 -903
  25. package/dist/index.js.map +1 -1
  26. package/dist/infrastructure/llm/LLMClient.js +102 -4
  27. package/dist/infrastructure/llm/LLMClient.js.map +1 -1
  28. package/dist/infrastructure/llm/ToolDefinitions.d.ts +0 -136
  29. package/dist/infrastructure/llm/ToolDefinitions.js +0 -102
  30. package/dist/infrastructure/llm/ToolDefinitions.js.map +1 -1
  31. package/dist/server/shell-simulator.d.ts +11 -0
  32. package/dist/server/shell-simulator.js +346 -4
  33. package/dist/server/shell-simulator.js.map +1 -1
  34. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,44 +5,38 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const commander_1 = require("commander");
8
- const inquirer_1 = __importDefault(require("inquirer"));
9
8
  const chalk_1 = __importDefault(require("chalk"));
10
- const readline_1 = __importDefault(require("readline"));
11
- const MultiAgentCoordinator_1 = require("./core/agent/MultiAgentCoordinator");
12
9
  const PlinkSerial_1 = require("./infrastructure/protocols/PlinkSerial");
13
- const SshSession_1 = require("./infrastructure/protocols/SshSession");
14
- const TelnetSession_1 = require("./infrastructure/protocols/TelnetSession");
15
- const LLMClient_1 = require("./infrastructure/llm/LLMClient");
16
- const AgentLoop_1 = require("./core/agent/AgentLoop");
17
- const NetconfSession_1 = require("./infrastructure/protocols/NetconfSession");
18
- const CmlSession_1 = require("./infrastructure/protocols/CmlSession");
19
10
  const ui_1 = require("./cli/ui/ui");
20
11
  const fs_1 = require("fs");
21
12
  const path_1 = require("path");
22
- const server_1 = require("./server");
23
- const shell_simulator_1 = require("./server/shell-simulator");
24
- const dashboard_1 = require("./server/dashboard");
13
+ // Import CLI command actions
14
+ const runCommand_1 = require("./cli/commands/runCommand");
15
+ const monitorCommand_1 = require("./cli/commands/monitorCommand");
16
+ const serverCommand_1 = require("./cli/commands/serverCommand");
17
+ const shellCommand_1 = require("./cli/commands/shellCommand");
18
+ const dashboardCommand_1 = require("./cli/commands/dashboardCommand");
25
19
  const program = new commander_1.Command();
26
- let activeCoordinator = null;
27
- let liveDashboardServer = null;
20
+ const coordinatorWrapper = { active: null };
21
+ const dashboardWrapper = { server: null };
28
22
  const cleanup = async () => {
29
- if (liveDashboardServer) {
23
+ if (dashboardWrapper.server) {
30
24
  try {
31
- liveDashboardServer.close();
25
+ dashboardWrapper.server.close();
32
26
  ui_1.logger.info('Live Visual Control Dashboard server closed.');
33
27
  }
34
28
  catch { }
35
- liveDashboardServer = null;
29
+ dashboardWrapper.server = null;
36
30
  }
37
- if (activeCoordinator) {
31
+ if (coordinatorWrapper.active) {
38
32
  ui_1.logger.info('Cleaning up active terminal connections and sub-processes...');
39
33
  try {
40
- await activeCoordinator.disconnectAll();
34
+ await coordinatorWrapper.active.disconnectAll();
41
35
  }
42
36
  catch (e) {
43
37
  ui_1.logger.error(`Cleanup Error: ${e.message}`);
44
38
  }
45
- activeCoordinator = null;
39
+ coordinatorWrapper.active = null;
46
40
  }
47
41
  };
48
42
  process.on('SIGINT', async () => {
@@ -56,8 +50,8 @@ process.on('SIGTERM', async () => {
56
50
  process.exit(143);
57
51
  });
58
52
  process.on('exit', () => {
59
- if (activeCoordinator) {
60
- for (const [id, session] of activeCoordinator.getSessions().entries()) {
53
+ if (coordinatorWrapper.active) {
54
+ for (const [id, session] of coordinatorWrapper.active.getSessions().entries()) {
61
55
  if (session instanceof PlinkSerial_1.PlinkSerialSession) {
62
56
  const proc = session.getProcess();
63
57
  if (proc && !proc.killed) {
@@ -87,7 +81,7 @@ program
87
81
  program
88
82
  .command('run')
89
83
  .description('Execute network configuration or optimization tasks on target Cisco hardware')
90
- .option('--protocol <type>', 'Connection protocol (serial | ssh | telnet | netconf | cml)', 'serial')
84
+ .option('--protocol <type>', 'Connection protocol (serial | ssh | telnet)', 'serial')
91
85
  .option('--provider <type>', 'LLM provider mode (local | cloud)', 'local')
92
86
  .option('--api-key <key>', 'API key for cloud provider (OpenRouter)')
93
87
  .option('-c, --com <ports>', 'COM Port interface identifier(s), comma-separated (e.g. COM3 or COM3,COM4)')
@@ -99,10 +93,6 @@ program
99
93
  .option('--env-password', 'Read device password from $CISCOLLM_PASS environment variable (safe for special chars)')
100
94
  .option('--private-key <path>', 'SSH private key file path for protocols that support key-based auth')
101
95
  .option('--passphrase <passphrase>', 'Passphrase for the SSH private key file')
102
- .option('--netconf-ready-timeout <ms>', 'NETCONF SSH ready timeout in milliseconds')
103
- .option('--netconf-hello-timeout <ms>', 'NETCONF hello exchange timeout in milliseconds')
104
- .option('--netconf-rpc-timeout <ms>', 'NETCONF RPC timeout in milliseconds')
105
- .option('--netconf-keepalive-interval <ms>', 'NETCONF SSH keepalive interval in milliseconds')
106
96
  .option('--local-type <type>', 'Local service type (ollama | lmstudio)')
107
97
  .option('--model <name>', 'Model name for compilation')
108
98
  .option('--endpoint <url>', 'Ollama/LM Studio/compatibility API server endpoint')
@@ -113,720 +103,7 @@ program
113
103
  .option('--dashboard-port <port>', 'Port to host the live Visual Control Dashboard', '3000')
114
104
  .option('-g, --goal <intent>', 'The execution goal for the agent to achieve')
115
105
  .action(async (options) => {
116
- let provider = options.provider;
117
- let localType = options.localType;
118
- if (localType) {
119
- localType = localType.toLowerCase().trim();
120
- if (localType === 'llmstudio') {
121
- localType = 'lmstudio';
122
- }
123
- }
124
- let apiKey = options.api_key || options.apiKey;
125
- let model = options.model;
126
- let endpoint = options.endpoint;
127
- let protocol = options.protocol;
128
- let com = options.com;
129
- let baud = options.baud;
130
- let host = options.host;
131
- let port = options.port;
132
- let username = options.username;
133
- let privateKey;
134
- if (options.privateKey) {
135
- privateKey = (0, fs_1.readFileSync)(options.privateKey, 'utf8');
136
- }
137
- let netconfPassphrase = options.passphrase;
138
- let password = options.envPassword
139
- ? (process.env.CISCOLLM_PASS || '')
140
- : options.password;
141
- let goal = options.goal;
142
- let strictCommandRef = options.strictCommandRef === true;
143
- let refTelemetry = options.refTelemetry !== false;
144
- let nonInteractive = options.nonInteractive === true;
145
- let rbacRole = options.rbacRole || 'admin';
146
- let dashboardPort = options.dashboardPort ? parseInt(options.dashboardPort, 10) : 3000;
147
- if (nonInteractive) {
148
- process.env.CISCOLLM_NON_INTERACTIVE = 'true';
149
- }
150
- ui_1.logger.banner();
151
- if (goal && !localType && provider === 'local') {
152
- const { chosenLocalType } = await inquirer_1.default.prompt([
153
- {
154
- type: 'list',
155
- name: 'chosenLocalType',
156
- message: chalk_1.default.cyan('Select Local LLM Service:'),
157
- choices: [
158
- { name: `${chalk_1.default.green('●')} Ollama ${chalk_1.default.dim('(http://127.0.0.1:11434/v1)')}`, value: 'ollama' },
159
- { name: `${chalk_1.default.magenta('●')} LM Studio ${chalk_1.default.dim('(http://127.0.0.1:1234/v1)')}`, value: 'lmstudio' },
160
- { name: `${chalk_1.default.yellow('●')} OpenRouter ${chalk_1.default.dim('(Cloud API)')}`, value: '__cloud__' }
161
- ],
162
- default: 'ollama'
163
- }
164
- ]);
165
- if (chosenLocalType === '__cloud__') {
166
- provider = 'cloud';
167
- localType = undefined;
168
- if (!apiKey) {
169
- const { key } = await inquirer_1.default.prompt([{ type: 'password', name: 'key', message: 'OpenRouter API Key:' }]);
170
- apiKey = key;
171
- }
172
- }
173
- else {
174
- localType = chosenLocalType;
175
- }
176
- }
177
- if (!localType)
178
- localType = 'ollama';
179
- if (!goal) {
180
- const detectedComs = await PlinkSerial_1.PlinkSerialSession.listAvailableComPorts();
181
- let currentStep = 'PROVIDER';
182
- const history = [];
183
- const answers = {
184
- provider: provider || 'local',
185
- localType: localType || 'ollama',
186
- apiKey: apiKey || '',
187
- model: model || '',
188
- endpoint: endpoint || '',
189
- protocol: protocol || 'serial',
190
- com: com || '',
191
- baud: baud || '9600',
192
- host: host || '',
193
- port: port || '',
194
- username: username || '',
195
- password: password || '',
196
- netconfAuth: 'password',
197
- netconfPrivateKey: '',
198
- netconfPassphrase: '',
199
- goal: ''
200
- };
201
- const goForward = (nextStep) => {
202
- history.push(currentStep);
203
- currentStep = nextStep;
204
- };
205
- const goBack = () => {
206
- if (history.length > 0) {
207
- currentStep = history.pop();
208
- }
209
- else {
210
- ui_1.logger.warn('Already at the first step.');
211
- }
212
- };
213
- const refreshConsole = () => {
214
- console.clear();
215
- ui_1.logger.banner();
216
- if (detectedComs.length > 0) {
217
- ui_1.logger.info('Detected active COM ports on system:');
218
- for (const port of detectedComs) {
219
- console.log(` ${chalk_1.default.yellow('•')} ${chalk_1.default.yellow(port)}`);
220
- }
221
- }
222
- console.log('');
223
- };
224
- while (currentStep !== 'CONFIRMATION') {
225
- refreshConsole();
226
- switch (currentStep) {
227
- case 'PROVIDER': {
228
- const ans = await inquirer_1.default.prompt([
229
- {
230
- type: 'list',
231
- name: 'provider',
232
- message: 'Select LLM Provider:',
233
- choices: [
234
- { name: 'Local (Ollama / LM Studio)', value: 'local' },
235
- { name: 'Cloud (OpenRouter)', value: 'cloud' }
236
- ],
237
- default: answers.provider
238
- }
239
- ]);
240
- answers.provider = ans.provider;
241
- if (answers.provider === 'local') {
242
- goForward('LOCAL_TYPE');
243
- }
244
- else {
245
- goForward('API_KEY');
246
- }
247
- break;
248
- }
249
- case 'LOCAL_TYPE': {
250
- const ans = await inquirer_1.default.prompt([
251
- {
252
- type: 'list',
253
- name: 'localType',
254
- message: 'Select Local LLM Service:',
255
- choices: [
256
- { name: 'Ollama', value: 'ollama' },
257
- { name: 'LM Studio', value: 'lmstudio' },
258
- { name: chalk_1.default.dim('< Go Back'), value: '__back__' }
259
- ],
260
- default: answers.localType
261
- }
262
- ]);
263
- if (ans.localType === '__back__') {
264
- goBack();
265
- }
266
- else {
267
- answers.localType = ans.localType;
268
- goForward('MODEL');
269
- }
270
- break;
271
- }
272
- case 'API_KEY': {
273
- const ans = await inquirer_1.default.prompt([
274
- {
275
- type: 'input',
276
- name: 'apiKey',
277
- message: 'Enter OpenRouter API Key (or type "back" to go back):',
278
- default: answers.apiKey || undefined
279
- }
280
- ]);
281
- if (ans.apiKey.trim().toLowerCase() === 'back') {
282
- goBack();
283
- }
284
- else {
285
- answers.apiKey = ans.apiKey;
286
- goForward('MODEL');
287
- }
288
- break;
289
- }
290
- case 'MODEL': {
291
- const defaultModel = answers.model || (answers.provider === 'cloud'
292
- ? 'nvidia/nemotron-3-super-120b-a12b:free'
293
- : 'qwen3.5:4b');
294
- const ans = await inquirer_1.default.prompt([
295
- {
296
- type: 'input',
297
- name: 'model',
298
- message: 'Enter LLM Model Name (or type "back" to go back):',
299
- default: defaultModel
300
- }
301
- ]);
302
- if (ans.model.trim().toLowerCase() === 'back') {
303
- goBack();
304
- }
305
- else {
306
- answers.model = ans.model;
307
- goForward('ENDPOINT');
308
- }
309
- break;
310
- }
311
- case 'ENDPOINT': {
312
- const defaultEndpoint = answers.endpoint || (answers.provider === 'cloud'
313
- ? 'https://openrouter.ai/api/v1'
314
- : (answers.localType === 'lmstudio'
315
- ? 'http://127.0.0.1:1234/v1'
316
- : 'http://127.0.0.1:11434/v1'));
317
- const ans = await inquirer_1.default.prompt([
318
- {
319
- type: 'input',
320
- name: 'endpoint',
321
- message: 'Enter LLM API Endpoint URL (or type "back" to go back):',
322
- default: defaultEndpoint
323
- }
324
- ]);
325
- if (ans.endpoint.trim().toLowerCase() === 'back') {
326
- goBack();
327
- }
328
- else {
329
- answers.endpoint = ans.endpoint;
330
- goForward('PROTOCOL');
331
- }
332
- break;
333
- }
334
- case 'PROTOCOL': {
335
- const ans = await inquirer_1.default.prompt([
336
- {
337
- type: 'list',
338
- name: 'protocol',
339
- message: 'Select Connection Protocol:',
340
- choices: [
341
- { name: 'serial', value: 'serial' },
342
- { name: 'ssh', value: 'ssh' },
343
- { name: 'telnet', value: 'telnet' },
344
- { name: 'netconf', value: 'netconf' },
345
- { name: 'cml', value: 'cml' },
346
- { name: chalk_1.default.dim('< Go Back'), value: '__back__' }
347
- ],
348
- default: answers.protocol
349
- }
350
- ]);
351
- if (ans.protocol === '__back__') {
352
- goBack();
353
- }
354
- else {
355
- answers.protocol = ans.protocol;
356
- if (answers.protocol === 'serial') {
357
- goForward('SERIAL_COM');
358
- }
359
- else if (answers.protocol === 'ssh' ||
360
- answers.protocol === 'telnet' ||
361
- answers.protocol === 'netconf' ||
362
- answers.protocol === 'cml') {
363
- goForward('IP_HOST');
364
- }
365
- else {
366
- goForward('GOAL');
367
- }
368
- }
369
- break;
370
- }
371
- case 'SERIAL_COM': {
372
- if (detectedComs.length > 0) {
373
- const choices = detectedComs.map(port => {
374
- const match = /^(COM\d+)\b/i.exec(port);
375
- const portValue = match ? match[1].toUpperCase() : port;
376
- return { name: port, value: portValue };
377
- });
378
- choices.push({ name: 'Enter COM port(s) manually', value: '__manual__' });
379
- choices.push({ name: chalk_1.default.dim('< Go Back'), value: '__back__' });
380
- const ans = await inquirer_1.default.prompt([
381
- {
382
- type: 'checkbox',
383
- name: 'coms',
384
- message: 'Select COM Port(s) (Use Space to select, Enter to confirm):',
385
- choices: choices,
386
- validate: (input) => {
387
- if (input.length === 0) {
388
- return 'You must select at least one option.';
389
- }
390
- if (input.includes('__back__') && input.length > 1) {
391
- return 'Cannot select "< Go Back" along with other ports.';
392
- }
393
- if (input.includes('__manual__') && input.length > 1) {
394
- return 'Cannot select "Enter COM port(s) manually" along with other ports.';
395
- }
396
- return true;
397
- }
398
- }
399
- ]);
400
- if (ans.coms.includes('__back__')) {
401
- goBack();
402
- }
403
- else if (ans.coms.includes('__manual__')) {
404
- const manualAns = await inquirer_1.default.prompt([
405
- {
406
- type: 'input',
407
- name: 'com',
408
- message: 'Enter COM Port name(s) (comma-separated, e.g. COM3 or COM3,COM4):',
409
- validate: (input) => input.trim().length > 0 ? true : 'COM port is required.'
410
- }
411
- ]);
412
- answers.com = manualAns.com;
413
- goForward('SERIAL_BAUD');
414
- }
415
- else {
416
- answers.com = ans.coms.join(',');
417
- goForward('SERIAL_BAUD');
418
- }
419
- }
420
- else {
421
- const ans = await inquirer_1.default.prompt([
422
- {
423
- type: 'input',
424
- name: 'com',
425
- message: 'Enter COM Port name(s) (comma-separated, e.g. COM3 or COM3,COM4) (or type "back" to go back):',
426
- default: answers.com || undefined,
427
- validate: (input) => {
428
- if (input.trim().toLowerCase() === 'back')
429
- return true;
430
- return input.trim().length > 0 ? true : 'COM port is required.';
431
- }
432
- }
433
- ]);
434
- if (ans.com.trim().toLowerCase() === 'back') {
435
- goBack();
436
- }
437
- else {
438
- answers.com = ans.com;
439
- goForward('SERIAL_BAUD');
440
- }
441
- }
442
- break;
443
- }
444
- case 'SERIAL_BAUD': {
445
- const ans = await inquirer_1.default.prompt([
446
- {
447
- type: 'list',
448
- name: 'baud',
449
- message: 'Select Serial Baud Rate:',
450
- choices: [
451
- '9600', '19200', '38400', '57600', '115200',
452
- { name: chalk_1.default.dim('< Go Back'), value: '__back__' }
453
- ],
454
- default: answers.baud
455
- }
456
- ]);
457
- if (ans.baud === '__back__') {
458
- goBack();
459
- }
460
- else {
461
- answers.baud = ans.baud;
462
- goForward('GOAL');
463
- }
464
- break;
465
- }
466
- case 'IP_HOST': {
467
- const ans = await inquirer_1.default.prompt([
468
- {
469
- type: 'input',
470
- name: 'host',
471
- message: 'Enter Target IP address(es) / Hostname(s) (comma-separated) (or type "back" to go back):',
472
- default: answers.host || undefined,
473
- validate: (input) => {
474
- if (input.trim().toLowerCase() === 'back')
475
- return true;
476
- return input.trim().length > 0 ? true : 'Host address is required.';
477
- }
478
- }
479
- ]);
480
- if (ans.host.trim().toLowerCase() === 'back') {
481
- goBack();
482
- }
483
- else {
484
- answers.host = ans.host;
485
- goForward('IP_PORT');
486
- }
487
- break;
488
- }
489
- case 'IP_PORT': {
490
- const ans = await inquirer_1.default.prompt([
491
- {
492
- type: 'input',
493
- name: 'port',
494
- message: 'Enter Connection Port (leave empty for default) (or type "back" to go back):',
495
- default: answers.port || undefined
496
- }
497
- ]);
498
- if (ans.port.trim().toLowerCase() === 'back') {
499
- goBack();
500
- }
501
- else {
502
- answers.port = ans.port;
503
- goForward('IP_USER');
504
- }
505
- break;
506
- }
507
- case 'IP_USER': {
508
- const ans = await inquirer_1.default.prompt([
509
- {
510
- type: 'input',
511
- name: 'username',
512
- message: 'Enter Device Username (leave empty if none) (or type "back" to go back):',
513
- default: answers.username || undefined
514
- }
515
- ]);
516
- if (ans.username.trim().toLowerCase() === 'back') {
517
- goBack();
518
- }
519
- else {
520
- answers.username = ans.username;
521
- goForward(answers.protocol === 'netconf' ? 'NETCONF_AUTH' : 'IP_PASS');
522
- }
523
- break;
524
- }
525
- case 'NETCONF_AUTH': {
526
- const ans = await inquirer_1.default.prompt([
527
- {
528
- type: 'list',
529
- name: 'netconfAuth',
530
- message: 'NETCONF authentication method: choose password or SSH key-based auth.',
531
- choices: [
532
- { name: 'Password login', value: 'password' },
533
- { name: 'SSH private key', value: 'key' },
534
- { name: chalk_1.default.dim('< Go Back'), value: '__back__' }
535
- ],
536
- default: answers.netconfAuth || 'password'
537
- }
538
- ]);
539
- if (ans.netconfAuth === '__back__') {
540
- goBack();
541
- }
542
- else {
543
- answers.netconfAuth = ans.netconfAuth;
544
- goForward(ans.netconfAuth === 'key' ? 'NETCONF_KEY_PATH' : 'IP_PASS');
545
- }
546
- break;
547
- }
548
- case 'NETCONF_KEY_PATH': {
549
- const ans = await inquirer_1.default.prompt([
550
- {
551
- type: 'input',
552
- name: 'netconfPrivateKey',
553
- message: 'Enter SSH private key file path for NETCONF (or type "back" to change auth method):',
554
- default: answers.netconfPrivateKey || undefined,
555
- validate: (input) => {
556
- if (input.trim().toLowerCase() === 'back')
557
- return true;
558
- return input.trim().length > 0 ? true : 'Private key path is required for key-based auth.';
559
- }
560
- }
561
- ]);
562
- if (ans.netconfPrivateKey.trim().toLowerCase() === 'back') {
563
- goBack();
564
- }
565
- else {
566
- answers.netconfPrivateKey = ans.netconfPrivateKey;
567
- goForward('NETCONF_PASSPHRASE');
568
- }
569
- break;
570
- }
571
- case 'NETCONF_PASSPHRASE': {
572
- const ans = await inquirer_1.default.prompt([
573
- {
574
- type: 'password',
575
- name: 'netconfPassphrase',
576
- message: 'Enter SSH key passphrase for NETCONF (leave empty if none) (or type "back" to go back):',
577
- default: answers.netconfPassphrase || undefined
578
- }
579
- ]);
580
- if (ans.netconfPassphrase === 'back') {
581
- goBack();
582
- }
583
- else {
584
- answers.netconfPassphrase = ans.netconfPassphrase;
585
- goForward('GOAL');
586
- }
587
- break;
588
- }
589
- case 'IP_PASS': {
590
- const ans = await inquirer_1.default.prompt([
591
- {
592
- type: 'password',
593
- name: 'password',
594
- message: 'Enter Device Password (leave empty if none) (or type "back" to go back):',
595
- default: answers.password || undefined
596
- }
597
- ]);
598
- if (ans.password === 'back') {
599
- goBack();
600
- }
601
- else {
602
- answers.password = ans.password;
603
- goForward('GOAL');
604
- }
605
- break;
606
- }
607
- case 'GOAL': {
608
- const ans = await inquirer_1.default.prompt([
609
- {
610
- type: 'input',
611
- name: 'goal',
612
- message: 'Enter your configuration goal for the Cisco device (or type "back" to go back):',
613
- default: answers.goal || undefined,
614
- validate: (input) => {
615
- if (input.trim().toLowerCase() === 'back')
616
- return true;
617
- return input.trim().length > 0 ? true : 'Goal is required.';
618
- }
619
- }
620
- ]);
621
- if (ans.goal.trim().toLowerCase() === 'back') {
622
- goBack();
623
- }
624
- else {
625
- answers.goal = ans.goal;
626
- console.log(chalk_1.default.bold.yellow('Configuration Summary:'));
627
- console.log(`- LLM Provider: ${chalk_1.default.cyan(answers.provider)}` + (answers.provider === 'local' ? ` (${answers.localType})` : ''));
628
- console.log(`- Model Name: ${chalk_1.default.cyan(answers.model || (answers.provider === 'cloud' ? 'nvidia/nemotron-3-super-120b-a12b:free' : 'qwen3.5:4b'))}`);
629
- console.log(`- API Endpoint: ${chalk_1.default.cyan(answers.endpoint || 'default')}`);
630
- console.log(`- Protocol: ${chalk_1.default.cyan(answers.protocol)}`);
631
- if (answers.protocol === 'serial') {
632
- console.log(`- COM Port: ${chalk_1.default.cyan(answers.com)}`);
633
- console.log(`- Baud Rate: ${chalk_1.default.cyan(answers.baud)}`);
634
- }
635
- else if (answers.protocol === 'ssh' ||
636
- answers.protocol === 'telnet' ||
637
- answers.protocol === 'netconf' ||
638
- answers.protocol === 'cml') {
639
- console.log(`- Host Target: ${chalk_1.default.cyan(answers.host)}`);
640
- console.log(`- Port: ${chalk_1.default.cyan(answers.port || 'default')}`);
641
- console.log(`- Username: ${chalk_1.default.cyan(answers.username || '(none)')}`);
642
- if (answers.protocol === 'netconf') {
643
- console.log(`- NETCONF Auth: ${chalk_1.default.cyan(answers.netconfAuth || 'password')}` + (answers.netconfAuth === 'key' ? ` (${chalk_1.default.cyan(answers.netconfPrivateKey || '(unset)')})` : ''));
644
- }
645
- }
646
- console.log(`- Config Goal: ${chalk_1.default.green(`"${answers.goal}"`)}`);
647
- const confirmAns = await inquirer_1.default.prompt([
648
- {
649
- type: 'list',
650
- name: 'confirm',
651
- message: 'Proceed with this configuration?',
652
- choices: [
653
- { name: 'Yes, start agent execution', value: 'yes' },
654
- { name: 'No, edit goal again', value: 'edit_goal' },
655
- { name: 'No, start wizard from the beginning', value: 'restart' },
656
- { name: 'Cancel and exit', value: 'cancel' }
657
- ]
658
- }
659
- ]);
660
- if (confirmAns.confirm === 'yes') {
661
- currentStep = 'CONFIRMATION';
662
- }
663
- else if (confirmAns.confirm === 'edit_goal') {
664
- currentStep = 'GOAL';
665
- }
666
- else if (confirmAns.confirm === 'restart') {
667
- history.length = 0;
668
- currentStep = 'PROVIDER';
669
- }
670
- else {
671
- ui_1.logger.info('Configuration wizard cancelled.');
672
- process.exit(0);
673
- }
674
- }
675
- break;
676
- }
677
- }
678
- }
679
- provider = answers.provider;
680
- localType = answers.localType;
681
- apiKey = answers.apiKey;
682
- model = answers.model;
683
- endpoint = answers.endpoint;
684
- protocol = answers.protocol;
685
- com = answers.com;
686
- baud = answers.baud;
687
- host = answers.host;
688
- port = answers.port;
689
- username = answers.username;
690
- password = answers.password;
691
- if (answers.netconfAuth === 'key' && answers.netconfPrivateKey) {
692
- privateKey = (0, fs_1.readFileSync)(answers.netconfPrivateKey, 'utf8');
693
- }
694
- netconfPassphrase = answers.netconfPassphrase || netconfPassphrase;
695
- goal = answers.goal;
696
- }
697
- ui_1.logger.info(`Initializing system link in [${provider.toUpperCase()}] mode using ${protocol.toUpperCase()}...`);
698
- ui_1.logger.info(`Command reference policy: strict=${strictCommandRef ? 'on' : 'off'}, telemetry=${refTelemetry ? 'on' : 'off'}`);
699
- activeCoordinator = new MultiAgentCoordinator_1.MultiAgentCoordinator();
700
- try {
701
- liveDashboardServer = (0, dashboard_1.startDashboardServer)(activeCoordinator, dashboardPort);
702
- }
703
- catch (e) {
704
- ui_1.logger.warn(`Failed to auto-start live Visual Control Dashboard: ${e.message}`);
705
- }
706
- const netconfSessionOptions = {
707
- username,
708
- password,
709
- privateKey,
710
- passphrase: netconfPassphrase,
711
- readyTimeoutMs: options.netconfReadyTimeout ? parseInt(options.netconfReadyTimeout, 10) : undefined,
712
- helloTimeoutMs: options.netconfHelloTimeout ? parseInt(options.netconfHelloTimeout, 10) : undefined,
713
- rpcTimeoutMs: options.netconfRpcTimeout ? parseInt(options.netconfRpcTimeout, 10) : undefined,
714
- keepaliveInterval: options.netconfKeepaliveInterval ? parseInt(options.netconfKeepaliveInterval, 10) : undefined
715
- };
716
- try {
717
- if (protocol === 'serial') {
718
- if (!com) {
719
- throw new Error('COM port (-c, --com) is required for serial protocol connections.');
720
- }
721
- const ports = com.split(',').map((p) => p.trim()).filter((p) => p.length > 0);
722
- for (const port of ports) {
723
- const session = new PlinkSerial_1.PlinkSerialSession(port, parseInt(baud, 10));
724
- activeCoordinator.registerSession(port, session);
725
- }
726
- }
727
- else if (protocol === 'ssh') {
728
- if (!host || !username) {
729
- throw new Error('Host (--host) and Username (-u, --username) are required for SSH connections.');
730
- }
731
- const hosts = host.split(',').map((h) => h.trim()).filter((h) => h.length > 0);
732
- for (const h of hosts) {
733
- const session = new SshSession_1.SshSession({
734
- host: h,
735
- port: port ? parseInt(port, 10) : 22,
736
- username: username,
737
- password: password
738
- });
739
- activeCoordinator.registerSession(h, session);
740
- }
741
- }
742
- else if (protocol === 'telnet') {
743
- if (!host) {
744
- throw new Error('Host (--host) is required for Telnet connections.');
745
- }
746
- const hosts = host.split(',').map((h) => h.trim()).filter((h) => h.length > 0);
747
- for (const h of hosts) {
748
- const session = new TelnetSession_1.TelnetSession({
749
- host: h,
750
- port: port ? parseInt(port, 10) : 23,
751
- username: username,
752
- password: password
753
- });
754
- activeCoordinator.registerSession(h, session);
755
- }
756
- }
757
- else if (protocol === 'netconf') {
758
- if (!host) {
759
- throw new Error('Host (--host) is required for NETCONF protocol connections.');
760
- }
761
- const hosts = host.split(',').map((h) => h.trim()).filter((h) => h.length > 0);
762
- for (const h of hosts) {
763
- const session = new NetconfSession_1.NetconfSession(h, port ? parseInt(port, 10) : 830, netconfSessionOptions);
764
- activeCoordinator.registerSession(h, session);
765
- }
766
- }
767
- else if (protocol === 'cml') {
768
- let endpointUrl = host || endpoint || 'http://127.0.0.1:8080';
769
- if (endpointUrl && !endpointUrl.startsWith('http://') && !endpointUrl.startsWith('https://')) {
770
- endpointUrl = `https://${endpointUrl}`;
771
- }
772
- const session = new CmlSession_1.CmlSession(endpointUrl, username, password);
773
- activeCoordinator.registerSession('cml-sandbox', session);
774
- }
775
- else {
776
- throw new Error(`Unsupported connection protocol type: ${protocol}`);
777
- }
778
- if (provider === 'local' && !endpoint) {
779
- if (localType === 'lmstudio') {
780
- endpoint = 'http://127.0.0.1:1234/v1';
781
- ui_1.logger.info(`LM Studio endpoint: ${chalk_1.default.cyan(endpoint)}`);
782
- }
783
- else {
784
- endpoint = 'http://127.0.0.1:11434/v1';
785
- ui_1.logger.info(`Ollama endpoint: ${chalk_1.default.cyan(endpoint)}`);
786
- }
787
- }
788
- const localAIClient = new LLMClient_1.LLMClient(provider, endpoint, model, apiKey, localType);
789
- const llmSpinner = (0, ui_1.createSpinner)('Preflight check: validating LLM endpoint reachability...').start();
790
- try {
791
- await localAIClient.ensureReachable();
792
- const setupOk = await localAIClient.setupModelIfNeeded(status => {
793
- llmSpinner.text = `LLM preflight: ${status}`;
794
- });
795
- if (setupOk) {
796
- llmSpinner.succeed('LLM endpoint is reachable and model is ready.');
797
- }
798
- else {
799
- llmSpinner.warn('LLM endpoint is reachable (model auto-load skipped/not supported by this provider).');
800
- }
801
- }
802
- catch (err) {
803
- llmSpinner.fail('LLM endpoint preflight failed.');
804
- throw err;
805
- }
806
- const connSpinner = (0, ui_1.createSpinner)('Connecting to target network devices...').start();
807
- try {
808
- await activeCoordinator.connectAll();
809
- connSpinner.succeed('All hardware sessions synchronized successfully.');
810
- }
811
- catch (err) {
812
- connSpinner.fail('Connection failed.');
813
- throw err;
814
- }
815
- const agent = new AgentLoop_1.CiscoAgentLoop(localAIClient, activeCoordinator, {
816
- strictReferenceMode: strictCommandRef,
817
- referenceTelemetry: refTelemetry,
818
- rbacRole: rbacRole
819
- });
820
- await agent.run(goal);
821
- }
822
- catch (err) {
823
- ui_1.logger.critical(`Execution Error: ${err.message}`);
824
- }
825
- finally {
826
- await cleanup();
827
- ui_1.logger.info('Session Terminated. Pipelines detached.');
828
- process.exit(0);
829
- }
106
+ await (0, runCommand_1.runAction)(options, coordinatorWrapper, dashboardWrapper, cleanup);
830
107
  });
831
108
  program
832
109
  .command('monitor')
@@ -849,185 +126,29 @@ program
849
126
  .option('--non-interactive', 'Enable completely autonomous, non-interactive healing')
850
127
  .option('--min-confidence <confidence>', 'Minimum AI confidence threshold to apply remediation', '0.80')
851
128
  .action(async (options) => {
852
- let provider = options.provider;
853
- let localType = options.localType;
854
- if (localType) {
855
- localType = localType.toLowerCase().trim();
856
- if (localType === 'llmstudio') {
857
- localType = 'lmstudio';
858
- }
859
- }
860
- let apiKey = options.api_key || options.apiKey;
861
- let model = options.model;
862
- let endpoint = options.endpoint;
863
- let protocol = options.protocol;
864
- let com = options.com;
865
- let baud = options.baud;
866
- let host = options.host;
867
- let port = options.port;
868
- let username = options.username;
869
- let privateKey;
870
- if (options.privateKey) {
871
- privateKey = (0, fs_1.readFileSync)(options.privateKey, 'utf8');
872
- }
873
- let netconfPassphrase = options.passphrase;
874
- let password = options.envPassword
875
- ? (process.env.CISCOLLM_PASS || '')
876
- : options.password;
877
- let nonInteractive = options.nonInteractive === true;
878
- let minConfidence = parseFloat(options.minConfidence || '0.80');
879
- ui_1.logger.banner();
880
- ui_1.logger.info(`Initializing auto-healing monitoring in [${provider.toUpperCase()}] mode...`);
881
- if (!localType)
882
- localType = 'ollama';
883
- activeCoordinator = new MultiAgentCoordinator_1.MultiAgentCoordinator();
884
- try {
885
- if (protocol === 'serial') {
886
- if (!com) {
887
- throw new Error('COM port (-c, --com) is required for serial protocol connections.');
888
- }
889
- const ports = com.split(',').map((p) => p.trim()).filter((p) => p.length > 0);
890
- for (const port of ports) {
891
- const session = new PlinkSerial_1.PlinkSerialSession(port, parseInt(baud, 10));
892
- activeCoordinator.registerSession(port, session);
893
- }
894
- }
895
- else if (protocol === 'ssh') {
896
- if (!host || !username) {
897
- throw new Error('Host (--host) and Username (-u, --username) are required for SSH connections.');
898
- }
899
- const hosts = host.split(',').map((h) => h.trim()).filter((h) => h.length > 0);
900
- for (const h of hosts) {
901
- const session = new SshSession_1.SshSession({
902
- host: h,
903
- port: port ? parseInt(port, 10) : 22,
904
- username: username,
905
- password: password
906
- });
907
- activeCoordinator.registerSession(h, session);
908
- }
909
- }
910
- else if (protocol === 'telnet') {
911
- if (!host) {
912
- throw new Error('Host (--host) is required for Telnet connections.');
913
- }
914
- const hosts = host.split(',').map((h) => h.trim()).filter((h) => h.length > 0);
915
- for (const h of hosts) {
916
- const session = new TelnetSession_1.TelnetSession({
917
- host: h,
918
- port: port ? parseInt(port, 10) : 23,
919
- username: username,
920
- password: password
921
- });
922
- activeCoordinator.registerSession(h, session);
923
- }
924
- }
925
- else {
926
- throw new Error(`Unsupported protocol for monitoring: ${protocol}`);
927
- }
928
- if (provider === 'local' && !endpoint) {
929
- if (localType === 'lmstudio') {
930
- endpoint = 'http://127.0.0.1:1234/v1';
931
- }
932
- else {
933
- endpoint = 'http://127.0.0.1:11434/v1';
934
- }
935
- }
936
- const localAIClient = new LLMClient_1.LLMClient(provider, endpoint, model, apiKey, localType);
937
- const llmSpinner = (0, ui_1.createSpinner)('Preflight check: validating LLM endpoint reachability...').start();
938
- try {
939
- await localAIClient.ensureReachable();
940
- llmSpinner.succeed('LLM endpoint is ready.');
941
- }
942
- catch (err) {
943
- llmSpinner.fail('LLM endpoint preflight failed.');
944
- throw err;
945
- }
946
- const connSpinner = (0, ui_1.createSpinner)('Connecting to target network devices...').start();
947
- try {
948
- await activeCoordinator.connectAll();
949
- connSpinner.succeed('All hardware sessions synchronized successfully.');
950
- }
951
- catch (err) {
952
- connSpinner.fail('Connection failed.');
953
- throw err;
954
- }
955
- const { AutoHealer } = require('./core/agent/AutoHealer');
956
- const healer = new AutoHealer(localAIClient, activeCoordinator, {
957
- nonInteractive,
958
- minConfidence
959
- });
960
- healer.start();
961
- ui_1.logger.info('Monitoring active. Press Ctrl+C to terminate session.');
962
- await new Promise(() => { });
963
- }
964
- catch (err) {
965
- ui_1.logger.critical(`Monitoring Error: ${err.message}`);
966
- }
967
- finally {
968
- await cleanup();
969
- ui_1.logger.info('Session Terminated. Pipelines detached.');
970
- process.exit(0);
971
- }
129
+ await (0, monitorCommand_1.monitorAction)(options, coordinatorWrapper, cleanup);
972
130
  });
973
131
  program
974
132
  .command('server')
975
- .description('Start the Cisco IOS Multi-Protocol Test Simulator (SSH, Telnet, NETCONF, and HTTP LLM Mock)')
976
- .option('--ssh-port <port>', 'Port for the mock SSH & NETCONF server', '2222')
133
+ .description('Start the Cisco IOS Multi-Protocol Test Simulator (SSH, Telnet, and HTTP LLM Mock)')
134
+ .option('--ssh-port <port>', 'Port for the mock SSH server', '2222')
977
135
  .option('--telnet-port <port>', 'Port for the mock Telnet server', '2323')
978
136
  .option('--http-port <port>', 'Port for the mock HTTP LLM server', '11434')
979
137
  .action((options) => {
980
- const sshPort = parseInt(options.sshPort, 10);
981
- const telnetPort = parseInt(options.telnetPort, 10);
982
- const httpPort = parseInt(options.httpPort, 10);
983
- (0, server_1.startSimulator)({ sshPort, telnetPort, httpPort });
138
+ (0, serverCommand_1.serverAction)(options);
984
139
  });
985
140
  program
986
141
  .command('shell')
987
142
  .description('Launch the interactive Cisco IOS mock shell simulator directly')
988
143
  .action(() => {
989
- const simulator = new shell_simulator_1.ShellSimulator();
990
- console.clear();
991
- console.log(chalk_1.default.bold.yellow('============================================================'));
992
- console.log(chalk_1.default.bold.yellow(' Cisco IOS Interactive Mock Shell Simulator (v1.1.0)'));
993
- console.log(chalk_1.default.bold.yellow('============================================================'));
994
- const welcomeBanner = `\r\nCisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 15.0(2)SE4, RELEASE SOFTWARE (fc1)\r\nTechnical Support: http://www.cisco.com/techsupport\r\nCopyright (c) 1986-2013 by Cisco Systems, Inc.\r\nCompiled Wed 26-Jun-13 02:49 by prod_rel_team\r\n\r\n`;
995
- console.log(welcomeBanner);
996
- const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
997
- const promptUser = () => {
998
- rl.question(simulator.getPrompt(), (line) => {
999
- const cmd = line.trim();
1000
- if (cmd.toLowerCase() === 'exit' && simulator.mode === 'USER_EXEC') {
1001
- console.log('Connection closed by foreign host.');
1002
- rl.close();
1003
- return;
1004
- }
1005
- try {
1006
- const output = simulator.execute(cmd);
1007
- if (output) {
1008
- console.log(output);
1009
- }
1010
- }
1011
- catch (err) {
1012
- console.log(`% Error: ${err.message}`);
1013
- }
1014
- promptUser();
1015
- });
1016
- };
1017
- promptUser();
1018
- rl.on('close', () => {
1019
- process.exit(0);
1020
- });
144
+ (0, shellCommand_1.shellAction)();
1021
145
  });
1022
146
  program
1023
147
  .command('dashboard')
1024
148
  .description('Start the Visual Control Dashboard server standalone')
1025
149
  .option('--port <number>', 'Port for the dashboard server', '3000')
1026
150
  .action((options) => {
1027
- const port = parseInt(options.port, 10);
1028
- const coordinator = new MultiAgentCoordinator_1.MultiAgentCoordinator();
1029
- (0, dashboard_1.startDashboardServer)(coordinator, port);
1030
- console.log(chalk_1.default.yellow('Standalone mode: Visualizing historical records and active topology when connected.'));
151
+ (0, dashboardCommand_1.dashboardAction)(options);
1031
152
  });
1032
153
  program.parse(process.argv);
1033
154
  //# sourceMappingURL=index.js.map