ciscollm-cli 1.3.0 → 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/LICENSE +21 -21
- package/README.md +242 -242
- 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 +129 -26
- package/dist/core/agent/AutoHealer.js.map +1 -1
- package/dist/core/agent/HierarchicalAgentManager.d.ts +1 -1
- package/dist/core/agent/HierarchicalAgentManager.js +21 -5
- package/dist/core/agent/HierarchicalAgentManager.js.map +1 -1
- package/dist/core/agent/PromptEngine.js +33 -68
- package/dist/core/agent/PromptEngine.js.map +1 -1
- package/dist/core/guardrails/AuditLogger.js +4 -4
- package/dist/core/guardrails/CommandFirewall.js +15 -0
- package/dist/core/guardrails/CommandFirewall.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/infrastructure/protocols/PlinkSerial.js +1 -1
- package/dist/infrastructure/protocols/PlinkSerial.js.map +1 -1
- package/dist/server/dashboard.js +1033 -1033
- package/dist/server/index.js +8 -8
- package/dist/server/shell-simulator.d.ts +28 -1
- package/dist/server/shell-simulator.js +599 -73
- package/dist/server/shell-simulator.js.map +1 -1
- package/dist/server/ssh.js +20 -20
- package/package.json +54 -54
|
@@ -50,6 +50,7 @@ class ShellSimulator {
|
|
|
50
50
|
shellFunctions = {};
|
|
51
51
|
ospfEnabled = false;
|
|
52
52
|
ospfProcessId = null;
|
|
53
|
+
ospfNetworks = [];
|
|
53
54
|
ripEnabled = false;
|
|
54
55
|
ripVersion = 2;
|
|
55
56
|
ripAutoSummary = false;
|
|
@@ -69,6 +70,12 @@ class ShellSimulator {
|
|
|
69
70
|
natRules = [];
|
|
70
71
|
acls = new Map();
|
|
71
72
|
ipRoutingEnabled = true;
|
|
73
|
+
featuresEnabled = new Set();
|
|
74
|
+
vpcDomainId = null;
|
|
75
|
+
vpcPeerKeepalive = null;
|
|
76
|
+
vnSegments = new Map();
|
|
77
|
+
vrfs = new Map();
|
|
78
|
+
activeVrf = null;
|
|
72
79
|
flashFiles = new Set(['c2960-lanbasek9-mz.150-2.SE4.bin']);
|
|
73
80
|
pendingCopyDest = null;
|
|
74
81
|
backupState = null;
|
|
@@ -123,12 +130,22 @@ class ShellSimulator {
|
|
|
123
130
|
return `${this.hostname}(config-ext-nacl)# `;
|
|
124
131
|
case 'VLAN_CONFIG':
|
|
125
132
|
return `${this.hostname}(config-vlan)# `;
|
|
133
|
+
case 'VPC_CONFIG':
|
|
134
|
+
return `${this.hostname}(config-vpc-domain)# `;
|
|
135
|
+
case 'VRF_CONFIG':
|
|
136
|
+
return `${this.hostname}(config-vrf)# `;
|
|
137
|
+
case 'VRF_AF_CONFIG':
|
|
138
|
+
return `${this.hostname}(config-vrf-af-ipv4)# `;
|
|
126
139
|
default:
|
|
127
140
|
return `${this.hostname}# `;
|
|
128
141
|
}
|
|
129
142
|
}
|
|
130
143
|
execute(line) {
|
|
131
|
-
|
|
144
|
+
if (line.trim().endsWith('?')) {
|
|
145
|
+
return this.getContextHelp(line);
|
|
146
|
+
}
|
|
147
|
+
const normalizedLine = this.normalizeCommandString(line);
|
|
148
|
+
const trimmed = normalizedLine.trim();
|
|
132
149
|
if (this.pendingCopyDest) {
|
|
133
150
|
const dest = trimmed || this.pendingCopyDest;
|
|
134
151
|
this.flashFiles.add(dest);
|
|
@@ -164,56 +181,57 @@ class ShellSimulator {
|
|
|
164
181
|
}
|
|
165
182
|
const args = commandToExecute.split(/\s+/);
|
|
166
183
|
const cmd = args[0].toLowerCase();
|
|
184
|
+
const isShow = cmd === 'show' || cmd === 'sh' || (cmd === 'do' && (args[1]?.toLowerCase() === 'show' || args[1]?.toLowerCase() === 'sh'));
|
|
167
185
|
if (cmd === '?' || cmd === 'help') {
|
|
168
186
|
if (cmd === 'help') {
|
|
169
|
-
return `Help may be requested at any point in a command by entering
|
|
170
|
-
a question mark '?'. If nothing matches, the help list will
|
|
187
|
+
return `Help may be requested at any point in a command by entering
|
|
188
|
+
a question mark '?'. If nothing matches, the help list will
|
|
171
189
|
show the available options.`;
|
|
172
190
|
}
|
|
173
191
|
if (this.mode === 'USER_EXEC') {
|
|
174
|
-
return `Exec commands:
|
|
175
|
-
disable Turn off privileged commands
|
|
176
|
-
enable Turn on privileged commands
|
|
177
|
-
exit Exit from the EXEC
|
|
178
|
-
ping Send echo messages
|
|
192
|
+
return `Exec commands:
|
|
193
|
+
disable Turn off privileged commands
|
|
194
|
+
enable Turn on privileged commands
|
|
195
|
+
exit Exit from the EXEC
|
|
196
|
+
ping Send echo messages
|
|
179
197
|
show Show running system information`;
|
|
180
198
|
}
|
|
181
199
|
else if (this.mode === 'PRIVILEGED_EXEC') {
|
|
182
|
-
return `Exec commands:
|
|
183
|
-
clear Reset functions
|
|
184
|
-
configure Enter configuration mode
|
|
185
|
-
copy Copy from one file to another
|
|
186
|
-
dir List files on a filesystem
|
|
187
|
-
disable Turn off privileged commands
|
|
188
|
-
enable Turn on privileged commands
|
|
189
|
-
exit Exit from the EXEC
|
|
190
|
-
ping Send echo messages
|
|
191
|
-
show Show running system information
|
|
200
|
+
return `Exec commands:
|
|
201
|
+
clear Reset functions
|
|
202
|
+
configure Enter configuration mode
|
|
203
|
+
copy Copy from one file to another
|
|
204
|
+
dir List files on a filesystem
|
|
205
|
+
disable Turn off privileged commands
|
|
206
|
+
enable Turn on privileged commands
|
|
207
|
+
exit Exit from the EXEC
|
|
208
|
+
ping Send echo messages
|
|
209
|
+
show Show running system information
|
|
192
210
|
write Write running configuration to memory or terminal`;
|
|
193
211
|
}
|
|
194
212
|
else if (this.mode === 'GLOBAL_CONFIG') {
|
|
195
|
-
return `Configure commands:
|
|
196
|
-
do To run EXEC commands in config mode
|
|
197
|
-
end Exit from configure mode
|
|
198
|
-
exit Exit from configure mode
|
|
199
|
-
hostname Set system's network name
|
|
200
|
-
interface Select an interface to configure
|
|
201
|
-
ip Global IP configuration subcommands
|
|
202
|
-
no Negate a command or set defaults
|
|
203
|
-
router Enable a routing process
|
|
213
|
+
return `Configure commands:
|
|
214
|
+
do To run EXEC commands in config mode
|
|
215
|
+
end Exit from configure mode
|
|
216
|
+
exit Exit from configure mode
|
|
217
|
+
hostname Set system's network name
|
|
218
|
+
interface Select an interface to configure
|
|
219
|
+
ip Global IP configuration subcommands
|
|
220
|
+
no Negate a command or set defaults
|
|
221
|
+
router Enable a routing process
|
|
204
222
|
vlan Vlan configuration commands`;
|
|
205
223
|
}
|
|
206
224
|
else if (this.mode === 'INTERFACE_CONFIG') {
|
|
207
|
-
return `Interface configuration commands:
|
|
208
|
-
description Detailed description of this interface
|
|
209
|
-
exit Exit from interface configuration mode
|
|
210
|
-
ip IP interface configuration subcommands
|
|
211
|
-
no Negate a command or set defaults
|
|
225
|
+
return `Interface configuration commands:
|
|
226
|
+
description Detailed description of this interface
|
|
227
|
+
exit Exit from interface configuration mode
|
|
228
|
+
ip IP interface configuration subcommands
|
|
229
|
+
no Negate a command or set defaults
|
|
212
230
|
shutdown Shutdown this interface`;
|
|
213
231
|
}
|
|
214
232
|
else {
|
|
215
|
-
return `Commands:
|
|
216
|
-
exit Exit current mode
|
|
233
|
+
return `Commands:
|
|
234
|
+
exit Exit current mode
|
|
217
235
|
end Exit to privileged EXEC mode`;
|
|
218
236
|
}
|
|
219
237
|
}
|
|
@@ -224,10 +242,15 @@ show the available options.`;
|
|
|
224
242
|
return `% Incomplete command.`;
|
|
225
243
|
}
|
|
226
244
|
if (cmd === 'exit') {
|
|
227
|
-
if (this.mode === 'INTERFACE_CONFIG' || this.mode === 'OSPF_CONFIG' || this.mode === 'RIP_CONFIG' || this.mode === 'BGP_CONFIG' || this.mode === 'EIGRP_CONFIG' || this.mode === 'DHCP_CONFIG' || this.mode === 'ACL_CONFIG' || this.mode === 'VLAN_CONFIG') {
|
|
245
|
+
if (this.mode === 'INTERFACE_CONFIG' || this.mode === 'OSPF_CONFIG' || this.mode === 'RIP_CONFIG' || this.mode === 'BGP_CONFIG' || this.mode === 'EIGRP_CONFIG' || this.mode === 'DHCP_CONFIG' || this.mode === 'ACL_CONFIG' || this.mode === 'VLAN_CONFIG' || this.mode === 'VPC_CONFIG' || this.mode === 'VRF_CONFIG') {
|
|
228
246
|
this.mode = 'GLOBAL_CONFIG';
|
|
229
247
|
this.activeInterface = null;
|
|
230
248
|
this.activeVlan = null;
|
|
249
|
+
this.activeVrf = null;
|
|
250
|
+
return '';
|
|
251
|
+
}
|
|
252
|
+
else if (this.mode === 'VRF_AF_CONFIG') {
|
|
253
|
+
this.mode = 'VRF_CONFIG';
|
|
231
254
|
return '';
|
|
232
255
|
}
|
|
233
256
|
else if (this.mode === 'GLOBAL_CONFIG') {
|
|
@@ -247,6 +270,7 @@ show the available options.`;
|
|
|
247
270
|
this.mode = 'PRIVILEGED_EXEC';
|
|
248
271
|
this.activeInterface = null;
|
|
249
272
|
this.activeVlan = null;
|
|
273
|
+
this.activeVrf = null;
|
|
250
274
|
return '';
|
|
251
275
|
}
|
|
252
276
|
}
|
|
@@ -290,7 +314,35 @@ show the available options.`;
|
|
|
290
314
|
this.shellEnabled = true;
|
|
291
315
|
return '';
|
|
292
316
|
}
|
|
293
|
-
|
|
317
|
+
const isGeneralCommand = isShow || cmd === 'ping' || cmd === 'write' || cmd === 'wr' || cmd === 'copy' || cmd === 'dir' || cmd === 'test';
|
|
318
|
+
if (this.mode === 'GLOBAL_CONFIG' && !isGeneralCommand) {
|
|
319
|
+
if (cmd === 'feature' && args[1]) {
|
|
320
|
+
const featureName = args.slice(1).join(' ').toLowerCase();
|
|
321
|
+
this.featuresEnabled.add(featureName);
|
|
322
|
+
return '';
|
|
323
|
+
}
|
|
324
|
+
if (cmd === 'no' && args[1] === 'feature' && args[2]) {
|
|
325
|
+
const featureName = args.slice(2).join(' ').toLowerCase();
|
|
326
|
+
this.featuresEnabled.delete(featureName);
|
|
327
|
+
return '';
|
|
328
|
+
}
|
|
329
|
+
if (cmd === 'vpc' && args[1] === 'domain' && args[2]) {
|
|
330
|
+
const domainId = parseInt(args[2], 10);
|
|
331
|
+
if (!isNaN(domainId)) {
|
|
332
|
+
this.vpcDomainId = domainId;
|
|
333
|
+
this.mode = 'VPC_CONFIG';
|
|
334
|
+
return '';
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (cmd === 'vrf' && args[1] === 'context' && args[2]) {
|
|
338
|
+
const vrfName = args[2];
|
|
339
|
+
this.activeVrf = vrfName;
|
|
340
|
+
if (!this.vrfs.has(vrfName)) {
|
|
341
|
+
this.vrfs.set(vrfName, { routeTargets: [] });
|
|
342
|
+
}
|
|
343
|
+
this.mode = 'VRF_CONFIG';
|
|
344
|
+
return '';
|
|
345
|
+
}
|
|
294
346
|
if (cmd === 'hostname' && args[1]) {
|
|
295
347
|
this.hostname = args[1];
|
|
296
348
|
return '';
|
|
@@ -457,7 +509,7 @@ show the available options.`;
|
|
|
457
509
|
}
|
|
458
510
|
return `% Invalid input detected at '^' marker.`;
|
|
459
511
|
}
|
|
460
|
-
if (this.mode === 'VLAN_CONFIG' && this.activeVlan !== null) {
|
|
512
|
+
if (this.mode === 'VLAN_CONFIG' && this.activeVlan !== null && !isGeneralCommand) {
|
|
461
513
|
if (cmd === 'name' && args[1]) {
|
|
462
514
|
this.vlanNames.set(this.activeVlan, args.slice(1).join(' '));
|
|
463
515
|
return '';
|
|
@@ -466,10 +518,40 @@ show the available options.`;
|
|
|
466
518
|
this.vlanNames.set(this.activeVlan, `VLAN${this.activeVlan.toString().padStart(4, '0')}`);
|
|
467
519
|
return '';
|
|
468
520
|
}
|
|
521
|
+
if (cmd === 'vn-segment' && args[1]) {
|
|
522
|
+
const vni = parseInt(args[1], 10);
|
|
523
|
+
if (!isNaN(vni)) {
|
|
524
|
+
this.vnSegments.set(this.activeVlan, vni);
|
|
525
|
+
return '';
|
|
526
|
+
}
|
|
527
|
+
}
|
|
469
528
|
return `% Invalid input detected at '^' marker.`;
|
|
470
529
|
}
|
|
471
|
-
if (this.mode === 'INTERFACE_CONFIG' && this.activeInterface) {
|
|
530
|
+
if (this.mode === 'INTERFACE_CONFIG' && this.activeInterface && !isGeneralCommand) {
|
|
472
531
|
const iface = this.interfaces.get(this.activeInterface);
|
|
532
|
+
if (cmd === 'vpc' && args[1]) {
|
|
533
|
+
const vpcId = parseInt(args[1], 10);
|
|
534
|
+
if (!isNaN(vpcId)) {
|
|
535
|
+
iface.vpcMemberId = vpcId;
|
|
536
|
+
return '';
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (cmd === 'source-interface' && args[1]) {
|
|
540
|
+
iface.sourceInterface = args[1];
|
|
541
|
+
return '';
|
|
542
|
+
}
|
|
543
|
+
if (cmd === 'member' && args[1] === 'vni' && args[2]) {
|
|
544
|
+
const vni = parseInt(args[2], 10);
|
|
545
|
+
if (!isNaN(vni)) {
|
|
546
|
+
if (!iface.memberVnis) {
|
|
547
|
+
iface.memberVnis = new Map();
|
|
548
|
+
}
|
|
549
|
+
const mcastGroup = args[3] === 'mcast-group' ? args[4] : undefined;
|
|
550
|
+
const associateVrf = args.includes('associate-vrf');
|
|
551
|
+
iface.memberVnis.set(vni, { mcastGroup, associateVrf });
|
|
552
|
+
return '';
|
|
553
|
+
}
|
|
554
|
+
}
|
|
473
555
|
if (cmd === 'shutdown') {
|
|
474
556
|
iface.adminShutdown = true;
|
|
475
557
|
iface.lineProtocolUp = false;
|
|
@@ -599,7 +681,14 @@ show the available options.`;
|
|
|
599
681
|
}
|
|
600
682
|
return `% Invalid input detected at '^' marker.`;
|
|
601
683
|
}
|
|
602
|
-
if (this.mode === 'OSPF_CONFIG') {
|
|
684
|
+
if (this.mode === 'OSPF_CONFIG' && !isGeneralCommand) {
|
|
685
|
+
if (cmd === 'network' && args[1] && args[2] && args[3] === 'area' && args[4]) {
|
|
686
|
+
const network = args[1];
|
|
687
|
+
const wildcard = args[2];
|
|
688
|
+
const area = args[4];
|
|
689
|
+
this.ospfNetworks.push({ network, wildcard, area });
|
|
690
|
+
return '';
|
|
691
|
+
}
|
|
603
692
|
if (cmd === 'network' || cmd === 'router-id') {
|
|
604
693
|
return '';
|
|
605
694
|
}
|
|
@@ -608,7 +697,7 @@ show the available options.`;
|
|
|
608
697
|
}
|
|
609
698
|
return `% Invalid input detected at '^' marker.`;
|
|
610
699
|
}
|
|
611
|
-
if (this.mode === 'RIP_CONFIG') {
|
|
700
|
+
if (this.mode === 'RIP_CONFIG' && !isGeneralCommand) {
|
|
612
701
|
if (cmd === 'version' && (args[1] === '1' || args[1] === '2')) {
|
|
613
702
|
this.ripVersion = parseInt(args[1], 10);
|
|
614
703
|
return '';
|
|
@@ -629,7 +718,7 @@ show the available options.`;
|
|
|
629
718
|
}
|
|
630
719
|
return `% Invalid input detected at '^' marker.`;
|
|
631
720
|
}
|
|
632
|
-
if (this.mode === 'BGP_CONFIG') {
|
|
721
|
+
if (this.mode === 'BGP_CONFIG' && !isGeneralCommand) {
|
|
633
722
|
if (cmd === 'neighbor' && args[1]) {
|
|
634
723
|
return '';
|
|
635
724
|
}
|
|
@@ -645,9 +734,49 @@ show the available options.`;
|
|
|
645
734
|
if (cmd === 'auto-summary') {
|
|
646
735
|
return '';
|
|
647
736
|
}
|
|
737
|
+
if (cmd === 'address-family' && args[1] === 'l2vpn' && args[2] === 'evpn') {
|
|
738
|
+
return '';
|
|
739
|
+
}
|
|
740
|
+
if (cmd === 'send-community') {
|
|
741
|
+
return '';
|
|
742
|
+
}
|
|
648
743
|
return `% Invalid input detected at '^' marker.`;
|
|
649
744
|
}
|
|
650
|
-
if (this.mode === '
|
|
745
|
+
if (this.mode === 'VPC_CONFIG' && !isGeneralCommand) {
|
|
746
|
+
if (cmd === 'peer-keepalive' && args[1] === 'destination') {
|
|
747
|
+
this.vpcPeerKeepalive = commandToExecute;
|
|
748
|
+
return '';
|
|
749
|
+
}
|
|
750
|
+
if (cmd === 'system-priority' || cmd === 'role' || cmd === 'peer-gateway' || cmd === 'peer-switch') {
|
|
751
|
+
return '';
|
|
752
|
+
}
|
|
753
|
+
return `% Invalid input detected at '^' marker.`;
|
|
754
|
+
}
|
|
755
|
+
if (this.mode === 'VRF_CONFIG' && this.activeVrf && !isGeneralCommand) {
|
|
756
|
+
const vrf = this.vrfs.get(this.activeVrf);
|
|
757
|
+
if (cmd === 'vni' && args[1]) {
|
|
758
|
+
vrf.vni = parseInt(args[1], 10);
|
|
759
|
+
return '';
|
|
760
|
+
}
|
|
761
|
+
if (cmd === 'rd' && args[1]) {
|
|
762
|
+
vrf.rd = args.slice(1).join(' ');
|
|
763
|
+
return '';
|
|
764
|
+
}
|
|
765
|
+
if (cmd === 'address-family' && args[1] === 'ipv4' && args[2] === 'unicast') {
|
|
766
|
+
this.mode = 'VRF_AF_CONFIG';
|
|
767
|
+
return '';
|
|
768
|
+
}
|
|
769
|
+
return `% Invalid input detected at '^' marker.`;
|
|
770
|
+
}
|
|
771
|
+
if (this.mode === 'VRF_AF_CONFIG' && this.activeVrf && !isGeneralCommand) {
|
|
772
|
+
const vrf = this.vrfs.get(this.activeVrf);
|
|
773
|
+
if (cmd === 'route-target' && args[1] === 'both' && args[2]) {
|
|
774
|
+
vrf.routeTargets.push(args.slice(1).join(' '));
|
|
775
|
+
return '';
|
|
776
|
+
}
|
|
777
|
+
return `% Invalid input detected at '^' marker.`;
|
|
778
|
+
}
|
|
779
|
+
if (this.mode === 'EIGRP_CONFIG' && !isGeneralCommand) {
|
|
651
780
|
if (cmd === 'network' && args[1]) {
|
|
652
781
|
return '';
|
|
653
782
|
}
|
|
@@ -662,36 +791,85 @@ show the available options.`;
|
|
|
662
791
|
}
|
|
663
792
|
return `% Invalid input detected at '^' marker.`;
|
|
664
793
|
}
|
|
665
|
-
if (this.mode === 'DHCP_CONFIG') {
|
|
794
|
+
if (this.mode === 'DHCP_CONFIG' && !isGeneralCommand) {
|
|
666
795
|
if (cmd === 'network' || cmd === 'default-router' || cmd === 'dns-server') {
|
|
667
796
|
return '';
|
|
668
797
|
}
|
|
669
798
|
return `% Invalid input detected at '^' marker.`;
|
|
670
799
|
}
|
|
671
|
-
if (this.mode === 'ACL_CONFIG') {
|
|
800
|
+
if (this.mode === 'ACL_CONFIG' && !isGeneralCommand) {
|
|
672
801
|
if (cmd === 'permit' || cmd === 'deny') {
|
|
673
802
|
return '';
|
|
674
803
|
}
|
|
675
804
|
return `% Invalid input detected at '^' marker.`;
|
|
676
805
|
}
|
|
677
|
-
const isShow = cmd === 'show' || cmd === 'sh' || (cmd === 'do' && (args[1]?.toLowerCase() === 'show' || args[1]?.toLowerCase() === 'sh'));
|
|
678
806
|
if (isShow) {
|
|
679
807
|
const showArgs = cmd === 'do' ? args.slice(2) : args.slice(1);
|
|
680
808
|
const showCmd = showArgs[0]?.toLowerCase();
|
|
809
|
+
if (showCmd === 'vpc') {
|
|
810
|
+
const domainId = this.vpcDomainId || 10;
|
|
811
|
+
const peerStatus = this.vpcPeerKeepalive ? 'peer adjacency formed ok' : 'peer link not configured';
|
|
812
|
+
const keepaliveStatus = this.vpcPeerKeepalive ? 'peer is alive' : 'peer keep-alive not configured';
|
|
813
|
+
return `Legend:
|
|
814
|
+
(*) - local vPC is down, dynamic backup loop preventer
|
|
815
|
+
|
|
816
|
+
vPC domain id : ${domainId}
|
|
817
|
+
Peer status : ${peerStatus}
|
|
818
|
+
vPC keep-alive status : ${keepaliveStatus}
|
|
819
|
+
Configuration-consistency status : success
|
|
820
|
+
Per-vlan consistency status : success
|
|
821
|
+
Type-2 consistency status : success
|
|
822
|
+
vPC role : primary
|
|
823
|
+
Number of vPCs configured : ${Array.from(this.interfaces.values()).filter(i => i.vpcMemberId !== undefined).length}
|
|
824
|
+
Peer Gateway : Enabled
|
|
825
|
+
`;
|
|
826
|
+
}
|
|
827
|
+
if (showCmd === 'nve' && showArgs[1] === 'interface') {
|
|
828
|
+
const nveIface = Array.from(this.interfaces.values()).find(i => i.name.toLowerCase().startsWith('nve'));
|
|
829
|
+
if (!nveIface) {
|
|
830
|
+
return '% NVE interface is not configured';
|
|
831
|
+
}
|
|
832
|
+
const sourceInt = nveIface.sourceInterface || 'Loopback0';
|
|
833
|
+
return `Interface: ${nveIface.name}, State: Up, Encapsulation: VXLAN
|
|
834
|
+
Source-Interface: ${sourceInt} (10.0.0.1)
|
|
835
|
+
`;
|
|
836
|
+
}
|
|
837
|
+
if (showCmd === 'nve' && showArgs[1] === 'vni') {
|
|
838
|
+
const nveIface = Array.from(this.interfaces.values()).find(i => i.name.toLowerCase().startsWith('nve'));
|
|
839
|
+
if (!nveIface || !nveIface.memberVnis) {
|
|
840
|
+
return `Interface VNI Multicast-group State Mode vPC Dev\n` +
|
|
841
|
+
`--------- -------- ----------------- ----- ---- -------\n`;
|
|
842
|
+
}
|
|
843
|
+
let out = `Interface VNI Multicast-group State Mode vPC Dev\n` +
|
|
844
|
+
`--------- -------- ----------------- ----- ---- -------\n`;
|
|
845
|
+
for (const [vni, details] of nveIface.memberVnis.entries()) {
|
|
846
|
+
const mcast = details.mcastGroup || 'n/a';
|
|
847
|
+
out += `${nveIface.name.padEnd(9)} ${vni.toString().padEnd(8)} ${mcast.padEnd(17)} Up CP n/a\n`;
|
|
848
|
+
}
|
|
849
|
+
return out;
|
|
850
|
+
}
|
|
851
|
+
if (showCmd === 'bgp' && showArgs[1] === 'l2vpn' && showArgs[2] === 'evpn' && showArgs[3] === 'summary') {
|
|
852
|
+
const bgpAs = this.bgpAsn || '65000';
|
|
853
|
+
return `BGP summary information for VRF default, address family L2VPN EVPN
|
|
854
|
+
BGP router identifier 10.0.0.1, local AS number ${bgpAs}
|
|
855
|
+
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
|
|
856
|
+
10.0.0.2 4 ${bgpAs} 120 125 47 0 0 01:24:55 2
|
|
857
|
+
`;
|
|
858
|
+
}
|
|
681
859
|
if (showCmd === 'version' || showCmd === 'ver') {
|
|
682
|
-
return `Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 15.0(2)SE4, RELEASE SOFTWARE (fc1)
|
|
683
|
-
Technical Support: http://www.cisco.com/techsupport
|
|
684
|
-
Copyright (c) 1986-2013 by Cisco Systems, Inc.
|
|
685
|
-
Compiled Wed 26-Jun-13 02:49 by prod_rel_team
|
|
686
|
-
|
|
687
|
-
ROM: Bootstrap program is 12.2(44)SE Version
|
|
688
|
-
BOOTLDR: C2960 Boot Loader (C2960-HBOOT-M) Version 12.2(44)SE, RELEASE SOFTWARE (fc1)
|
|
689
|
-
|
|
690
|
-
Switch1 uptime is 2 hours, 15 minutes
|
|
691
|
-
System returned to ROM by power-on
|
|
692
|
-
System image file is "flash:/c2960-lanbasek9-mz.150-2.SE4.bin"
|
|
693
|
-
|
|
694
|
-
This product contains cryptographic features and is subject to Y...
|
|
860
|
+
return `Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 15.0(2)SE4, RELEASE SOFTWARE (fc1)
|
|
861
|
+
Technical Support: http://www.cisco.com/techsupport
|
|
862
|
+
Copyright (c) 1986-2013 by Cisco Systems, Inc.
|
|
863
|
+
Compiled Wed 26-Jun-13 02:49 by prod_rel_team
|
|
864
|
+
|
|
865
|
+
ROM: Bootstrap program is 12.2(44)SE Version
|
|
866
|
+
BOOTLDR: C2960 Boot Loader (C2960-HBOOT-M) Version 12.2(44)SE, RELEASE SOFTWARE (fc1)
|
|
867
|
+
|
|
868
|
+
Switch1 uptime is 2 hours, 15 minutes
|
|
869
|
+
System returned to ROM by power-on
|
|
870
|
+
System image file is "flash:/c2960-lanbasek9-mz.150-2.SE4.bin"
|
|
871
|
+
|
|
872
|
+
This product contains cryptographic features and is subject to Y...
|
|
695
873
|
`;
|
|
696
874
|
}
|
|
697
875
|
if (showCmd === 'ip' && showArgs[1]?.startsWith('int') && showArgs[2]?.startsWith('br')) {
|
|
@@ -712,6 +890,15 @@ This product contains cryptographic features and is subject to Y...
|
|
|
712
890
|
if (status.description) {
|
|
713
891
|
out += ` description ${status.description}\n`;
|
|
714
892
|
}
|
|
893
|
+
if (status.isSwitchport) {
|
|
894
|
+
out += ` switchport\n`;
|
|
895
|
+
if (status.switchportMode) {
|
|
896
|
+
out += ` switchport mode ${status.switchportMode}\n`;
|
|
897
|
+
}
|
|
898
|
+
if (status.vlan) {
|
|
899
|
+
out += ` switchport access vlan ${status.vlan}\n`;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
715
902
|
if (status.ip) {
|
|
716
903
|
out += ` ip address ${status.ip} ${status.subnet}\n`;
|
|
717
904
|
}
|
|
@@ -725,6 +912,12 @@ This product contains cryptographic features and is subject to Y...
|
|
|
725
912
|
out += `ip route ${r.network} ${r.mask} ${r.nextHop || r.outgoingInterface}\n`;
|
|
726
913
|
}
|
|
727
914
|
}
|
|
915
|
+
if (this.ospfEnabled) {
|
|
916
|
+
out += `router ospf ${this.ospfProcessId || '10'}\n`;
|
|
917
|
+
for (const net of this.ospfNetworks) {
|
|
918
|
+
out += ` network ${net.network} ${net.wildcard} area ${net.area}\n`;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
728
921
|
out += `!\nend\n`;
|
|
729
922
|
return out;
|
|
730
923
|
}
|
|
@@ -736,9 +929,16 @@ This product contains cryptographic features and is subject to Y...
|
|
|
736
929
|
` Incoming update filter list for all interfaces is not set\n` +
|
|
737
930
|
` Router ID 192.168.1.254\n` +
|
|
738
931
|
` Number of areas in this router is 1. 1 normal 0 stub 0 nssa\n` +
|
|
739
|
-
` Routing for Networks:\n
|
|
740
|
-
|
|
741
|
-
|
|
932
|
+
` Routing for Networks:\n`;
|
|
933
|
+
if (this.ospfNetworks.length > 0) {
|
|
934
|
+
for (const net of this.ospfNetworks) {
|
|
935
|
+
out += ` ${net.network}/${this.getPrefixLength(this.wildcardToSubnet(net.wildcard))} area ${net.area}\n`;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
out += ` 192.168.1.0/24 area 0\n`;
|
|
940
|
+
}
|
|
941
|
+
out += ` Routing Information Sources:\n` +
|
|
742
942
|
` Gateway Distance Last Update\n` +
|
|
743
943
|
` Distance: (default is 110)\n\n`;
|
|
744
944
|
}
|
|
@@ -799,6 +999,12 @@ This product contains cryptographic features and is subject to Y...
|
|
|
799
999
|
let out = `Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP\n\n`;
|
|
800
1000
|
out += `Gateway of last resort is not set\n\n`;
|
|
801
1001
|
for (const r of this.routes) {
|
|
1002
|
+
if (r.connected && r.outgoingInterface) {
|
|
1003
|
+
const iface = this.interfaces.get(r.outgoingInterface);
|
|
1004
|
+
if (iface && iface.adminShutdown) {
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
802
1008
|
const code = r.connected ? 'C' : 'S';
|
|
803
1009
|
const target = r.nextHop ? `via ${r.nextHop}` : `directly connected, ${r.outgoingInterface || 'Null0'}`;
|
|
804
1010
|
out += `${code} ${r.network}/${this.getPrefixLength(r.mask)} is ${target}\n`;
|
|
@@ -842,11 +1048,11 @@ This product contains cryptographic features and is subject to Y...
|
|
|
842
1048
|
return out;
|
|
843
1049
|
}
|
|
844
1050
|
if (showCmd === 'cdp' && showArgs[1]?.startsWith('ne')) {
|
|
845
|
-
return `Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge
|
|
846
|
-
S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone
|
|
847
|
-
|
|
848
|
-
Device ID Local Intrfce Holdtme Capability Platform Port ID
|
|
849
|
-
Switch2 Gig 0/1 125 S I WS-C2960- Gig 0/1
|
|
1051
|
+
return `Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge
|
|
1052
|
+
S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone
|
|
1053
|
+
|
|
1054
|
+
Device ID Local Intrfce Holdtme Capability Platform Port ID
|
|
1055
|
+
Switch2 Gig 0/1 125 S I WS-C2960- Gig 0/1
|
|
850
1056
|
`;
|
|
851
1057
|
}
|
|
852
1058
|
if (showCmd === 'vtp' && showArgs[1] === 'status') {
|
|
@@ -912,9 +1118,9 @@ Switch2 Gig 0/1 125 S I WS-C2960- Gig 0/1
|
|
|
912
1118
|
return out;
|
|
913
1119
|
}
|
|
914
1120
|
if (showCmd === 'lldp' && showArgs[1]?.startsWith('ne')) {
|
|
915
|
-
return `Device ID Local Intf Hold-time Capability Port ID
|
|
916
|
-
Switch2 Gi0/1 120 S Gi0/1
|
|
917
|
-
Total entries displayed: 1
|
|
1121
|
+
return `Device ID Local Intf Hold-time Capability Port ID
|
|
1122
|
+
Switch2 Gi0/1 120 S Gi0/1
|
|
1123
|
+
Total entries displayed: 1
|
|
918
1124
|
`;
|
|
919
1125
|
}
|
|
920
1126
|
return `% Unrecognized show command: show ${showArgs.join(' ')}`;
|
|
@@ -955,9 +1161,9 @@ Total entries displayed: 1
|
|
|
955
1161
|
}
|
|
956
1162
|
if (cmd === 'ping' && args[1]) {
|
|
957
1163
|
const ip = args[1];
|
|
958
|
-
return `Sending 5, 100-byte ICMP Echos to ${ip}, timeout is 2 seconds:
|
|
959
|
-
!!!!!
|
|
960
|
-
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/4 ms
|
|
1164
|
+
return `Sending 5, 100-byte ICMP Echos to ${ip}, timeout is 2 seconds:
|
|
1165
|
+
!!!!!
|
|
1166
|
+
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/4 ms
|
|
961
1167
|
`;
|
|
962
1168
|
}
|
|
963
1169
|
if (cmd === 'test' && args[1] === 'trigger-syslog') {
|
|
@@ -967,6 +1173,176 @@ Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/4 ms
|
|
|
967
1173
|
}
|
|
968
1174
|
return `% Unrecognized command: ${trimmed}`;
|
|
969
1175
|
}
|
|
1176
|
+
normalizeCommandString(line) {
|
|
1177
|
+
const trimmed = line.trim();
|
|
1178
|
+
if (!trimmed)
|
|
1179
|
+
return '';
|
|
1180
|
+
// If it starts with 'do ', we normalize the rest recursively
|
|
1181
|
+
if (trimmed.toLowerCase().startsWith('do ')) {
|
|
1182
|
+
const rest = trimmed.substring(3);
|
|
1183
|
+
return 'do ' + this.normalizeCommandString(rest);
|
|
1184
|
+
}
|
|
1185
|
+
const parts = trimmed.split(/\s+/);
|
|
1186
|
+
if (parts.length === 0 || !parts[0])
|
|
1187
|
+
return trimmed;
|
|
1188
|
+
let cmd = parts[0].toLowerCase();
|
|
1189
|
+
// 1. First token abbreviation expansion
|
|
1190
|
+
if (cmd === 'conf' || cmd === 'config' || cmd === 'configure') {
|
|
1191
|
+
parts[0] = 'configure';
|
|
1192
|
+
if (parts[1] && (parts[1].toLowerCase() === 't' || parts[1].toLowerCase() === 'term' || parts[1].toLowerCase() === 'terminal')) {
|
|
1193
|
+
parts[1] = 'terminal';
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
else if (cmd === 'int' || cmd === 'inter' || cmd === 'interface') {
|
|
1197
|
+
parts[0] = 'interface';
|
|
1198
|
+
if (parts[1]) {
|
|
1199
|
+
parts[1] = this.normalizeInterfaceName(parts[1]);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
else if (cmd === 'sh' || cmd === 'sho' || cmd === 'show') {
|
|
1203
|
+
parts[0] = 'show';
|
|
1204
|
+
}
|
|
1205
|
+
else if (cmd === 'en' || cmd === 'ena' || cmd === 'enab' || cmd === 'enable') {
|
|
1206
|
+
parts[0] = 'enable';
|
|
1207
|
+
}
|
|
1208
|
+
else if (cmd === 'di' || cmd === 'dis' || cmd === 'disa' || cmd === 'disable') {
|
|
1209
|
+
parts[0] = 'disable';
|
|
1210
|
+
}
|
|
1211
|
+
else if (cmd === 'ex' || cmd === 'exi' || cmd === 'exit') {
|
|
1212
|
+
parts[0] = 'exit';
|
|
1213
|
+
}
|
|
1214
|
+
else if (cmd === 'end') {
|
|
1215
|
+
parts[0] = 'end';
|
|
1216
|
+
}
|
|
1217
|
+
else if (cmd === 'wr' || cmd === 'wri' || cmd === 'write') {
|
|
1218
|
+
parts[0] = 'write';
|
|
1219
|
+
}
|
|
1220
|
+
else if (cmd === 'p' || cmd === 'pi' || cmd === 'pin' || cmd === 'ping') {
|
|
1221
|
+
parts[0] = 'ping';
|
|
1222
|
+
}
|
|
1223
|
+
else if (cmd === 'net' || cmd === 'network') {
|
|
1224
|
+
parts[0] = 'network';
|
|
1225
|
+
}
|
|
1226
|
+
else if (cmd === 'sw' || cmd === 'swit' || cmd === 'switch' || cmd === 'switchport') {
|
|
1227
|
+
parts[0] = 'switchport';
|
|
1228
|
+
}
|
|
1229
|
+
else if (cmd === 'no') {
|
|
1230
|
+
// Recurse for the remainder
|
|
1231
|
+
if (parts.length > 1) {
|
|
1232
|
+
const normalizedSub = this.normalizeCommandString(parts.slice(1).join(' '));
|
|
1233
|
+
return 'no ' + normalizedSub;
|
|
1234
|
+
}
|
|
1235
|
+
return trimmed;
|
|
1236
|
+
}
|
|
1237
|
+
// 2. Sub-command expansion
|
|
1238
|
+
cmd = parts[0].toLowerCase();
|
|
1239
|
+
if (cmd === 'show') {
|
|
1240
|
+
if (parts[1]) {
|
|
1241
|
+
const sub = parts[1].toLowerCase();
|
|
1242
|
+
if (sub === 'ru' || sub === 'run' || sub === 'runn' || sub === 'running' || sub === 'running-config') {
|
|
1243
|
+
parts[1] = 'running-config';
|
|
1244
|
+
}
|
|
1245
|
+
else if (sub === 'vl' || sub === 'vla' || sub === 'vlan') {
|
|
1246
|
+
parts[1] = 'vlan';
|
|
1247
|
+
}
|
|
1248
|
+
else if (sub === 'ip') {
|
|
1249
|
+
if (parts[2]) {
|
|
1250
|
+
const sub2 = parts[2].toLowerCase();
|
|
1251
|
+
if (sub2 === 'ro' || sub2 === 'rou' || sub2 === 'rout' || sub2 === 'route') {
|
|
1252
|
+
parts[2] = 'route';
|
|
1253
|
+
}
|
|
1254
|
+
else if (sub2 === 'int' || sub2 === 'inter' || sub2 === 'interface') {
|
|
1255
|
+
parts[2] = 'interface';
|
|
1256
|
+
if (parts[3] && (parts[3].toLowerCase() === 'br' || parts[3].toLowerCase() === 'brief')) {
|
|
1257
|
+
parts[3] = 'brief';
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
else if (sub2 === 'ospf') {
|
|
1261
|
+
if (parts[3]) {
|
|
1262
|
+
const sub3 = parts[3].toLowerCase();
|
|
1263
|
+
if (sub3 === 'ne' || sub3 === 'nei' || sub3 === 'neig' || sub3 === 'neigh' || sub3 === 'neighbor') {
|
|
1264
|
+
parts[3] = 'neighbor';
|
|
1265
|
+
}
|
|
1266
|
+
else if (sub3 === 'in' || sub3 === 'int' || sub3 === 'interface') {
|
|
1267
|
+
parts[3] = 'interface';
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
else if (cmd === 'ip') {
|
|
1276
|
+
if (parts[1]) {
|
|
1277
|
+
const sub = parts[1].toLowerCase();
|
|
1278
|
+
if (sub === 'add' || sub === 'addr' || sub === 'address') {
|
|
1279
|
+
parts[1] = 'address';
|
|
1280
|
+
}
|
|
1281
|
+
else if (sub === 'ro' || sub === 'rou' || sub === 'rout' || sub === 'route') {
|
|
1282
|
+
parts[1] = 'route';
|
|
1283
|
+
}
|
|
1284
|
+
else if (sub === 'routing') {
|
|
1285
|
+
parts[1] = 'routing';
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
else if (cmd === 'router') {
|
|
1290
|
+
if (parts[1]) {
|
|
1291
|
+
const sub = parts[1].toLowerCase();
|
|
1292
|
+
if (sub === 'os' || sub === 'osp' || sub === 'ospf') {
|
|
1293
|
+
parts[1] = 'ospf';
|
|
1294
|
+
}
|
|
1295
|
+
else if (sub === 'ri' || sub === 'rip') {
|
|
1296
|
+
parts[1] = 'rip';
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
else if (cmd === 'shutdown' || cmd === 'shut') {
|
|
1301
|
+
parts[0] = 'shutdown';
|
|
1302
|
+
}
|
|
1303
|
+
else if (cmd === 'description' || cmd === 'desc') {
|
|
1304
|
+
parts[0] = 'description';
|
|
1305
|
+
}
|
|
1306
|
+
else if (cmd === 'switchport') {
|
|
1307
|
+
if (parts[1]) {
|
|
1308
|
+
const sub1 = parts[1].toLowerCase();
|
|
1309
|
+
if (sub1 === 'mo' || sub1 === 'mod' || sub1 === 'mode') {
|
|
1310
|
+
parts[1] = 'mode';
|
|
1311
|
+
if (parts[2]) {
|
|
1312
|
+
const sub2 = parts[2].toLowerCase();
|
|
1313
|
+
if (sub2 === 'ac' || sub2 === 'acc' || sub2 === 'access') {
|
|
1314
|
+
parts[2] = 'access';
|
|
1315
|
+
}
|
|
1316
|
+
else if (sub2 === 'tr' || sub2 === 'tru' || sub2 === 'trun' || sub2 === 'trunk') {
|
|
1317
|
+
parts[2] = 'trunk';
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
else if (sub1 === 'ac' || sub1 === 'acc' || sub1 === 'access') {
|
|
1322
|
+
parts[1] = 'access';
|
|
1323
|
+
if (parts[2]) {
|
|
1324
|
+
const sub2 = parts[2].toLowerCase();
|
|
1325
|
+
if (sub2 === 'vl' || sub2 === 'vla' || sub2 === 'vlan') {
|
|
1326
|
+
parts[2] = 'vlan';
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
else if (sub1 === 'tr' || sub1 === 'tru' || sub1 === 'trun' || sub1 === 'trunk') {
|
|
1331
|
+
parts[1] = 'trunk';
|
|
1332
|
+
if (parts[2]) {
|
|
1333
|
+
const sub2 = parts[2].toLowerCase();
|
|
1334
|
+
if (sub2 === 'al' || sub2 === 'all' || sub2 === 'allow' || sub2 === 'allowed') {
|
|
1335
|
+
parts[2] = 'allowed';
|
|
1336
|
+
if (parts[3] && (parts[3].toLowerCase() === 'vl' || parts[3].toLowerCase() === 'vla' || parts[3].toLowerCase() === 'vlan')) {
|
|
1337
|
+
parts[3] = 'vlan';
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return parts.join(' ');
|
|
1345
|
+
}
|
|
970
1346
|
normalizeInterfaceName(name) {
|
|
971
1347
|
const lower = name.toLowerCase();
|
|
972
1348
|
if (lower.startsWith('gigabitethernet')) {
|
|
@@ -1005,6 +1381,21 @@ Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/4 ms
|
|
|
1005
1381
|
if (lower.startsWith('vl')) {
|
|
1006
1382
|
return 'Vlan' + name.substring(2);
|
|
1007
1383
|
}
|
|
1384
|
+
if (lower.startsWith('ethernet')) {
|
|
1385
|
+
return 'Ethernet' + name.substring(8);
|
|
1386
|
+
}
|
|
1387
|
+
if (lower.startsWith('eth')) {
|
|
1388
|
+
return 'Ethernet' + name.substring(3);
|
|
1389
|
+
}
|
|
1390
|
+
if (lower.startsWith('port-channel')) {
|
|
1391
|
+
return 'Port-channel' + name.substring(12);
|
|
1392
|
+
}
|
|
1393
|
+
if (lower.startsWith('po')) {
|
|
1394
|
+
return 'Port-channel' + name.substring(2);
|
|
1395
|
+
}
|
|
1396
|
+
if (lower.startsWith('nve')) {
|
|
1397
|
+
return 'Nve' + name.substring(3);
|
|
1398
|
+
}
|
|
1008
1399
|
return name;
|
|
1009
1400
|
}
|
|
1010
1401
|
shortenInterfaceName(name) {
|
|
@@ -1012,7 +1403,10 @@ Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/4 ms
|
|
|
1012
1403
|
.replace('GigabitEthernet', 'Gi')
|
|
1013
1404
|
.replace('FastEthernet', 'Fa')
|
|
1014
1405
|
.replace('TenGigabitEthernet', 'Te')
|
|
1015
|
-
.replace('Loopback', 'Lo')
|
|
1406
|
+
.replace('Loopback', 'Lo')
|
|
1407
|
+
.replace('Ethernet', 'Eth')
|
|
1408
|
+
.replace('Port-channel', 'Po')
|
|
1409
|
+
.replace('Nve', 'Nve');
|
|
1016
1410
|
}
|
|
1017
1411
|
getPrefixLength(mask) {
|
|
1018
1412
|
const parts = mask.split('.').map(Number);
|
|
@@ -1033,6 +1427,138 @@ Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/4 ms
|
|
|
1033
1427
|
const netParts = ipParts.map((p, i) => p & maskParts[i]);
|
|
1034
1428
|
return netParts.join('.');
|
|
1035
1429
|
}
|
|
1430
|
+
wildcardToSubnet(wildcard) {
|
|
1431
|
+
return wildcard.split('.').map(p => (255 - parseInt(p, 10)).toString()).join('.');
|
|
1432
|
+
}
|
|
1433
|
+
getContextHelp(line) {
|
|
1434
|
+
const trimmed = line.trim();
|
|
1435
|
+
const endsWithSpaceHelp = line.endsWith(' ?') || line.endsWith(' ?');
|
|
1436
|
+
const searchPrefix = trimmed.substring(0, trimmed.length - 1).trim();
|
|
1437
|
+
// Normalize the search prefix to know what mode/command we are matching
|
|
1438
|
+
const normalizedPrefix = this.normalizeCommandString(searchPrefix);
|
|
1439
|
+
const parts = normalizedPrefix.split(/\s+/).filter(Boolean);
|
|
1440
|
+
// 1. Help for empty prompt (just '?')
|
|
1441
|
+
if (parts.length === 0) {
|
|
1442
|
+
return this.getModeCommandsHelp();
|
|
1443
|
+
}
|
|
1444
|
+
// 2. Help for a word prefix (e.g. "sh?" or "sw?")
|
|
1445
|
+
if (!endsWithSpaceHelp) {
|
|
1446
|
+
const lastWord = parts[parts.length - 1].toLowerCase();
|
|
1447
|
+
const precedingPrefix = parts.slice(0, -1).join(' ');
|
|
1448
|
+
return this.getWordCompletionHelp(precedingPrefix, lastWord);
|
|
1449
|
+
}
|
|
1450
|
+
// 3. Help for sub-commands (e.g. "show ?", "ip ?")
|
|
1451
|
+
const cmd = parts[0].toLowerCase();
|
|
1452
|
+
if (cmd === 'show') {
|
|
1453
|
+
if (parts.length === 1) {
|
|
1454
|
+
return ` running-config Current operating configuration
|
|
1455
|
+
ip IP information
|
|
1456
|
+
vlan VLAN status
|
|
1457
|
+
version System hardware and software status
|
|
1458
|
+
cdp Discovery Protocol neighbors`;
|
|
1459
|
+
}
|
|
1460
|
+
if (parts[1]?.toLowerCase() === 'ip') {
|
|
1461
|
+
if (parts.length === 2) {
|
|
1462
|
+
return ` interface IP interface status and configuration
|
|
1463
|
+
route IP routing table
|
|
1464
|
+
protocols Active IP routing protocols`;
|
|
1465
|
+
}
|
|
1466
|
+
if (parts[2]?.toLowerCase() === 'interface') {
|
|
1467
|
+
return ` brief Brief summary of IP status and configuration`;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
else if (cmd === 'ip') {
|
|
1472
|
+
if (parts.length === 1) {
|
|
1473
|
+
return ` address Configure interface IP address
|
|
1474
|
+
route Configure static IP route
|
|
1475
|
+
routing Enable IP routing`;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
else if (cmd === 'interface') {
|
|
1479
|
+
if (parts.length === 1) {
|
|
1480
|
+
return ` GigabitEthernet GigabitEthernet IEEE 802.3z
|
|
1481
|
+
FastEthernet FastEthernet IEEE 802.3
|
|
1482
|
+
Loopback Loopback interface
|
|
1483
|
+
Vlan Vlan interface`;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
else if (cmd === 'switchport') {
|
|
1487
|
+
if (parts.length === 1) {
|
|
1488
|
+
return ` mode Set trunking mode of the interface
|
|
1489
|
+
access Set access mode characteristics`;
|
|
1490
|
+
}
|
|
1491
|
+
if (parts[1]?.toLowerCase() === 'mode') {
|
|
1492
|
+
return ` access Set port to Access mode
|
|
1493
|
+
trunk Set port to Trunking mode`;
|
|
1494
|
+
}
|
|
1495
|
+
if (parts[1]?.toLowerCase() === 'access') {
|
|
1496
|
+
return ` vlan Set access VLAN`;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
else if (cmd === 'no') {
|
|
1500
|
+
if (parts.length === 1) {
|
|
1501
|
+
return ` shutdown Enable the interface
|
|
1502
|
+
ip Remove IP configuration
|
|
1503
|
+
description Remove interface description`;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return `% No help available for command: "${normalizedPrefix} ?"`;
|
|
1507
|
+
}
|
|
1508
|
+
getModeCommandsHelp() {
|
|
1509
|
+
switch (this.mode) {
|
|
1510
|
+
case 'USER_EXEC':
|
|
1511
|
+
return ` enable Turn on privileged commands
|
|
1512
|
+
show Show running system information
|
|
1513
|
+
ping Send echo messages`;
|
|
1514
|
+
case 'PRIVILEGED_EXEC':
|
|
1515
|
+
return ` configure Enter configuration mode
|
|
1516
|
+
show Show running system information
|
|
1517
|
+
write Write running configuration to memory
|
|
1518
|
+
copy Copy configuration files
|
|
1519
|
+
ping Send echo messages
|
|
1520
|
+
disable Turn off privileged commands`;
|
|
1521
|
+
case 'GLOBAL_CONFIG':
|
|
1522
|
+
return ` hostname Set system network name
|
|
1523
|
+
interface Configure an interface
|
|
1524
|
+
ip Global IP configuration commands
|
|
1525
|
+
router Enable routing protocol
|
|
1526
|
+
vlan Configure VLAN parameters
|
|
1527
|
+
end Exit configuration mode
|
|
1528
|
+
exit Exit configuration mode`;
|
|
1529
|
+
case 'INTERFACE_CONFIG':
|
|
1530
|
+
return ` ip Interface IP configuration commands
|
|
1531
|
+
shutdown Shutdown the selected interface
|
|
1532
|
+
description Set interface description
|
|
1533
|
+
switchport Set interface switchport parameters
|
|
1534
|
+
exit Exit interface configuration mode`;
|
|
1535
|
+
default:
|
|
1536
|
+
return ` exit Exit current configuration mode
|
|
1537
|
+
end Exit to privileged exec mode`;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
getWordCompletionHelp(precedingPrefix, lastWord) {
|
|
1541
|
+
const allCommands = this.getModeCommandsList();
|
|
1542
|
+
const matches = allCommands.filter(c => c.startsWith(lastWord));
|
|
1543
|
+
if (matches.length > 0) {
|
|
1544
|
+
return matches.map(m => ` ${m}`).join('\n');
|
|
1545
|
+
}
|
|
1546
|
+
return `% No command matches prefix: "${lastWord}"`;
|
|
1547
|
+
}
|
|
1548
|
+
getModeCommandsList() {
|
|
1549
|
+
switch (this.mode) {
|
|
1550
|
+
case 'USER_EXEC':
|
|
1551
|
+
return ['enable', 'show', 'ping', 'exit'];
|
|
1552
|
+
case 'PRIVILEGED_EXEC':
|
|
1553
|
+
return ['configure', 'show', 'write', 'copy', 'ping', 'disable', 'exit'];
|
|
1554
|
+
case 'GLOBAL_CONFIG':
|
|
1555
|
+
return ['hostname', 'interface', 'ip', 'router', 'vlan', 'end', 'exit'];
|
|
1556
|
+
case 'INTERFACE_CONFIG':
|
|
1557
|
+
return ['ip', 'shutdown', 'description', 'switchport', 'exit', 'end'];
|
|
1558
|
+
default:
|
|
1559
|
+
return ['exit', 'end'];
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1036
1562
|
}
|
|
1037
1563
|
exports.ShellSimulator = ShellSimulator;
|
|
1038
1564
|
//# sourceMappingURL=shell-simulator.js.map
|