ispbills-icli 8.6.8 → 8.6.9
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/package.json +1 -1
- package/src/tui/app.js +110 -49
- package/src/tui/components.js +3 -3
package/package.json
CHANGED
package/src/tui/app.js
CHANGED
|
@@ -318,6 +318,50 @@ let itemId = 0;
|
|
|
318
318
|
const nextId = () => ++itemId;
|
|
319
319
|
const MODES = ['ask', 'plan', 'autopilot'];
|
|
320
320
|
|
|
321
|
+
// Tab-specific quick-command panels. Static — does not depend on component state.
|
|
322
|
+
const TAB_CONTENT = {
|
|
323
|
+
devices: {
|
|
324
|
+
title: 'Devices',
|
|
325
|
+
commands: [
|
|
326
|
+
{ cmd: '/status', desc: 'Fleet health summary' },
|
|
327
|
+
{ cmd: '/reachable', desc: 'Who is up / down' },
|
|
328
|
+
{ cmd: '/alarms', desc: 'Active alarms' },
|
|
329
|
+
{ cmd: '/check [device]', desc: 'Full device diagnostics' },
|
|
330
|
+
{ cmd: '/uptime [device]', desc: 'Uptime and last reboot' },
|
|
331
|
+
{ cmd: '/cpu [device]', desc: 'CPU / memory load' },
|
|
332
|
+
{ cmd: '/offline', desc: 'All offline devices' },
|
|
333
|
+
{ cmd: '/flapping [device]', desc: 'Flapping interfaces' },
|
|
334
|
+
],
|
|
335
|
+
hint: 'Press a number (1–8) to insert command · Enter to return to Session',
|
|
336
|
+
},
|
|
337
|
+
maps: {
|
|
338
|
+
title: 'Network Maps',
|
|
339
|
+
commands: [
|
|
340
|
+
{ cmd: '/vlan [id]', desc: 'VLAN topology & members' },
|
|
341
|
+
{ cmd: '/route [device]', desc: 'Routing table & next-hops' },
|
|
342
|
+
{ cmd: '/overlaps', desc: 'IP subnet overlap check' },
|
|
343
|
+
{ cmd: '/mac [addr]', desc: 'MAC address lookup' },
|
|
344
|
+
{ cmd: '/arp [device]', desc: 'ARP table & IP-MAC map' },
|
|
345
|
+
{ cmd: '/trace [host]', desc: 'Traceroute to host' },
|
|
346
|
+
{ cmd: '/topology', desc: 'Network topology diagram' },
|
|
347
|
+
{ cmd: '/segments', desc: 'Network segments & subnets' },
|
|
348
|
+
],
|
|
349
|
+
hint: 'Press a number (1–8) to insert command · Enter to return to Session',
|
|
350
|
+
},
|
|
351
|
+
alarms: {
|
|
352
|
+
title: 'Alarms',
|
|
353
|
+
commands: [
|
|
354
|
+
{ cmd: '/alarms', desc: 'Active alarms & recent faults' },
|
|
355
|
+
{ cmd: '/offline', desc: 'All offline devices' },
|
|
356
|
+
{ cmd: '/down [circuit]', desc: 'Down circuits / uplinks' },
|
|
357
|
+
{ cmd: '/flapping [device]', desc: 'Flapping interfaces' },
|
|
358
|
+
{ cmd: '/optical [device]', desc: 'Optical RX/TX (dBm)' },
|
|
359
|
+
{ cmd: '/reachable', desc: 'Reachability sweep' },
|
|
360
|
+
],
|
|
361
|
+
hint: 'Press a number (1–6) to insert command · Enter to return to Session',
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
|
|
321
365
|
export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, showBanner = true, agentName, resumeMode }) {
|
|
322
366
|
const { exit } = useApp();
|
|
323
367
|
const initialResumeRef = useRef(undefined);
|
|
@@ -984,7 +1028,42 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
|
|
|
984
1028
|
if (q === '/model' || q.startsWith('/model ')) {
|
|
985
1029
|
const name = q.slice('/model'.length).trim();
|
|
986
1030
|
if (!name) {
|
|
987
|
-
|
|
1031
|
+
// Show interactive model picker
|
|
1032
|
+
const cur = model || modelRef.current || '';
|
|
1033
|
+
const MODELS = [
|
|
1034
|
+
{ id: 'gpt-4o-mini', desc: 'Fast & affordable (default)' },
|
|
1035
|
+
{ id: 'openai/gpt-4o', desc: 'OpenAI GPT-4o — balanced' },
|
|
1036
|
+
{ id: 'openai/gpt-4.1', desc: 'OpenAI GPT-4.1 — latest' },
|
|
1037
|
+
{ id: 'openai/o3', desc: 'OpenAI o3 — reasoning' },
|
|
1038
|
+
{ id: 'openai/o3-mini', desc: 'OpenAI o3-mini — fast reasoning' },
|
|
1039
|
+
{ id: 'anthropic/claude-3-5-sonnet', desc: 'Claude 3.5 Sonnet — strong' },
|
|
1040
|
+
{ id: 'anthropic/claude-3-opus', desc: 'Claude 3 Opus — advanced' },
|
|
1041
|
+
{ id: 'anthropic/claude-3-haiku', desc: 'Claude 3 Haiku — fast' },
|
|
1042
|
+
{ id: 'meta/llama-3.3-70b-instruct', desc: 'Llama 3.3 70B (open)' },
|
|
1043
|
+
{ id: 'mistral/mistral-large', desc: 'Mistral Large' },
|
|
1044
|
+
];
|
|
1045
|
+
choiceActionRef.current = (val) => {
|
|
1046
|
+
if (val) {
|
|
1047
|
+
modelRef.current = val;
|
|
1048
|
+
setModel(val);
|
|
1049
|
+
cfg._model = val;
|
|
1050
|
+
persistCfg();
|
|
1051
|
+
saveCurrentSession();
|
|
1052
|
+
pushNotice(`${glyphs.check} Model set to ${val}.`, C.green);
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
setChoice({
|
|
1056
|
+
title: `Select AI Model (current: ${cur || 'server default'})`,
|
|
1057
|
+
note: 'Use ↑/↓ to move, Enter to confirm, Esc to cancel',
|
|
1058
|
+
sel: Math.max(0, MODELS.findIndex((m) => m.id === cur)),
|
|
1059
|
+
text: '',
|
|
1060
|
+
focusText: false,
|
|
1061
|
+
allowText: false,
|
|
1062
|
+
options: MODELS.map((m, i) => ({
|
|
1063
|
+
label: `${i + 1}. ${m.id.padEnd(36)} ${m.desc}`,
|
|
1064
|
+
value: m.id,
|
|
1065
|
+
})),
|
|
1066
|
+
});
|
|
988
1067
|
} else {
|
|
989
1068
|
modelRef.current = name;
|
|
990
1069
|
setModel(name);
|
|
@@ -1318,7 +1397,7 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
|
|
|
1318
1397
|
' optical — Optical power analysis and ONU diagnostics (enabled)',
|
|
1319
1398
|
' fleet-check — Parallel multi-device scanning (enabled)',
|
|
1320
1399
|
' backup — Automated config backup via git (enabled)',
|
|
1321
|
-
'
|
|
1400
|
+
' maps — Network topology maps and IP planning (enabled)',
|
|
1322
1401
|
' alarms — Real-time alarm monitoring (enabled)',
|
|
1323
1402
|
'',
|
|
1324
1403
|
'Use /experimental to unlock additional skills.',
|
|
@@ -1822,61 +1901,42 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
|
|
|
1822
1901
|
if (key.ctrl && input === 'p' && chips.length) { setActiveChip((i) => (i - 1 + chips.length) % chips.length); return; }
|
|
1823
1902
|
if (key.tab && !key.shift && !busy && !composerText.trim()) {
|
|
1824
1903
|
setActiveTab((t) => {
|
|
1825
|
-
const tabs = ['session', 'devices', '
|
|
1904
|
+
const tabs = ['session', 'devices', 'maps', 'alarms'];
|
|
1826
1905
|
return tabs[(tabs.indexOf(t) + 1) % tabs.length];
|
|
1827
1906
|
});
|
|
1828
1907
|
}
|
|
1908
|
+
// Alt+↑/↓ for viewport scrolling (alternative to PgUp/PgDn)
|
|
1909
|
+
if (key.meta && key.upArrow && !choice && !search && !sessionPicker && !diffState) {
|
|
1910
|
+
setScrollOffset((n) => Math.min(maxScrollRef.current, n + Math.max(3, Math.floor(scrollPage / 4))));
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
if (key.meta && key.downArrow && !choice && !search && !sessionPicker && !diffState) {
|
|
1914
|
+
setScrollOffset((n) => Math.max(0, n - Math.max(3, Math.floor(scrollPage / 4))));
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
// Number keys on non-session tabs: 1-9 insert the panel's Nth command into composer
|
|
1918
|
+
if (activeTab !== 'session' && !choice && !search) {
|
|
1919
|
+
const n = parseInt(input, 10);
|
|
1920
|
+
if (!isNaN(n) && n >= 1) {
|
|
1921
|
+
const panel = TAB_CONTENT[activeTab];
|
|
1922
|
+
const cmd = panel?.commands[n - 1]?.cmd;
|
|
1923
|
+
if (cmd) {
|
|
1924
|
+
const insertCmd = cmd.replace(/\[.*?\]/g, '').trim();
|
|
1925
|
+
setActiveTab('session');
|
|
1926
|
+
setComposerPreset({ token: Date.now(), text: insertCmd });
|
|
1927
|
+
}
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1829
1931
|
});
|
|
1830
1932
|
|
|
1831
|
-
// Tab-specific content panels (shown when not on Session tab)
|
|
1832
|
-
const TAB_CONTENT = {
|
|
1833
|
-
devices: {
|
|
1834
|
-
title: 'Devices',
|
|
1835
|
-
commands: [
|
|
1836
|
-
{ cmd: '/status', desc: 'Fleet health summary' },
|
|
1837
|
-
{ cmd: '/reachable', desc: 'Who is up / down' },
|
|
1838
|
-
{ cmd: '/alarms', desc: 'Active alarms' },
|
|
1839
|
-
{ cmd: '/check [device]', desc: 'Full device diagnostics' },
|
|
1840
|
-
{ cmd: '/uptime [device]', desc: 'Uptime and last reboot' },
|
|
1841
|
-
{ cmd: '/cpu [device]', desc: 'CPU / memory load' },
|
|
1842
|
-
{ cmd: '/offline', desc: 'All offline devices' },
|
|
1843
|
-
{ cmd: '/flapping [device]', desc: 'Flapping interfaces' },
|
|
1844
|
-
],
|
|
1845
|
-
hint: 'Press Enter to switch back to Session and use the composer',
|
|
1846
|
-
},
|
|
1847
|
-
customers: {
|
|
1848
|
-
title: 'Customers',
|
|
1849
|
-
commands: [
|
|
1850
|
-
{ cmd: '/customer [name]', desc: 'Look up a customer' },
|
|
1851
|
-
{ cmd: '/online', desc: 'Online PPPoE sessions' },
|
|
1852
|
-
{ cmd: '/pppoe [user]', desc: 'PPPoE session detail' },
|
|
1853
|
-
{ cmd: '/dhcp', desc: 'DHCP leases & pool usage' },
|
|
1854
|
-
{ cmd: '/onu [id]', desc: 'ONU signal and status' },
|
|
1855
|
-
{ cmd: '/bandwidth [device]', desc: 'Traffic / bandwidth' },
|
|
1856
|
-
],
|
|
1857
|
-
hint: 'Press Enter to switch back to Session and send commands',
|
|
1858
|
-
},
|
|
1859
|
-
alarms: {
|
|
1860
|
-
title: 'Alarms',
|
|
1861
|
-
commands: [
|
|
1862
|
-
{ cmd: '/alarms', desc: 'Active alarms & recent faults' },
|
|
1863
|
-
{ cmd: '/offline', desc: 'All offline devices' },
|
|
1864
|
-
{ cmd: '/down [circuit]', desc: 'Down circuits / uplinks' },
|
|
1865
|
-
{ cmd: '/flapping [device]', desc: 'Flapping interfaces' },
|
|
1866
|
-
{ cmd: '/optical [device]', desc: 'Optical RX/TX (dBm)' },
|
|
1867
|
-
{ cmd: '/reachable', desc: 'Reachability sweep' },
|
|
1868
|
-
],
|
|
1869
|
-
hint: 'Press Enter to switch back to Session and type a command',
|
|
1870
|
-
},
|
|
1871
|
-
};
|
|
1872
|
-
|
|
1873
1933
|
const tabPanel = activeTab !== 'session' ? TAB_CONTENT[activeTab] : null;
|
|
1874
1934
|
|
|
1875
1935
|
const renderItem = (it) => {
|
|
1876
1936
|
const c = it.cols || 80;
|
|
1877
1937
|
switch (it.type) {
|
|
1878
1938
|
case 'user': return html`<${UserMessage} key=${it.id} text=${it.text} cols=${c} ts=${it.ts} />`;
|
|
1879
|
-
case 'assistant': return html`<${AssistantMessage} key=${it.id} text=${it.text} cols=${c}
|
|
1939
|
+
case 'assistant': return html`<${AssistantMessage} key=${it.id} text=${it.text} cols=${c} />`;
|
|
1880
1940
|
case 'plan': return html`<${Plan} key=${it.id} reply=${it.reply} />`;
|
|
1881
1941
|
case 'connect': return html`<${Connect} key=${it.id} reply=${it.reply} />`;
|
|
1882
1942
|
case 'activity': return html`<${StatusLines} key=${it.id} lines=${it.status} busy=${false} />`;
|
|
@@ -1929,7 +1989,7 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
|
|
|
1929
1989
|
|
|
1930
1990
|
const scrollIndicator = hasAbove
|
|
1931
1991
|
? html`<${Box} paddingX=${1}>
|
|
1932
|
-
<${Text} color=${C.muted} dimColor>${glyphs.up} ${aboveCount} message${aboveCount === 1 ? '' : 's'} above ${midDot()} PgUp/PgDn to scroll<//>
|
|
1992
|
+
<${Text} color=${C.muted} dimColor>${glyphs.up} ${aboveCount} message${aboveCount === 1 ? '' : 's'} above ${midDot()} PgUp/PgDn or Alt+↑/↓ to scroll<//>
|
|
1933
1993
|
<//>`
|
|
1934
1994
|
: null;
|
|
1935
1995
|
|
|
@@ -1966,10 +2026,11 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
|
|
|
1966
2026
|
<${Text} color=${C.brand} bold>${tabPanel.title} — Quick Commands<//>\n
|
|
1967
2027
|
${tabPanel.commands.map((c, i) =>
|
|
1968
2028
|
html`<${Box} key=${i}>
|
|
1969
|
-
<${Text} color=${C.
|
|
2029
|
+
<${Text} color=${C.accent} bold>${String(i + 1)}. <//>
|
|
2030
|
+
<${Text} color=${C.slash}>${c.cmd.padEnd(22)}<//>
|
|
1970
2031
|
<${Text} color=${C.muted}>${c.desc}<//>
|
|
1971
2032
|
<//>`)}
|
|
1972
|
-
\n<${Text} color=${C.muted}>${tabPanel.hint}
|
|
2033
|
+
\n<${Text} color=${C.muted}>${tabPanel.hint}<//>
|
|
1973
2034
|
<//>`
|
|
1974
2035
|
: html`<${Box} flexDirection="column">
|
|
1975
2036
|
${showEmptyBanner
|
package/src/tui/components.js
CHANGED
|
@@ -109,7 +109,7 @@ function SpeakerLine({ label, color, glyphColor, cols = 80, ts, rightLabel = ''
|
|
|
109
109
|
const TABS = [
|
|
110
110
|
{ id: 'session', label: 'Session' },
|
|
111
111
|
{ id: 'devices', label: 'Devices' },
|
|
112
|
-
{ id: '
|
|
112
|
+
{ id: 'maps', label: 'Network Maps' },
|
|
113
113
|
{ id: 'alarms', label: 'Alarms' },
|
|
114
114
|
];
|
|
115
115
|
|
|
@@ -278,13 +278,13 @@ function StreamingText({ text = '' }) {
|
|
|
278
278
|
return rendered;
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
export function AssistantMessage({ text, cols = 80, ts, label = `${glyphs.
|
|
281
|
+
export function AssistantMessage({ text, cols = 80, ts, label = `${glyphs.ring} iCopilot`, streaming = false }) {
|
|
282
282
|
// §11: response content box capped at 100 columns; separators stay full-width (§6).
|
|
283
283
|
const bodyWidth = Math.min(cols, 100);
|
|
284
284
|
return html`
|
|
285
285
|
<${Box} flexDirection="column" marginTop=${1}>
|
|
286
286
|
<${Sep} cols=${cols} />
|
|
287
|
-
<${SpeakerLine} label=${label} color="white" glyphColor=${C.accent} cols=${cols}
|
|
287
|
+
<${SpeakerLine} label=${label} color="white" glyphColor=${C.accent} cols=${cols} />
|
|
288
288
|
<${Box} paddingX=${1} flexDirection="column" width=${bodyWidth}>
|
|
289
289
|
${streaming ? html`<${StreamingText} text=${text} />` : html`<${Markdown} content=${text} />`}
|
|
290
290
|
<//>
|