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.
Files changed (47) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +242 -242
  3. package/dist/cli/commands/dashboardCommand.d.ts +1 -0
  4. package/dist/cli/commands/dashboardCommand.js +16 -0
  5. package/dist/cli/commands/dashboardCommand.js.map +1 -0
  6. package/dist/cli/commands/monitorCommand.d.ts +4 -0
  7. package/dist/cli/commands/monitorCommand.js +132 -0
  8. package/dist/cli/commands/monitorCommand.js.map +1 -0
  9. package/dist/cli/commands/runCommand.d.ts +6 -0
  10. package/dist/cli/commands/runCommand.js +635 -0
  11. package/dist/cli/commands/runCommand.js.map +1 -0
  12. package/dist/cli/commands/serverCommand.d.ts +1 -0
  13. package/dist/cli/commands/serverCommand.js +11 -0
  14. package/dist/cli/commands/serverCommand.js.map +1 -0
  15. package/dist/cli/commands/shellCommand.d.ts +1 -0
  16. package/dist/cli/commands/shellCommand.js +44 -0
  17. package/dist/cli/commands/shellCommand.js.map +1 -0
  18. package/dist/core/agent/AgentLoop.d.ts +0 -4
  19. package/dist/core/agent/AgentLoop.js +1 -158
  20. package/dist/core/agent/AgentLoop.js.map +1 -1
  21. package/dist/core/agent/AutoHealer.d.ts +12 -0
  22. package/dist/core/agent/AutoHealer.js +129 -26
  23. package/dist/core/agent/AutoHealer.js.map +1 -1
  24. package/dist/core/agent/HierarchicalAgentManager.d.ts +1 -1
  25. package/dist/core/agent/HierarchicalAgentManager.js +21 -5
  26. package/dist/core/agent/HierarchicalAgentManager.js.map +1 -1
  27. package/dist/core/agent/PromptEngine.js +33 -68
  28. package/dist/core/agent/PromptEngine.js.map +1 -1
  29. package/dist/core/guardrails/AuditLogger.js +4 -4
  30. package/dist/core/guardrails/CommandFirewall.js +15 -0
  31. package/dist/core/guardrails/CommandFirewall.js.map +1 -1
  32. package/dist/index.js +24 -903
  33. package/dist/index.js.map +1 -1
  34. package/dist/infrastructure/llm/LLMClient.js +102 -4
  35. package/dist/infrastructure/llm/LLMClient.js.map +1 -1
  36. package/dist/infrastructure/llm/ToolDefinitions.d.ts +0 -136
  37. package/dist/infrastructure/llm/ToolDefinitions.js +0 -102
  38. package/dist/infrastructure/llm/ToolDefinitions.js.map +1 -1
  39. package/dist/infrastructure/protocols/PlinkSerial.js +1 -1
  40. package/dist/infrastructure/protocols/PlinkSerial.js.map +1 -1
  41. package/dist/server/dashboard.js +1033 -1033
  42. package/dist/server/index.js +8 -8
  43. package/dist/server/shell-simulator.d.ts +28 -1
  44. package/dist/server/shell-simulator.js +599 -73
  45. package/dist/server/shell-simulator.js.map +1 -1
  46. package/dist/server/ssh.js +20 -20
  47. 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
- const trimmed = line.trim();
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
- if (this.mode === 'GLOBAL_CONFIG') {
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 === 'EIGRP_CONFIG') {
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
- ` 192.168.1.0/24 area 0\n` +
741
- ` Routing Information Sources:\n` +
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