agenttop 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -12,18 +12,18 @@ import {
|
|
|
12
12
|
saveConfig,
|
|
13
13
|
setNickname,
|
|
14
14
|
startMcpServer
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-H2JOTO54.js";
|
|
16
16
|
|
|
17
17
|
// src/index.tsx
|
|
18
18
|
import { readFileSync as readFileSync4 } from "fs";
|
|
19
19
|
import { join as join4, dirname as dirname4 } from "path";
|
|
20
20
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
21
|
-
import
|
|
21
|
+
import React10 from "react";
|
|
22
22
|
import { render } from "ink";
|
|
23
23
|
|
|
24
24
|
// src/ui/App.tsx
|
|
25
|
-
import { useState as
|
|
26
|
-
import { Box as
|
|
25
|
+
import { useState as useState8, useEffect as useEffect5, useCallback as useCallback3 } from "react";
|
|
26
|
+
import { Box as Box9, Text as Text9, useApp, useInput as useInput3, useStdout as useStdout2 } from "ink";
|
|
27
27
|
|
|
28
28
|
// src/hooks/installer.ts
|
|
29
29
|
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync, chmodSync } from "fs";
|
|
@@ -273,76 +273,88 @@ var formatModel = (model) => {
|
|
|
273
273
|
if (model.includes("haiku")) return "haiku";
|
|
274
274
|
return model.slice(0, 8);
|
|
275
275
|
};
|
|
276
|
-
var formatProject = (project) => {
|
|
276
|
+
var formatProject = (project, max) => {
|
|
277
277
|
const parts = project.split("/");
|
|
278
278
|
const last = parts[parts.length - 1] || project;
|
|
279
|
-
return last.length >
|
|
279
|
+
return last.length > max ? last.slice(0, max - 1) + "\u2026" : last;
|
|
280
280
|
};
|
|
281
281
|
var formatTokens = (n) => {
|
|
282
282
|
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
|
283
283
|
if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
|
|
284
284
|
return String(n);
|
|
285
285
|
};
|
|
286
|
+
var truncate = (s, max) => s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
287
|
+
var SIDEBAR_WIDTH = 28;
|
|
288
|
+
var INNER_WIDTH = SIDEBAR_WIDTH - 4;
|
|
286
289
|
var SessionList = React2.memo(({ sessions, selectedIndex, focused, filter }) => {
|
|
287
|
-
return /* @__PURE__ */ jsxs2(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
/* @__PURE__ */ jsxs2(
|
|
304
|
-
Text2,
|
|
305
|
-
{
|
|
306
|
-
color: isSelected ? colors.bright : colors.text,
|
|
307
|
-
bold: isSelected,
|
|
308
|
-
backgroundColor: isSelected ? colors.selected : void 0,
|
|
309
|
-
children: [
|
|
310
|
-
indicator,
|
|
311
|
-
" ",
|
|
312
|
-
displayName
|
|
313
|
-
]
|
|
314
|
-
}
|
|
315
|
-
),
|
|
316
|
-
session.nickname && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
317
|
-
" ",
|
|
318
|
-
session.slug
|
|
319
|
-
] }),
|
|
320
|
-
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
321
|
-
" ",
|
|
322
|
-
formatProject(session.project),
|
|
323
|
-
" | ",
|
|
324
|
-
formatModel(session.model)
|
|
325
|
-
] }),
|
|
326
|
-
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
327
|
-
" ",
|
|
328
|
-
"CPU ",
|
|
329
|
-
session.cpu,
|
|
330
|
-
"% | ",
|
|
331
|
-
session.memMB,
|
|
332
|
-
"MB | ",
|
|
333
|
-
session.agentCount,
|
|
334
|
-
" ag"
|
|
290
|
+
return /* @__PURE__ */ jsxs2(
|
|
291
|
+
Box2,
|
|
292
|
+
{
|
|
293
|
+
flexDirection: "column",
|
|
294
|
+
width: SIDEBAR_WIDTH,
|
|
295
|
+
borderStyle: "single",
|
|
296
|
+
borderColor: focused ? colors.primary : colors.border,
|
|
297
|
+
overflow: "hidden",
|
|
298
|
+
children: [
|
|
299
|
+
/* @__PURE__ */ jsxs2(Box2, { paddingX: 1, children: [
|
|
300
|
+
/* @__PURE__ */ jsx2(Text2, { color: colors.header, bold: true, children: "SESSIONS" }),
|
|
301
|
+
filter && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
302
|
+
" [",
|
|
303
|
+
truncate(filter, 10),
|
|
304
|
+
"]"
|
|
305
|
+
] })
|
|
335
306
|
] }),
|
|
336
|
-
/* @__PURE__ */
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
"
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
307
|
+
sessions.length === 0 && /* @__PURE__ */ jsx2(Box2, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx2(Text2, { color: colors.muted, italic: true, children: filter ? "No matches" : "No sessions" }) }),
|
|
308
|
+
sessions.map((session, i) => {
|
|
309
|
+
const isSelected = i === selectedIndex;
|
|
310
|
+
const indicator = isSelected ? ">" : " ";
|
|
311
|
+
const nameMaxLen = INNER_WIDTH - 2;
|
|
312
|
+
const displayName = truncate(session.nickname || session.slug, nameMaxLen);
|
|
313
|
+
const totalIn = session.usage.inputTokens + session.usage.cacheReadTokens;
|
|
314
|
+
const proj = formatProject(session.project, 12);
|
|
315
|
+
const model = formatModel(session.model);
|
|
316
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, paddingY: 0, children: [
|
|
317
|
+
/* @__PURE__ */ jsxs2(
|
|
318
|
+
Text2,
|
|
319
|
+
{
|
|
320
|
+
color: isSelected ? colors.bright : colors.text,
|
|
321
|
+
bold: isSelected,
|
|
322
|
+
backgroundColor: isSelected ? colors.selected : void 0,
|
|
323
|
+
wrap: "truncate",
|
|
324
|
+
children: [
|
|
325
|
+
indicator,
|
|
326
|
+
" ",
|
|
327
|
+
displayName
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
session.nickname && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
332
|
+
" ",
|
|
333
|
+
truncate(session.slug, nameMaxLen)
|
|
334
|
+
] }),
|
|
335
|
+
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
336
|
+
" ",
|
|
337
|
+
proj,
|
|
338
|
+
" ",
|
|
339
|
+
model,
|
|
340
|
+
" ",
|
|
341
|
+
session.agentCount,
|
|
342
|
+
"ag"
|
|
343
|
+
] }),
|
|
344
|
+
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
345
|
+
" ",
|
|
346
|
+
formatTokens(totalIn),
|
|
347
|
+
"in ",
|
|
348
|
+
formatTokens(session.usage.outputTokens),
|
|
349
|
+
"out ",
|
|
350
|
+
session.cpu,
|
|
351
|
+
"%"
|
|
352
|
+
] })
|
|
353
|
+
] }, session.sessionId);
|
|
354
|
+
})
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
);
|
|
346
358
|
});
|
|
347
359
|
|
|
348
360
|
// src/ui/components/ActivityFeed.tsx
|
|
@@ -684,16 +696,234 @@ var FooterBar = React7.memo(({ keybindings, updateStatus }) => /* @__PURE__ */ j
|
|
|
684
696
|
label(keybindings.detail),
|
|
685
697
|
":detail"
|
|
686
698
|
] }) }),
|
|
699
|
+
/* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsxs7(Text7, { color: "#5C6370", children: [
|
|
700
|
+
label(keybindings.settings),
|
|
701
|
+
":settings"
|
|
702
|
+
] }) }),
|
|
687
703
|
updateStatus && /* @__PURE__ */ jsx7(Box7, { marginRight: 2, children: /* @__PURE__ */ jsx7(Text7, { color: colors.secondary, children: updateStatus }) })
|
|
688
704
|
] }));
|
|
689
705
|
|
|
706
|
+
// src/ui/components/SettingsMenu.tsx
|
|
707
|
+
import React8, { useState as useState3, useMemo } from "react";
|
|
708
|
+
import { Box as Box8, Text as Text8, useInput as useInput2, useStdout } from "ink";
|
|
709
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
710
|
+
var KEYBIND_LABELS = {
|
|
711
|
+
quit: "Quit",
|
|
712
|
+
navUp: "Navigate up",
|
|
713
|
+
navDown: "Navigate down",
|
|
714
|
+
panelNext: "Next panel",
|
|
715
|
+
panelPrev: "Previous panel",
|
|
716
|
+
scrollTop: "Scroll to top",
|
|
717
|
+
scrollBottom: "Scroll to bottom",
|
|
718
|
+
filter: "Filter sessions",
|
|
719
|
+
nickname: "Set nickname",
|
|
720
|
+
clearNickname: "Clear nickname",
|
|
721
|
+
detail: "Detail view",
|
|
722
|
+
update: "Install update",
|
|
723
|
+
settings: "Settings"
|
|
724
|
+
};
|
|
725
|
+
var RULE_LABELS = {
|
|
726
|
+
network: "Network detection",
|
|
727
|
+
exfiltration: "Exfiltration detection",
|
|
728
|
+
sensitiveFiles: "Sensitive files",
|
|
729
|
+
shellEscape: "Shell escape",
|
|
730
|
+
injection: "Prompt injection"
|
|
731
|
+
};
|
|
732
|
+
var SEVERITY_OPTIONS = ["info", "warn", "high", "critical"];
|
|
733
|
+
var displayKey = (key) => {
|
|
734
|
+
if (key === "tab") return "tab";
|
|
735
|
+
if (key === "shift+tab") return "S-tab";
|
|
736
|
+
if (key === "enter") return "enter";
|
|
737
|
+
return key;
|
|
738
|
+
};
|
|
739
|
+
var buildMenuItems = () => {
|
|
740
|
+
const items = [];
|
|
741
|
+
items.push({ type: "header", label: "KEYBINDINGS", section: "keybindings", getValue: () => "", key: void 0 });
|
|
742
|
+
for (const [k, label2] of Object.entries(KEYBIND_LABELS)) {
|
|
743
|
+
const kbKey = k;
|
|
744
|
+
items.push({
|
|
745
|
+
type: "keybind",
|
|
746
|
+
label: label2,
|
|
747
|
+
section: "keybindings",
|
|
748
|
+
key: kbKey,
|
|
749
|
+
getValue: (cfg) => displayKey(cfg.keybindings[kbKey]),
|
|
750
|
+
apply: (cfg, newValue) => ({
|
|
751
|
+
...cfg,
|
|
752
|
+
keybindings: { ...cfg.keybindings, [kbKey]: newValue }
|
|
753
|
+
})
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
items.push({ type: "header", label: "SECURITY RULES", section: "security", getValue: () => "", key: void 0 });
|
|
757
|
+
for (const [k, label2] of Object.entries(RULE_LABELS)) {
|
|
758
|
+
const ruleKey = k;
|
|
759
|
+
items.push({
|
|
760
|
+
type: "toggle",
|
|
761
|
+
label: label2,
|
|
762
|
+
section: "security",
|
|
763
|
+
key: ruleKey,
|
|
764
|
+
getValue: (cfg) => cfg.security.rules[ruleKey] ? "ON" : "OFF",
|
|
765
|
+
apply: (cfg) => ({
|
|
766
|
+
...cfg,
|
|
767
|
+
security: {
|
|
768
|
+
...cfg.security,
|
|
769
|
+
rules: { ...cfg.security.rules, [ruleKey]: !cfg.security.rules[ruleKey] }
|
|
770
|
+
}
|
|
771
|
+
})
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
items.push({
|
|
775
|
+
type: "header",
|
|
776
|
+
label: "NOTIFICATIONS",
|
|
777
|
+
section: "notifications",
|
|
778
|
+
getValue: () => "",
|
|
779
|
+
key: void 0
|
|
780
|
+
});
|
|
781
|
+
items.push({
|
|
782
|
+
type: "toggle",
|
|
783
|
+
label: "Terminal bell",
|
|
784
|
+
section: "notifications",
|
|
785
|
+
key: "bell",
|
|
786
|
+
getValue: (cfg) => cfg.notifications.bell ? "ON" : "OFF",
|
|
787
|
+
apply: (cfg) => ({
|
|
788
|
+
...cfg,
|
|
789
|
+
notifications: { ...cfg.notifications, bell: !cfg.notifications.bell }
|
|
790
|
+
})
|
|
791
|
+
});
|
|
792
|
+
items.push({
|
|
793
|
+
type: "toggle",
|
|
794
|
+
label: "Desktop notifications",
|
|
795
|
+
section: "notifications",
|
|
796
|
+
key: "desktop",
|
|
797
|
+
getValue: (cfg) => cfg.notifications.desktop ? "ON" : "OFF",
|
|
798
|
+
apply: (cfg) => ({
|
|
799
|
+
...cfg,
|
|
800
|
+
notifications: { ...cfg.notifications, desktop: !cfg.notifications.desktop }
|
|
801
|
+
})
|
|
802
|
+
});
|
|
803
|
+
items.push({
|
|
804
|
+
type: "cycle",
|
|
805
|
+
label: "Minimum severity",
|
|
806
|
+
section: "notifications",
|
|
807
|
+
key: "minSeverity",
|
|
808
|
+
getValue: (cfg) => cfg.notifications.minSeverity,
|
|
809
|
+
apply: (cfg) => {
|
|
810
|
+
const idx = SEVERITY_OPTIONS.indexOf(cfg.notifications.minSeverity);
|
|
811
|
+
const next = SEVERITY_OPTIONS[(idx + 1) % SEVERITY_OPTIONS.length];
|
|
812
|
+
return { ...cfg, notifications: { ...cfg.notifications, minSeverity: next } };
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
return items;
|
|
816
|
+
};
|
|
817
|
+
var MENU_ITEMS = buildMenuItems();
|
|
818
|
+
var SELECTABLE_INDICES = MENU_ITEMS.map((item, i) => item.type !== "header" ? i : -1).filter((i) => i >= 0);
|
|
819
|
+
var SettingsMenu = React8.memo(({ config, onClose }) => {
|
|
820
|
+
const { stdout } = useStdout();
|
|
821
|
+
const termHeight = stdout?.rows ?? 40;
|
|
822
|
+
const [localConfig, setLocalConfig] = useState3(() => JSON.parse(JSON.stringify(config)));
|
|
823
|
+
const [selectablePos, setSelectablePos] = useState3(0);
|
|
824
|
+
const [rebinding, setRebinding] = useState3(false);
|
|
825
|
+
const selectedIndex = SELECTABLE_INDICES[selectablePos];
|
|
826
|
+
const maxLabelLen = useMemo(
|
|
827
|
+
() => Math.max(...MENU_ITEMS.filter((i) => i.type !== "header").map((i) => i.label.length)),
|
|
828
|
+
[]
|
|
829
|
+
);
|
|
830
|
+
useInput2((input, key) => {
|
|
831
|
+
if (rebinding) {
|
|
832
|
+
const item = MENU_ITEMS[selectedIndex];
|
|
833
|
+
let newKey;
|
|
834
|
+
if (key.tab && key.shift) newKey = "shift+tab";
|
|
835
|
+
else if (key.tab) newKey = "tab";
|
|
836
|
+
else if (key.return) newKey = "enter";
|
|
837
|
+
else if (key.escape) {
|
|
838
|
+
setRebinding(false);
|
|
839
|
+
return;
|
|
840
|
+
} else if (input && input.length === 1) newKey = input;
|
|
841
|
+
else return;
|
|
842
|
+
if (item.apply) {
|
|
843
|
+
setLocalConfig((c) => item.apply(c, newKey));
|
|
844
|
+
}
|
|
845
|
+
setRebinding(false);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
if (key.escape) {
|
|
849
|
+
onClose(localConfig);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (key.upArrow) {
|
|
853
|
+
setSelectablePos((p) => Math.max(0, p - 1));
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (key.downArrow) {
|
|
857
|
+
setSelectablePos((p) => Math.min(SELECTABLE_INDICES.length - 1, p + 1));
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
if (key.return) {
|
|
861
|
+
const item = MENU_ITEMS[selectedIndex];
|
|
862
|
+
if (item.type === "keybind") {
|
|
863
|
+
setRebinding(true);
|
|
864
|
+
} else if (item.apply) {
|
|
865
|
+
setLocalConfig((c) => item.apply(c));
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
const contentHeight = termHeight - 6;
|
|
870
|
+
const halfView = Math.floor(contentHeight / 2);
|
|
871
|
+
const menuItemIndex = MENU_ITEMS.indexOf(MENU_ITEMS[selectedIndex]);
|
|
872
|
+
const scrollStart = Math.max(0, Math.min(menuItemIndex - halfView, MENU_ITEMS.length - contentHeight));
|
|
873
|
+
const visibleItems = MENU_ITEMS.slice(Math.max(0, scrollStart), Math.max(0, scrollStart) + contentHeight);
|
|
874
|
+
const visibleStartIndex = Math.max(0, scrollStart);
|
|
875
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", height: termHeight, children: /* @__PURE__ */ jsxs8(
|
|
876
|
+
Box8,
|
|
877
|
+
{
|
|
878
|
+
borderStyle: "round",
|
|
879
|
+
borderColor: colors.primary,
|
|
880
|
+
flexDirection: "column",
|
|
881
|
+
paddingX: 2,
|
|
882
|
+
paddingY: 1,
|
|
883
|
+
height: termHeight,
|
|
884
|
+
children: [
|
|
885
|
+
/* @__PURE__ */ jsxs8(Box8, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
886
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.header, bold: true, children: "SETTINGS" }),
|
|
887
|
+
/* @__PURE__ */ jsx8(Text8, { color: colors.muted, children: "esc to save & close" })
|
|
888
|
+
] }),
|
|
889
|
+
visibleItems.map((item, vi) => {
|
|
890
|
+
const realIndex = visibleStartIndex + vi;
|
|
891
|
+
const isSelected = realIndex === selectedIndex;
|
|
892
|
+
if (item.type === "header") {
|
|
893
|
+
return /* @__PURE__ */ jsx8(Box8, { marginTop: realIndex > 0 ? 1 : 0, children: /* @__PURE__ */ jsxs8(Text8, { color: colors.accent, bold: true, children: [
|
|
894
|
+
" ",
|
|
895
|
+
item.label
|
|
896
|
+
] }) }, `h-${item.label}`);
|
|
897
|
+
}
|
|
898
|
+
const value = item.getValue(localConfig);
|
|
899
|
+
const dots = ".".repeat(Math.max(2, maxLabelLen - item.label.length + 4));
|
|
900
|
+
const isRebindingThis = rebinding && isSelected;
|
|
901
|
+
const displayValue = isRebindingThis ? "[press key...]" : value;
|
|
902
|
+
const valueColor = isRebindingThis ? colors.warning : value === "ON" ? colors.secondary : value === "OFF" ? colors.error : colors.bright;
|
|
903
|
+
return /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
904
|
+
/* @__PURE__ */ jsxs8(Text8, { color: isSelected ? colors.primary : colors.text, children: [
|
|
905
|
+
isSelected ? "> " : " ",
|
|
906
|
+
" ",
|
|
907
|
+
item.label,
|
|
908
|
+
" ",
|
|
909
|
+
dots,
|
|
910
|
+
" "
|
|
911
|
+
] }),
|
|
912
|
+
/* @__PURE__ */ jsx8(Text8, { color: valueColor, children: displayValue })
|
|
913
|
+
] }, `${item.section}-${item.key}`);
|
|
914
|
+
})
|
|
915
|
+
]
|
|
916
|
+
}
|
|
917
|
+
) });
|
|
918
|
+
});
|
|
919
|
+
|
|
690
920
|
// src/ui/hooks/useSessions.ts
|
|
691
|
-
import { useState as
|
|
921
|
+
import { useState as useState4, useEffect as useEffect2, useCallback, useRef } from "react";
|
|
692
922
|
var ACTIVE_POLL_MS = 1e4;
|
|
693
923
|
var IDLE_POLL_MS = 3e4;
|
|
694
924
|
var useSessions = (allUsers, filter) => {
|
|
695
|
-
const [sessions, setSessions] =
|
|
696
|
-
const [selectedIndex, setSelectedIndex] =
|
|
925
|
+
const [sessions, setSessions] = useState4([]);
|
|
926
|
+
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
697
927
|
const usageOverrides = useRef(/* @__PURE__ */ new Map());
|
|
698
928
|
const refresh = useCallback(() => {
|
|
699
929
|
const found = discoverSessions(allUsers);
|
|
@@ -753,10 +983,10 @@ var useSessions = (allUsers, filter) => {
|
|
|
753
983
|
};
|
|
754
984
|
|
|
755
985
|
// src/ui/hooks/useActivityStream.ts
|
|
756
|
-
import { useState as
|
|
986
|
+
import { useState as useState5, useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
757
987
|
var MAX_EVENTS = 200;
|
|
758
988
|
var useActivityStream = (session, allUsers) => {
|
|
759
|
-
const [events, setEvents] =
|
|
989
|
+
const [events, setEvents] = useState5([]);
|
|
760
990
|
const watcherRef = useRef2(null);
|
|
761
991
|
useEffect3(() => {
|
|
762
992
|
setEvents([]);
|
|
@@ -785,7 +1015,7 @@ var useActivityStream = (session, allUsers) => {
|
|
|
785
1015
|
};
|
|
786
1016
|
|
|
787
1017
|
// src/ui/hooks/useAlerts.ts
|
|
788
|
-
import { useState as
|
|
1018
|
+
import { useState as useState6, useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
789
1019
|
|
|
790
1020
|
// src/notifications.ts
|
|
791
1021
|
import { exec as exec2 } from "child_process";
|
|
@@ -853,7 +1083,7 @@ var AlertLogger = class {
|
|
|
853
1083
|
// src/ui/hooks/useAlerts.ts
|
|
854
1084
|
var MAX_ALERTS = 100;
|
|
855
1085
|
var useAlerts = (enabled, alertLevel, allUsers, config) => {
|
|
856
|
-
const [alerts, setAlerts] =
|
|
1086
|
+
const [alerts, setAlerts] = useState6([]);
|
|
857
1087
|
const engineRef = useRef3(new SecurityEngine(alertLevel));
|
|
858
1088
|
const watcherRef = useRef3(null);
|
|
859
1089
|
const loggerRef = useRef3(null);
|
|
@@ -893,10 +1123,10 @@ var useAlerts = (enabled, alertLevel, allUsers, config) => {
|
|
|
893
1123
|
};
|
|
894
1124
|
|
|
895
1125
|
// src/ui/hooks/useTextInput.ts
|
|
896
|
-
import { useState as
|
|
897
|
-
var useTextInput = (onConfirm) => {
|
|
898
|
-
const [value, setValue] =
|
|
899
|
-
const [isActive, setIsActive] =
|
|
1126
|
+
import { useState as useState7, useCallback as useCallback2 } from "react";
|
|
1127
|
+
var useTextInput = (onConfirm, onCancel) => {
|
|
1128
|
+
const [value, setValue] = useState7("");
|
|
1129
|
+
const [isActive, setIsActive] = useState7(false);
|
|
900
1130
|
const start = useCallback2((initial = "") => {
|
|
901
1131
|
setValue(initial);
|
|
902
1132
|
setIsActive(true);
|
|
@@ -904,7 +1134,8 @@ var useTextInput = (onConfirm) => {
|
|
|
904
1134
|
const cancel = useCallback2(() => {
|
|
905
1135
|
setValue("");
|
|
906
1136
|
setIsActive(false);
|
|
907
|
-
|
|
1137
|
+
onCancel?.();
|
|
1138
|
+
}, [onCancel]);
|
|
908
1139
|
const confirm = useCallback2(() => {
|
|
909
1140
|
const result = value;
|
|
910
1141
|
setIsActive(false);
|
|
@@ -924,7 +1155,13 @@ var useTextInput = (onConfirm) => {
|
|
|
924
1155
|
return true;
|
|
925
1156
|
}
|
|
926
1157
|
if (key.backspace || key.delete) {
|
|
927
|
-
setValue((v) =>
|
|
1158
|
+
setValue((v) => {
|
|
1159
|
+
if (v.length === 0) {
|
|
1160
|
+
cancel();
|
|
1161
|
+
return v;
|
|
1162
|
+
}
|
|
1163
|
+
return v.slice(0, -1);
|
|
1164
|
+
});
|
|
928
1165
|
return true;
|
|
929
1166
|
}
|
|
930
1167
|
if (input && input.length === 1 && input >= " ") {
|
|
@@ -939,42 +1176,56 @@ var useTextInput = (onConfirm) => {
|
|
|
939
1176
|
};
|
|
940
1177
|
|
|
941
1178
|
// src/ui/App.tsx
|
|
942
|
-
import { jsx as
|
|
1179
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
943
1180
|
var matchKey = (binding, input, key) => {
|
|
944
1181
|
if (binding === "tab") return Boolean(key.tab);
|
|
945
1182
|
if (binding === "shift+tab") return Boolean(key.shift && key.tab);
|
|
946
1183
|
if (binding === "enter") return Boolean(key.return);
|
|
947
1184
|
return input === binding;
|
|
948
1185
|
};
|
|
949
|
-
var App = ({ options, config, version, firstRun }) => {
|
|
1186
|
+
var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
950
1187
|
const { exit } = useApp();
|
|
951
|
-
const { stdout } =
|
|
1188
|
+
const { stdout } = useStdout2();
|
|
952
1189
|
const termHeight = stdout?.rows ?? 40;
|
|
953
|
-
const
|
|
954
|
-
const
|
|
955
|
-
const [
|
|
956
|
-
const [
|
|
957
|
-
const [
|
|
958
|
-
const [
|
|
959
|
-
const [
|
|
960
|
-
const [
|
|
961
|
-
const [
|
|
962
|
-
const
|
|
1190
|
+
const [liveConfig, setLiveConfig] = useState8(initialConfig);
|
|
1191
|
+
const kb = liveConfig.keybindings;
|
|
1192
|
+
const [activePanel, setActivePanel] = useState8("sessions");
|
|
1193
|
+
const [activityScroll, setActivityScroll] = useState8(0);
|
|
1194
|
+
const [inputMode, setInputMode] = useState8("normal");
|
|
1195
|
+
const [showSetup, setShowSetup] = useState8(firstRun);
|
|
1196
|
+
const [filter, setFilter] = useState8("");
|
|
1197
|
+
const [updateInfo, setUpdateInfo] = useState8(null);
|
|
1198
|
+
const [updateStatus, setUpdateStatus] = useState8("");
|
|
1199
|
+
const [showDetail, setShowDetail] = useState8(false);
|
|
1200
|
+
const [showSettings, setShowSettings] = useState8(false);
|
|
1201
|
+
const { sessions, selectedSession, selectedIndex, selectNext, selectPrev, refresh } = useSessions(
|
|
963
1202
|
options.allUsers,
|
|
964
1203
|
filter || void 0
|
|
965
1204
|
);
|
|
966
1205
|
const events = useActivityStream(selectedSession, options.allUsers);
|
|
967
|
-
const { alerts } = useAlerts(!options.noSecurity, options.alertLevel, options.allUsers,
|
|
968
|
-
const nicknameInput = useTextInput(
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1206
|
+
const { alerts } = useAlerts(!options.noSecurity, options.alertLevel, options.allUsers, liveConfig);
|
|
1207
|
+
const nicknameInput = useTextInput(
|
|
1208
|
+
(value) => {
|
|
1209
|
+
if (selectedSession && value.trim()) {
|
|
1210
|
+
setNickname(selectedSession.sessionId, value.trim());
|
|
1211
|
+
refresh();
|
|
1212
|
+
}
|
|
1213
|
+
setInputMode("normal");
|
|
1214
|
+
},
|
|
1215
|
+
() => setInputMode("normal")
|
|
1216
|
+
);
|
|
1217
|
+
const filterInput = useTextInput(
|
|
1218
|
+
(value) => {
|
|
1219
|
+
setFilter(value);
|
|
1220
|
+
setInputMode("normal");
|
|
1221
|
+
},
|
|
1222
|
+
() => {
|
|
1223
|
+
setFilter("");
|
|
1224
|
+
setInputMode("normal");
|
|
1225
|
+
}
|
|
1226
|
+
);
|
|
976
1227
|
useEffect5(() => {
|
|
977
|
-
if (options.noUpdates || !
|
|
1228
|
+
if (options.noUpdates || !liveConfig.updates.checkOnLaunch) return;
|
|
978
1229
|
try {
|
|
979
1230
|
const i = checkForUpdate();
|
|
980
1231
|
if (i.available) setUpdateInfo(i);
|
|
@@ -986,7 +1237,7 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
986
1237
|
if (i.available) setUpdateInfo(i);
|
|
987
1238
|
} catch {
|
|
988
1239
|
}
|
|
989
|
-
},
|
|
1240
|
+
}, liveConfig.updates.checkInterval);
|
|
990
1241
|
return () => clearInterval(iv);
|
|
991
1242
|
}, []);
|
|
992
1243
|
const alertHeight = options.noSecurity ? 0 : 6;
|
|
@@ -996,9 +1247,14 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
996
1247
|
useEffect5(() => {
|
|
997
1248
|
setActivityScroll(0);
|
|
998
1249
|
}, [selectedSession?.sessionId]);
|
|
1250
|
+
const handleSettingsClose = useCallback3((updatedConfig) => {
|
|
1251
|
+
setLiveConfig(updatedConfig);
|
|
1252
|
+
saveConfig(updatedConfig);
|
|
1253
|
+
setShowSettings(false);
|
|
1254
|
+
}, []);
|
|
999
1255
|
const handleSetupComplete = useCallback3(
|
|
1000
1256
|
(results) => {
|
|
1001
|
-
const nc = { ...
|
|
1257
|
+
const nc = { ...liveConfig };
|
|
1002
1258
|
const [hc, mc] = results;
|
|
1003
1259
|
if (hc === "yes") {
|
|
1004
1260
|
try {
|
|
@@ -1017,13 +1273,13 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
1017
1273
|
saveConfig(nc);
|
|
1018
1274
|
setShowSetup(false);
|
|
1019
1275
|
},
|
|
1020
|
-
[
|
|
1276
|
+
[liveConfig]
|
|
1021
1277
|
);
|
|
1022
1278
|
const switchPanel = useCallback3((_dir) => {
|
|
1023
1279
|
setActivePanel((p) => p === "sessions" ? "activity" : "sessions");
|
|
1024
1280
|
}, []);
|
|
1025
|
-
|
|
1026
|
-
if (showSetup) return;
|
|
1281
|
+
useInput3((input, key) => {
|
|
1282
|
+
if (showSetup || showSettings) return;
|
|
1027
1283
|
if (inputMode === "nickname") {
|
|
1028
1284
|
nicknameInput.handleInput(input, key);
|
|
1029
1285
|
return;
|
|
@@ -1067,6 +1323,7 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
1067
1323
|
}
|
|
1068
1324
|
if (matchKey(kb.clearNickname, input, key) && selectedSession) {
|
|
1069
1325
|
clearNickname(selectedSession.sessionId);
|
|
1326
|
+
refresh();
|
|
1070
1327
|
return;
|
|
1071
1328
|
}
|
|
1072
1329
|
if (matchKey(kb.filter, input, key)) {
|
|
@@ -1078,6 +1335,10 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
1078
1335
|
setFilter("");
|
|
1079
1336
|
return;
|
|
1080
1337
|
}
|
|
1338
|
+
if (matchKey(kb.settings, input, key)) {
|
|
1339
|
+
setShowSettings(true);
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1081
1342
|
if (matchKey(kb.update, input, key) && updateInfo?.available) {
|
|
1082
1343
|
setUpdateStatus("updating...");
|
|
1083
1344
|
installUpdate().then(() => setUpdateStatus(`updated to v${updateInfo.latest} \u2014 restart to apply`)).catch(() => setUpdateStatus("update failed"));
|
|
@@ -1096,12 +1357,12 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
1096
1357
|
});
|
|
1097
1358
|
if (showSetup) {
|
|
1098
1359
|
const steps = [];
|
|
1099
|
-
if (
|
|
1360
|
+
if (liveConfig.prompts.hook === "pending")
|
|
1100
1361
|
steps.push({
|
|
1101
1362
|
title: "Install Claude Code hook?",
|
|
1102
1363
|
description: "Adds a PostToolUse hook that blocks prompt injection attempts in real-time."
|
|
1103
1364
|
});
|
|
1104
|
-
if (
|
|
1365
|
+
if (liveConfig.prompts.mcp === "pending")
|
|
1105
1366
|
steps.push({
|
|
1106
1367
|
title: "Install MCP server?",
|
|
1107
1368
|
description: "Registers agenttop as an MCP server so Claude Code can query session status and alerts."
|
|
@@ -1110,9 +1371,12 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
1110
1371
|
setShowSetup(false);
|
|
1111
1372
|
return null;
|
|
1112
1373
|
}
|
|
1113
|
-
return /* @__PURE__ */
|
|
1374
|
+
return /* @__PURE__ */ jsx9(SetupModal, { steps, onComplete: handleSetupComplete });
|
|
1375
|
+
}
|
|
1376
|
+
if (showSettings) {
|
|
1377
|
+
return /* @__PURE__ */ jsx9(SettingsMenu, { config: liveConfig, onClose: handleSettingsClose });
|
|
1114
1378
|
}
|
|
1115
|
-
const rightPanel = showDetail && selectedSession ? /* @__PURE__ */
|
|
1379
|
+
const rightPanel = showDetail && selectedSession ? /* @__PURE__ */ jsx9(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx9(
|
|
1116
1380
|
ActivityFeed,
|
|
1117
1381
|
{
|
|
1118
1382
|
events,
|
|
@@ -1122,10 +1386,10 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
1122
1386
|
scrollOffset: activityScroll
|
|
1123
1387
|
}
|
|
1124
1388
|
);
|
|
1125
|
-
return /* @__PURE__ */
|
|
1126
|
-
/* @__PURE__ */
|
|
1127
|
-
/* @__PURE__ */
|
|
1128
|
-
/* @__PURE__ */
|
|
1389
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", height: termHeight, children: [
|
|
1390
|
+
/* @__PURE__ */ jsx9(StatusBar, { sessionCount: sessions.length, alertCount: alerts.length, version, updateInfo }),
|
|
1391
|
+
/* @__PURE__ */ jsxs9(Box9, { flexGrow: 1, height: mainHeight, children: [
|
|
1392
|
+
/* @__PURE__ */ jsx9(
|
|
1129
1393
|
SessionList,
|
|
1130
1394
|
{
|
|
1131
1395
|
sessions,
|
|
@@ -1136,18 +1400,18 @@ var App = ({ options, config, version, firstRun }) => {
|
|
|
1136
1400
|
),
|
|
1137
1401
|
rightPanel
|
|
1138
1402
|
] }),
|
|
1139
|
-
!options.noSecurity && /* @__PURE__ */
|
|
1140
|
-
inputMode === "nickname" && /* @__PURE__ */
|
|
1141
|
-
/* @__PURE__ */
|
|
1142
|
-
/* @__PURE__ */
|
|
1143
|
-
/* @__PURE__ */
|
|
1403
|
+
!options.noSecurity && /* @__PURE__ */ jsx9(AlertBar, { alerts }),
|
|
1404
|
+
inputMode === "nickname" && /* @__PURE__ */ jsxs9(Box9, { paddingX: 1, children: [
|
|
1405
|
+
/* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "nickname: " }),
|
|
1406
|
+
/* @__PURE__ */ jsx9(Text9, { color: colors.bright, children: nicknameInput.value }),
|
|
1407
|
+
/* @__PURE__ */ jsx9(Text9, { color: colors.muted, children: "_" })
|
|
1144
1408
|
] }),
|
|
1145
|
-
inputMode === "filter" && /* @__PURE__ */
|
|
1146
|
-
/* @__PURE__ */
|
|
1147
|
-
/* @__PURE__ */
|
|
1148
|
-
/* @__PURE__ */
|
|
1409
|
+
inputMode === "filter" && /* @__PURE__ */ jsxs9(Box9, { paddingX: 1, children: [
|
|
1410
|
+
/* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "/" }),
|
|
1411
|
+
/* @__PURE__ */ jsx9(Text9, { color: colors.bright, children: filterInput.value }),
|
|
1412
|
+
/* @__PURE__ */ jsx9(Text9, { color: colors.muted, children: "_" })
|
|
1149
1413
|
] }),
|
|
1150
|
-
inputMode === "normal" && /* @__PURE__ */
|
|
1414
|
+
inputMode === "normal" && /* @__PURE__ */ jsx9(FooterBar, { keybindings: kb, updateStatus })
|
|
1151
1415
|
] });
|
|
1152
1416
|
};
|
|
1153
1417
|
|
|
@@ -1384,7 +1648,7 @@ var main = () => {
|
|
|
1384
1648
|
if (options.noUpdates) config.updates.checkOnLaunch = false;
|
|
1385
1649
|
if (options.noSecurity) config.security.enabled = false;
|
|
1386
1650
|
if (firstRun) saveConfig(config);
|
|
1387
|
-
render(
|
|
1651
|
+
render(React10.createElement(App, { options, config, version: VERSION, firstRun }));
|
|
1388
1652
|
};
|
|
1389
1653
|
main();
|
|
1390
1654
|
//# sourceMappingURL=index.js.map
|