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.
- package/dist/cli/commands/dashboardCommand.d.ts +1 -0
- package/dist/cli/commands/dashboardCommand.js +16 -0
- package/dist/cli/commands/dashboardCommand.js.map +1 -0
- package/dist/cli/commands/monitorCommand.d.ts +4 -0
- package/dist/cli/commands/monitorCommand.js +132 -0
- package/dist/cli/commands/monitorCommand.js.map +1 -0
- package/dist/cli/commands/runCommand.d.ts +6 -0
- package/dist/cli/commands/runCommand.js +635 -0
- package/dist/cli/commands/runCommand.js.map +1 -0
- package/dist/cli/commands/serverCommand.d.ts +1 -0
- package/dist/cli/commands/serverCommand.js +11 -0
- package/dist/cli/commands/serverCommand.js.map +1 -0
- package/dist/cli/commands/shellCommand.d.ts +1 -0
- package/dist/cli/commands/shellCommand.js +44 -0
- package/dist/cli/commands/shellCommand.js.map +1 -0
- package/dist/core/agent/AgentLoop.d.ts +0 -4
- package/dist/core/agent/AgentLoop.js +1 -158
- package/dist/core/agent/AgentLoop.js.map +1 -1
- package/dist/core/agent/AutoHealer.d.ts +12 -0
- package/dist/core/agent/AutoHealer.js +106 -3
- package/dist/core/agent/AutoHealer.js.map +1 -1
- package/dist/core/agent/PromptEngine.js +27 -63
- package/dist/core/agent/PromptEngine.js.map +1 -1
- package/dist/index.js +24 -903
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/llm/LLMClient.js +102 -4
- package/dist/infrastructure/llm/LLMClient.js.map +1 -1
- package/dist/infrastructure/llm/ToolDefinitions.d.ts +0 -136
- package/dist/infrastructure/llm/ToolDefinitions.js +0 -102
- package/dist/infrastructure/llm/ToolDefinitions.js.map +1 -1
- package/dist/server/shell-simulator.d.ts +11 -0
- package/dist/server/shell-simulator.js +346 -4
- package/dist/server/shell-simulator.js.map +1 -1
- 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
|
-
|
|
23
|
-
const
|
|
24
|
-
const
|
|
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
|
-
|
|
27
|
-
|
|
20
|
+
const coordinatorWrapper = { active: null };
|
|
21
|
+
const dashboardWrapper = { server: null };
|
|
28
22
|
const cleanup = async () => {
|
|
29
|
-
if (
|
|
23
|
+
if (dashboardWrapper.server) {
|
|
30
24
|
try {
|
|
31
|
-
|
|
25
|
+
dashboardWrapper.server.close();
|
|
32
26
|
ui_1.logger.info('Live Visual Control Dashboard server closed.');
|
|
33
27
|
}
|
|
34
28
|
catch { }
|
|
35
|
-
|
|
29
|
+
dashboardWrapper.server = null;
|
|
36
30
|
}
|
|
37
|
-
if (
|
|
31
|
+
if (coordinatorWrapper.active) {
|
|
38
32
|
ui_1.logger.info('Cleaning up active terminal connections and sub-processes...');
|
|
39
33
|
try {
|
|
40
|
-
await
|
|
34
|
+
await coordinatorWrapper.active.disconnectAll();
|
|
41
35
|
}
|
|
42
36
|
catch (e) {
|
|
43
37
|
ui_1.logger.error(`Cleanup Error: ${e.message}`);
|
|
44
38
|
}
|
|
45
|
-
|
|
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 (
|
|
60
|
-
for (const [id, session] of
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
976
|
-
.option('--ssh-port <port>', 'Port for the mock SSH
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|