agenttop 0.10.7 → 0.11.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/README.md +158 -20
- package/dist/{chunk-CXXCDFJ5.js → chunk-LPXME2WB.js} +570 -53
- package/dist/chunk-LPXME2WB.js.map +1 -0
- package/dist/index.js +1085 -311
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-CXXCDFJ5.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
STATUS_PRIORITY,
|
|
3
4
|
SecurityEngine,
|
|
4
5
|
Watcher,
|
|
5
6
|
archiveSession,
|
|
@@ -7,30 +8,34 @@ import {
|
|
|
7
8
|
deleteSessionFiles,
|
|
8
9
|
discoverSessions,
|
|
9
10
|
getArchived,
|
|
11
|
+
getClaudeProcessesAsync,
|
|
10
12
|
getNicknames,
|
|
11
13
|
getProjectsDirs,
|
|
12
14
|
getTaskDirs,
|
|
13
15
|
isFirstRun,
|
|
14
16
|
loadConfig,
|
|
17
|
+
movePinned,
|
|
18
|
+
pinSession,
|
|
15
19
|
purgeExpiredArchives,
|
|
16
20
|
resolveAlertLogPath,
|
|
17
21
|
rotateLogFile,
|
|
18
22
|
saveConfig,
|
|
19
23
|
setNickname,
|
|
20
24
|
startMcpServer,
|
|
21
|
-
unarchiveSession
|
|
22
|
-
|
|
25
|
+
unarchiveSession,
|
|
26
|
+
unpinSession
|
|
27
|
+
} from "./chunk-LPXME2WB.js";
|
|
23
28
|
|
|
24
29
|
// src/index.tsx
|
|
25
30
|
import { readFileSync as readFileSync3 } from "fs";
|
|
26
31
|
import { join as join5, dirname as dirname4 } from "path";
|
|
27
32
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
28
|
-
import
|
|
33
|
+
import React19 from "react";
|
|
29
34
|
import { render } from "ink";
|
|
30
35
|
|
|
31
36
|
// src/ui/App.tsx
|
|
32
|
-
import { useState as
|
|
33
|
-
import { Box as
|
|
37
|
+
import { useState as useState18, useEffect as useEffect10, useCallback as useCallback7 } from "react";
|
|
38
|
+
import { Box as Box18, Text as Text17, useApp, useStdout as useStdout4 } from "ink";
|
|
34
39
|
|
|
35
40
|
// src/config/themes.ts
|
|
36
41
|
var COLOR_KEYS = [
|
|
@@ -45,7 +50,9 @@ var COLOR_KEYS = [
|
|
|
45
50
|
"bright",
|
|
46
51
|
"border",
|
|
47
52
|
"selected",
|
|
48
|
-
"header"
|
|
53
|
+
"header",
|
|
54
|
+
"waiting",
|
|
55
|
+
"stale"
|
|
49
56
|
];
|
|
50
57
|
var TOOL_COLOR_KEYS = [
|
|
51
58
|
"Bash",
|
|
@@ -82,11 +89,28 @@ var fromTuple = ([
|
|
|
82
89
|
bright,
|
|
83
90
|
border,
|
|
84
91
|
selected,
|
|
85
|
-
header
|
|
92
|
+
header,
|
|
93
|
+
waiting,
|
|
94
|
+
stale
|
|
86
95
|
]) => ({
|
|
87
96
|
name,
|
|
88
97
|
builtin: true,
|
|
89
|
-
colors: {
|
|
98
|
+
colors: {
|
|
99
|
+
primary,
|
|
100
|
+
secondary,
|
|
101
|
+
accent,
|
|
102
|
+
warning,
|
|
103
|
+
error,
|
|
104
|
+
critical,
|
|
105
|
+
muted,
|
|
106
|
+
text,
|
|
107
|
+
bright,
|
|
108
|
+
border,
|
|
109
|
+
selected,
|
|
110
|
+
header,
|
|
111
|
+
waiting,
|
|
112
|
+
stale
|
|
113
|
+
},
|
|
90
114
|
toolColors: deriveToolColors({
|
|
91
115
|
primary,
|
|
92
116
|
secondary,
|
|
@@ -99,7 +123,9 @@ var fromTuple = ([
|
|
|
99
123
|
bright,
|
|
100
124
|
border,
|
|
101
125
|
selected,
|
|
102
|
-
header
|
|
126
|
+
header,
|
|
127
|
+
waiting,
|
|
128
|
+
stale
|
|
103
129
|
})
|
|
104
130
|
});
|
|
105
131
|
var PRESETS = [
|
|
@@ -116,7 +142,9 @@ var PRESETS = [
|
|
|
116
142
|
"#FFFFFF",
|
|
117
143
|
"#3E4451",
|
|
118
144
|
"#2C313A",
|
|
119
|
-
"#61AFEF"
|
|
145
|
+
"#61AFEF",
|
|
146
|
+
"#E5C07B",
|
|
147
|
+
"#D19A66"
|
|
120
148
|
],
|
|
121
149
|
[
|
|
122
150
|
"dracula",
|
|
@@ -131,7 +159,9 @@ var PRESETS = [
|
|
|
131
159
|
"#FFFFFF",
|
|
132
160
|
"#44475A",
|
|
133
161
|
"#383A59",
|
|
134
|
-
"#BD93F9"
|
|
162
|
+
"#BD93F9",
|
|
163
|
+
"#F1FA8C",
|
|
164
|
+
"#FFB86C"
|
|
135
165
|
],
|
|
136
166
|
[
|
|
137
167
|
"monokai-pro",
|
|
@@ -146,7 +176,9 @@ var PRESETS = [
|
|
|
146
176
|
"#FFFFFF",
|
|
147
177
|
"#403E41",
|
|
148
178
|
"#2D2A2E",
|
|
149
|
-
"#78DCE8"
|
|
179
|
+
"#78DCE8",
|
|
180
|
+
"#FFD866",
|
|
181
|
+
"#FC9867"
|
|
150
182
|
],
|
|
151
183
|
[
|
|
152
184
|
"solarized-dark",
|
|
@@ -161,7 +193,9 @@ var PRESETS = [
|
|
|
161
193
|
"#FDF6E3",
|
|
162
194
|
"#073642",
|
|
163
195
|
"#002B36",
|
|
164
|
-
"#268BD2"
|
|
196
|
+
"#268BD2",
|
|
197
|
+
"#B58900",
|
|
198
|
+
"#CB4B16"
|
|
165
199
|
],
|
|
166
200
|
[
|
|
167
201
|
"solarized-light",
|
|
@@ -176,7 +210,9 @@ var PRESETS = [
|
|
|
176
210
|
"#002B36",
|
|
177
211
|
"#EEE8D5",
|
|
178
212
|
"#FDF6E3",
|
|
179
|
-
"#268BD2"
|
|
213
|
+
"#268BD2",
|
|
214
|
+
"#B58900",
|
|
215
|
+
"#CB4B16"
|
|
180
216
|
],
|
|
181
217
|
[
|
|
182
218
|
"nord",
|
|
@@ -191,7 +227,9 @@ var PRESETS = [
|
|
|
191
227
|
"#ECEFF4",
|
|
192
228
|
"#3B4252",
|
|
193
229
|
"#2E3440",
|
|
194
|
-
"#88C0D0"
|
|
230
|
+
"#88C0D0",
|
|
231
|
+
"#EBCB8B",
|
|
232
|
+
"#D08770"
|
|
195
233
|
],
|
|
196
234
|
[
|
|
197
235
|
"gruvbox-dark",
|
|
@@ -206,7 +244,9 @@ var PRESETS = [
|
|
|
206
244
|
"#FBF1C7",
|
|
207
245
|
"#3C3836",
|
|
208
246
|
"#282828",
|
|
209
|
-
"#83A598"
|
|
247
|
+
"#83A598",
|
|
248
|
+
"#FABD2F",
|
|
249
|
+
"#FE8019"
|
|
210
250
|
],
|
|
211
251
|
[
|
|
212
252
|
"tokyo-night",
|
|
@@ -221,7 +261,9 @@ var PRESETS = [
|
|
|
221
261
|
"#C0CAF5",
|
|
222
262
|
"#292E42",
|
|
223
263
|
"#1A1B26",
|
|
224
|
-
"#7AA2F7"
|
|
264
|
+
"#7AA2F7",
|
|
265
|
+
"#E0AF68",
|
|
266
|
+
"#FF9E64"
|
|
225
267
|
],
|
|
226
268
|
[
|
|
227
269
|
"catppuccin-mocha",
|
|
@@ -236,7 +278,9 @@ var PRESETS = [
|
|
|
236
278
|
"#FFFFFF",
|
|
237
279
|
"#313244",
|
|
238
280
|
"#1E1E2E",
|
|
239
|
-
"#89B4FA"
|
|
281
|
+
"#89B4FA",
|
|
282
|
+
"#F9E2AF",
|
|
283
|
+
"#FAB387"
|
|
240
284
|
],
|
|
241
285
|
[
|
|
242
286
|
"catppuccin-latte",
|
|
@@ -251,7 +295,9 @@ var PRESETS = [
|
|
|
251
295
|
"#11111B",
|
|
252
296
|
"#E6E9EF",
|
|
253
297
|
"#EFF1F5",
|
|
254
|
-
"#1E66F5"
|
|
298
|
+
"#1E66F5",
|
|
299
|
+
"#DF8E1D",
|
|
300
|
+
"#FE640B"
|
|
255
301
|
],
|
|
256
302
|
[
|
|
257
303
|
"rose-pine",
|
|
@@ -266,7 +312,9 @@ var PRESETS = [
|
|
|
266
312
|
"#E0DEF4",
|
|
267
313
|
"#26233A",
|
|
268
314
|
"#191724",
|
|
269
|
-
"#9CCFD8"
|
|
315
|
+
"#9CCFD8",
|
|
316
|
+
"#F6C177",
|
|
317
|
+
"#EA9D34"
|
|
270
318
|
],
|
|
271
319
|
[
|
|
272
320
|
"rose-pine-moon",
|
|
@@ -281,7 +329,9 @@ var PRESETS = [
|
|
|
281
329
|
"#E0DEF4",
|
|
282
330
|
"#2A273F",
|
|
283
331
|
"#232136",
|
|
284
|
-
"#9CCFD8"
|
|
332
|
+
"#9CCFD8",
|
|
333
|
+
"#F6C177",
|
|
334
|
+
"#EA9D34"
|
|
285
335
|
],
|
|
286
336
|
[
|
|
287
337
|
"pastel-dark",
|
|
@@ -296,7 +346,9 @@ var PRESETS = [
|
|
|
296
346
|
"#FFFFFF",
|
|
297
347
|
"#3A3A4A",
|
|
298
348
|
"#2B2B3A",
|
|
299
|
-
"#89CFF0"
|
|
349
|
+
"#89CFF0",
|
|
350
|
+
"#FFD580",
|
|
351
|
+
"#FFAA5E"
|
|
300
352
|
],
|
|
301
353
|
[
|
|
302
354
|
"kanagawa",
|
|
@@ -311,7 +363,9 @@ var PRESETS = [
|
|
|
311
363
|
"#FFFFFF",
|
|
312
364
|
"#2A2A37",
|
|
313
365
|
"#1F1F28",
|
|
314
|
-
"#7E9CD8"
|
|
366
|
+
"#7E9CD8",
|
|
367
|
+
"#E6C384",
|
|
368
|
+
"#FFA066"
|
|
315
369
|
],
|
|
316
370
|
[
|
|
317
371
|
"everforest",
|
|
@@ -326,7 +380,213 @@ var PRESETS = [
|
|
|
326
380
|
"#FFFFFF",
|
|
327
381
|
"#374145",
|
|
328
382
|
"#2D353B",
|
|
329
|
-
"#7FBBB3"
|
|
383
|
+
"#7FBBB3",
|
|
384
|
+
"#DBBC7F",
|
|
385
|
+
"#E69875"
|
|
386
|
+
],
|
|
387
|
+
[
|
|
388
|
+
"hi-feline",
|
|
389
|
+
"#FF6B8A",
|
|
390
|
+
"#FFB3C6",
|
|
391
|
+
"#FF1744",
|
|
392
|
+
"#FFE082",
|
|
393
|
+
"#FF5252",
|
|
394
|
+
"#FF0000",
|
|
395
|
+
"#C48B9F",
|
|
396
|
+
"#FFE4EC",
|
|
397
|
+
"#FFFFFF",
|
|
398
|
+
"#4A2030",
|
|
399
|
+
"#3A1525",
|
|
400
|
+
"#FF6B8A",
|
|
401
|
+
"#FFE082",
|
|
402
|
+
"#FFA726"
|
|
403
|
+
],
|
|
404
|
+
[
|
|
405
|
+
"plumber-bros",
|
|
406
|
+
"#4CAF50",
|
|
407
|
+
"#F44336",
|
|
408
|
+
"#2196F3",
|
|
409
|
+
"#FFD700",
|
|
410
|
+
"#FF5722",
|
|
411
|
+
"#FF0000",
|
|
412
|
+
"#795548",
|
|
413
|
+
"#EFEBE9",
|
|
414
|
+
"#FFFFFF",
|
|
415
|
+
"#1B5E20",
|
|
416
|
+
"#0D3010",
|
|
417
|
+
"#4CAF50",
|
|
418
|
+
"#FFD700",
|
|
419
|
+
"#FF9800"
|
|
420
|
+
],
|
|
421
|
+
[
|
|
422
|
+
"hedgehog-speed",
|
|
423
|
+
"#1565C0",
|
|
424
|
+
"#FFD700",
|
|
425
|
+
"#4CAF50",
|
|
426
|
+
"#FFEB3B",
|
|
427
|
+
"#F44336",
|
|
428
|
+
"#FF0000",
|
|
429
|
+
"#5C6BC0",
|
|
430
|
+
"#E8EAF6",
|
|
431
|
+
"#FFFFFF",
|
|
432
|
+
"#0D47A1",
|
|
433
|
+
"#0A2E6E",
|
|
434
|
+
"#1565C0",
|
|
435
|
+
"#FFEB3B",
|
|
436
|
+
"#FF9800"
|
|
437
|
+
],
|
|
438
|
+
[
|
|
439
|
+
"block-craft",
|
|
440
|
+
"#4CAF50",
|
|
441
|
+
"#8D6E63",
|
|
442
|
+
"#795548",
|
|
443
|
+
"#FFEB3B",
|
|
444
|
+
"#F44336",
|
|
445
|
+
"#FF0000",
|
|
446
|
+
"#78909C",
|
|
447
|
+
"#BCAAA4",
|
|
448
|
+
"#FFFFFF",
|
|
449
|
+
"#33691E",
|
|
450
|
+
"#1B4400",
|
|
451
|
+
"#4CAF50",
|
|
452
|
+
"#FFEB3B",
|
|
453
|
+
"#FF9800"
|
|
454
|
+
],
|
|
455
|
+
[
|
|
456
|
+
"galaxy-conflicts",
|
|
457
|
+
"#F44336",
|
|
458
|
+
"#2196F3",
|
|
459
|
+
"#9C27B0",
|
|
460
|
+
"#FFD54F",
|
|
461
|
+
"#FF1744",
|
|
462
|
+
"#FF0000",
|
|
463
|
+
"#546E7A",
|
|
464
|
+
"#ECEFF1",
|
|
465
|
+
"#FFFFFF",
|
|
466
|
+
"#1A1A2E",
|
|
467
|
+
"#0D0D1A",
|
|
468
|
+
"#F44336",
|
|
469
|
+
"#FFD54F",
|
|
470
|
+
"#FF6D00"
|
|
471
|
+
],
|
|
472
|
+
[
|
|
473
|
+
"pocket-creatures",
|
|
474
|
+
"#F44336",
|
|
475
|
+
"#FFFFFF",
|
|
476
|
+
"#FFEB3B",
|
|
477
|
+
"#FFC107",
|
|
478
|
+
"#E53935",
|
|
479
|
+
"#FF0000",
|
|
480
|
+
"#90A4AE",
|
|
481
|
+
"#ECEFF1",
|
|
482
|
+
"#FFFFFF",
|
|
483
|
+
"#B71C1C",
|
|
484
|
+
"#7F0000",
|
|
485
|
+
"#F44336",
|
|
486
|
+
"#FFC107",
|
|
487
|
+
"#FF9800"
|
|
488
|
+
],
|
|
489
|
+
[
|
|
490
|
+
"brick-wizard",
|
|
491
|
+
"#7B1FA2",
|
|
492
|
+
"#FFD700",
|
|
493
|
+
"#880E4F",
|
|
494
|
+
"#FFC107",
|
|
495
|
+
"#F44336",
|
|
496
|
+
"#FF0000",
|
|
497
|
+
"#6A1B9A",
|
|
498
|
+
"#E1BEE7",
|
|
499
|
+
"#FFFFFF",
|
|
500
|
+
"#311B92",
|
|
501
|
+
"#1A0A52",
|
|
502
|
+
"#7B1FA2",
|
|
503
|
+
"#FFC107",
|
|
504
|
+
"#FF9800"
|
|
505
|
+
],
|
|
506
|
+
[
|
|
507
|
+
"caped-knight",
|
|
508
|
+
"#616161",
|
|
509
|
+
"#FDD835",
|
|
510
|
+
"#424242",
|
|
511
|
+
"#FFC107",
|
|
512
|
+
"#F44336",
|
|
513
|
+
"#FF0000",
|
|
514
|
+
"#757575",
|
|
515
|
+
"#E0E0E0",
|
|
516
|
+
"#FFFFFF",
|
|
517
|
+
"#212121",
|
|
518
|
+
"#0A0A0A",
|
|
519
|
+
"#616161",
|
|
520
|
+
"#FFC107",
|
|
521
|
+
"#FF9800"
|
|
522
|
+
],
|
|
523
|
+
[
|
|
524
|
+
"web-crawler",
|
|
525
|
+
"#F44336",
|
|
526
|
+
"#1565C0",
|
|
527
|
+
"#FFFFFF",
|
|
528
|
+
"#FFEB3B",
|
|
529
|
+
"#D50000",
|
|
530
|
+
"#FF0000",
|
|
531
|
+
"#78909C",
|
|
532
|
+
"#E3F2FD",
|
|
533
|
+
"#FFFFFF",
|
|
534
|
+
"#B71C1C",
|
|
535
|
+
"#7F0000",
|
|
536
|
+
"#F44336",
|
|
537
|
+
"#FFEB3B",
|
|
538
|
+
"#FF9800"
|
|
539
|
+
],
|
|
540
|
+
[
|
|
541
|
+
"frozen-kingdom",
|
|
542
|
+
"#81D4FA",
|
|
543
|
+
"#CE93D8",
|
|
544
|
+
"#B3E5FC",
|
|
545
|
+
"#FFF9C4",
|
|
546
|
+
"#EF9A9A",
|
|
547
|
+
"#FF0000",
|
|
548
|
+
"#90CAF9",
|
|
549
|
+
"#E1F5FE",
|
|
550
|
+
"#FFFFFF",
|
|
551
|
+
"#1A237E",
|
|
552
|
+
"#0D1252",
|
|
553
|
+
"#81D4FA",
|
|
554
|
+
"#FFF9C4",
|
|
555
|
+
"#FFCC80"
|
|
556
|
+
],
|
|
557
|
+
[
|
|
558
|
+
"coral-reef",
|
|
559
|
+
"#FF7043",
|
|
560
|
+
"#29B6F6",
|
|
561
|
+
"#AB47BC",
|
|
562
|
+
"#FFEE58",
|
|
563
|
+
"#EF5350",
|
|
564
|
+
"#FF0000",
|
|
565
|
+
"#4DB6AC",
|
|
566
|
+
"#E0F7FA",
|
|
567
|
+
"#FFFFFF",
|
|
568
|
+
"#BF360C",
|
|
569
|
+
"#7F2008",
|
|
570
|
+
"#FF7043",
|
|
571
|
+
"#FFEE58",
|
|
572
|
+
"#FFA726"
|
|
573
|
+
],
|
|
574
|
+
[
|
|
575
|
+
"toy-ranch",
|
|
576
|
+
"#8D6E63",
|
|
577
|
+
"#7CB342",
|
|
578
|
+
"#7E57C2",
|
|
579
|
+
"#FFEE58",
|
|
580
|
+
"#EF5350",
|
|
581
|
+
"#FF0000",
|
|
582
|
+
"#90A4AE",
|
|
583
|
+
"#EFEBE9",
|
|
584
|
+
"#FFFFFF",
|
|
585
|
+
"#4E342E",
|
|
586
|
+
"#2E1C15",
|
|
587
|
+
"#8D6E63",
|
|
588
|
+
"#FFEE58",
|
|
589
|
+
"#FFA726"
|
|
330
590
|
]
|
|
331
591
|
];
|
|
332
592
|
var BUILTIN_THEMES = PRESETS.map(fromTuple);
|
|
@@ -350,7 +610,7 @@ var deriveSeverityColors = (c) => ({
|
|
|
350
610
|
});
|
|
351
611
|
|
|
352
612
|
// src/updates.ts
|
|
353
|
-
import { execFile,
|
|
613
|
+
import { execFile, spawn } from "child_process";
|
|
354
614
|
import { readFile } from "fs/promises";
|
|
355
615
|
import { join, dirname } from "path";
|
|
356
616
|
import { fileURLToPath } from "url";
|
|
@@ -366,7 +626,7 @@ var getPackageVersion = async () => {
|
|
|
366
626
|
};
|
|
367
627
|
var getNpmPath = () => {
|
|
368
628
|
const nodeDir = dirname(process.execPath);
|
|
369
|
-
return join(nodeDir, "npm");
|
|
629
|
+
return join(nodeDir, process.platform === "win32" ? "npm.cmd" : "npm");
|
|
370
630
|
};
|
|
371
631
|
var checkForUpdate = () => new Promise((resolve) => {
|
|
372
632
|
const npm = getNpmPath();
|
|
@@ -388,7 +648,7 @@ var checkForUpdate = () => new Promise((resolve) => {
|
|
|
388
648
|
var installUpdate = () => {
|
|
389
649
|
const npm = getNpmPath();
|
|
390
650
|
return new Promise((resolve, reject) => {
|
|
391
|
-
|
|
651
|
+
execFile(npm, ["install", "-g", "agenttop@latest"], { timeout: 6e4 }, (err, stdout) => {
|
|
392
652
|
if (err) {
|
|
393
653
|
reject(err);
|
|
394
654
|
} else {
|
|
@@ -429,12 +689,15 @@ var colors = {
|
|
|
429
689
|
warning: "#E5C07B",
|
|
430
690
|
error: "#E06C75",
|
|
431
691
|
critical: "#FF0000",
|
|
692
|
+
success: "#98C379",
|
|
432
693
|
muted: "#5C6370",
|
|
433
694
|
text: "#ABB2BF",
|
|
434
695
|
bright: "#FFFFFF",
|
|
435
696
|
border: "#3E4451",
|
|
436
697
|
selected: "#2C313A",
|
|
437
|
-
header: "#61AFEF"
|
|
698
|
+
header: "#61AFEF",
|
|
699
|
+
waiting: "#E5C07B",
|
|
700
|
+
stale: "#D19A66"
|
|
438
701
|
};
|
|
439
702
|
var severityColors = {
|
|
440
703
|
info: colors.muted,
|
|
@@ -456,6 +719,7 @@ var toolColors = {
|
|
|
456
719
|
var getToolColor = (toolName) => toolColors[toolName] || colors.text;
|
|
457
720
|
var applyTheme = (theme) => {
|
|
458
721
|
Object.assign(colors, theme.colors);
|
|
722
|
+
colors.success = theme.colors.secondary;
|
|
459
723
|
Object.assign(toolColors, theme.toolColors);
|
|
460
724
|
Object.assign(severityColors, deriveSeverityColors(theme.colors));
|
|
461
725
|
};
|
|
@@ -496,18 +760,32 @@ var StatusBar = React.memo(({ sessionCount, alertCount, version, updateInfo }) =
|
|
|
496
760
|
// src/ui/components/SessionList.tsx
|
|
497
761
|
import React2 from "react";
|
|
498
762
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
499
|
-
|
|
763
|
+
|
|
764
|
+
// src/ui/format.ts
|
|
765
|
+
var formatTokens = (n) => {
|
|
766
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
|
767
|
+
if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
|
|
768
|
+
return String(n);
|
|
769
|
+
};
|
|
770
|
+
var formatTime = (ts) => {
|
|
771
|
+
const d = new Date(ts);
|
|
772
|
+
return d.toLocaleTimeString("en-GB", { hour12: false });
|
|
773
|
+
};
|
|
500
774
|
var formatModel = (model) => {
|
|
775
|
+
if (model.includes("opus")) return "opus";
|
|
776
|
+
if (model.includes("sonnet")) return "sonnet";
|
|
777
|
+
if (model.includes("haiku")) return "haiku";
|
|
778
|
+
return model.slice(0, 4);
|
|
779
|
+
};
|
|
780
|
+
var formatModelShort = (model) => {
|
|
501
781
|
if (model.includes("opus")) return "opus";
|
|
502
782
|
if (model.includes("sonnet")) return "son";
|
|
503
783
|
if (model.includes("haiku")) return "hai";
|
|
504
784
|
return model.slice(0, 4);
|
|
505
785
|
};
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
return String(n);
|
|
510
|
-
};
|
|
786
|
+
|
|
787
|
+
// src/ui/components/SessionList.tsx
|
|
788
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
511
789
|
var truncate = (s, max) => s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
512
790
|
var getDisplayName = (s) => {
|
|
513
791
|
if (s.nickname) return s.nickname;
|
|
@@ -567,11 +845,14 @@ var SessionList = React2.memo(
|
|
|
567
845
|
if (item.type === "group") {
|
|
568
846
|
const g = item.group;
|
|
569
847
|
const arrow = g.expanded ? "\u25BE" : "\u25B8";
|
|
570
|
-
const dotColor2 = g.
|
|
571
|
-
const statusDot2 = g.
|
|
572
|
-
const nameColor2 = isSelected ? colors.bright : g.
|
|
573
|
-
const
|
|
574
|
-
const
|
|
848
|
+
const dotColor2 = g.status === "waiting" ? colors.waiting : g.status === "stale" ? colors.stale : g.status === "active" ? colors.success : colors.muted;
|
|
849
|
+
const statusDot2 = g.status === "inactive" ? "\u25CB" : "\u25CF";
|
|
850
|
+
const nameColor2 = isSelected ? colors.bright : g.status !== "inactive" ? colors.secondary : colors.text;
|
|
851
|
+
const groupPinned = g.sessions.some((s) => s.pinned);
|
|
852
|
+
const pinMarker2 = groupPinned ? "* " : " ";
|
|
853
|
+
const statusTag2 = g.status === "waiting" ? " [waiting]" : g.status === "stale" ? " [stale]" : "";
|
|
854
|
+
const label2 = truncate(`${g.key} (${g.sessions.length})${statusTag2}`, INNER_WIDTH - 4);
|
|
855
|
+
const model2 = formatModelShort(g.latestModel);
|
|
575
856
|
return /* @__PURE__ */ jsxs2(
|
|
576
857
|
Box2,
|
|
577
858
|
{
|
|
@@ -584,6 +865,7 @@ var SessionList = React2.memo(
|
|
|
584
865
|
" ",
|
|
585
866
|
/* @__PURE__ */ jsx2(Text2, { color: dotColor2, children: statusDot2 }),
|
|
586
867
|
" ",
|
|
868
|
+
pinMarker2,
|
|
587
869
|
label2
|
|
588
870
|
] }),
|
|
589
871
|
/* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
|
|
@@ -602,14 +884,15 @@ var SessionList = React2.memo(
|
|
|
602
884
|
);
|
|
603
885
|
}
|
|
604
886
|
const session = item.type === "session" ? item.session : item.session;
|
|
605
|
-
const
|
|
606
|
-
const statusDot =
|
|
607
|
-
const
|
|
887
|
+
const dotColor = session.status === "waiting" ? colors.waiting : session.status === "stale" ? colors.stale : session.status === "active" ? colors.success : colors.muted;
|
|
888
|
+
const statusDot = session.status === "inactive" ? "\u25CB" : "\u25CF";
|
|
889
|
+
const pinMarker = session.pinned ? "* " : " ";
|
|
890
|
+
const statusTag = session.status === "waiting" ? " [waiting]" : session.status === "stale" ? " [stale]" : "";
|
|
608
891
|
const totalIn = session.usage.inputTokens + session.usage.cacheReadTokens;
|
|
609
|
-
const model =
|
|
892
|
+
const model = formatModelShort(session.model);
|
|
610
893
|
if (item.type === "session") {
|
|
611
|
-
const nameColor2 = isSelected ? colors.bright :
|
|
612
|
-
const displayName2 = truncate(session.nickname || session.slug
|
|
894
|
+
const nameColor2 = isSelected ? colors.bright : session.status !== "inactive" ? colors.secondary : colors.muted;
|
|
895
|
+
const displayName2 = truncate(`${session.nickname || session.slug}${statusTag}`, INNER_WIDTH - 6);
|
|
613
896
|
return /* @__PURE__ */ jsxs2(
|
|
614
897
|
Box2,
|
|
615
898
|
{
|
|
@@ -622,6 +905,7 @@ var SessionList = React2.memo(
|
|
|
622
905
|
" ",
|
|
623
906
|
/* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
|
|
624
907
|
" ",
|
|
908
|
+
pinMarker,
|
|
625
909
|
displayName2
|
|
626
910
|
] }),
|
|
627
911
|
/* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
|
|
@@ -640,8 +924,8 @@ var SessionList = React2.memo(
|
|
|
640
924
|
);
|
|
641
925
|
}
|
|
642
926
|
const indicator = isSelected ? "\u25B8" : " ";
|
|
643
|
-
const nameColor = isSelected ? colors.bright :
|
|
644
|
-
const displayName = truncate(getDisplayName(session)
|
|
927
|
+
const nameColor = isSelected ? colors.bright : session.status !== "inactive" ? colors.secondary : colors.text;
|
|
928
|
+
const displayName = truncate(`${getDisplayName(session)}${statusTag}`, INNER_WIDTH - 4);
|
|
645
929
|
return /* @__PURE__ */ jsxs2(
|
|
646
930
|
Box2,
|
|
647
931
|
{
|
|
@@ -654,6 +938,7 @@ var SessionList = React2.memo(
|
|
|
654
938
|
" ",
|
|
655
939
|
/* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
|
|
656
940
|
" ",
|
|
941
|
+
pinMarker,
|
|
657
942
|
displayName
|
|
658
943
|
] }),
|
|
659
944
|
/* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
|
|
@@ -682,10 +967,6 @@ import React3 from "react";
|
|
|
682
967
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
683
968
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
684
969
|
var TAG_COLORS = ["#61AFEF", "#98C379", "#C678DD", "#E5C07B", "#E06C75", "#56B6C2", "#D19A66", "#BE5046"];
|
|
685
|
-
var formatTime = (ts) => {
|
|
686
|
-
const d = new Date(ts);
|
|
687
|
-
return d.toLocaleTimeString("en-GB", { hour12: false });
|
|
688
|
-
};
|
|
689
970
|
var summarizeInput = (call) => {
|
|
690
971
|
const input = call.toolInput;
|
|
691
972
|
switch (call.toolName) {
|
|
@@ -713,7 +994,7 @@ var ActivityFeed = React3.memo(
|
|
|
713
994
|
events,
|
|
714
995
|
sessionSlug,
|
|
715
996
|
sessionId,
|
|
716
|
-
|
|
997
|
+
status,
|
|
717
998
|
focused,
|
|
718
999
|
height,
|
|
719
1000
|
scrollOffset,
|
|
@@ -790,7 +1071,7 @@ var ActivityFeed = React3.memo(
|
|
|
790
1071
|
] }) }, `${call.timestamp}-${i}`);
|
|
791
1072
|
}),
|
|
792
1073
|
focused && canScroll && !isAtTop && visible.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingX: 1, justifyContent: "flex-end", children: /* @__PURE__ */ jsx3(Text3, { color: colors.muted, children: isAtBottom ? "" : "G:bottom " }) }),
|
|
793
|
-
!merged && sessionId &&
|
|
1074
|
+
!merged && sessionId && status === "inactive" && /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, flexDirection: "column", children: [
|
|
794
1075
|
/* @__PURE__ */ jsx3(Text3, { color: colors.muted, children: " " }),
|
|
795
1076
|
/* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
|
|
796
1077
|
"resume: ",
|
|
@@ -815,10 +1096,6 @@ var ActivityFeed = React3.memo(
|
|
|
815
1096
|
import React4 from "react";
|
|
816
1097
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
817
1098
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
818
|
-
var formatTime2 = (ts) => {
|
|
819
|
-
const d = new Date(ts);
|
|
820
|
-
return d.toLocaleTimeString("en-GB", { hour12: false });
|
|
821
|
-
};
|
|
822
1099
|
var severityIcon = {
|
|
823
1100
|
info: "i",
|
|
824
1101
|
warn: "!",
|
|
@@ -840,7 +1117,7 @@ var AlertBar = React4.memo(({ alerts, maxVisible = 4 }) => {
|
|
|
840
1117
|
] }),
|
|
841
1118
|
/* @__PURE__ */ jsxs4(Text4, { color: colors.muted, children: [
|
|
842
1119
|
" ",
|
|
843
|
-
|
|
1120
|
+
formatTime(alert.timestamp),
|
|
844
1121
|
" "
|
|
845
1122
|
] }),
|
|
846
1123
|
/* @__PURE__ */ jsxs4(Text4, { color: colors.warning, children: [
|
|
@@ -856,11 +1133,6 @@ var AlertBar = React4.memo(({ alerts, maxVisible = 4 }) => {
|
|
|
856
1133
|
import React5 from "react";
|
|
857
1134
|
import { Box as Box5, Text as Text5 } from "ink";
|
|
858
1135
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
859
|
-
var formatTokens2 = (n) => {
|
|
860
|
-
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
|
861
|
-
if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
|
|
862
|
-
return String(n);
|
|
863
|
-
};
|
|
864
1136
|
var formatUptime = (startTime) => {
|
|
865
1137
|
const ms = Date.now() - startTime;
|
|
866
1138
|
const secs = Math.floor(ms / 1e3);
|
|
@@ -870,12 +1142,6 @@ var formatUptime = (startTime) => {
|
|
|
870
1142
|
if (mins > 0) return `${mins}m ${secs % 60}s`;
|
|
871
1143
|
return `${secs}s`;
|
|
872
1144
|
};
|
|
873
|
-
var formatModel2 = (model) => {
|
|
874
|
-
if (model.includes("opus")) return "opus";
|
|
875
|
-
if (model.includes("sonnet")) return "sonnet";
|
|
876
|
-
if (model.includes("haiku")) return "haiku";
|
|
877
|
-
return model.slice(0, 20);
|
|
878
|
-
};
|
|
879
1145
|
var SessionDetail = React5.memo(({ session, focused }) => {
|
|
880
1146
|
const totalInput = session.usage.inputTokens + session.usage.cacheCreationTokens + session.usage.cacheReadTokens;
|
|
881
1147
|
const totalTokens = totalInput + session.usage.outputTokens;
|
|
@@ -903,7 +1169,7 @@ var SessionDetail = React5.memo(({ session, focused }) => {
|
|
|
903
1169
|
] }),
|
|
904
1170
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
905
1171
|
/* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: "model: " }),
|
|
906
|
-
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children:
|
|
1172
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatModel(session.model) })
|
|
907
1173
|
] }),
|
|
908
1174
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
909
1175
|
/* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: "cwd: " }),
|
|
@@ -946,19 +1212,19 @@ var SessionDetail = React5.memo(({ session, focused }) => {
|
|
|
946
1212
|
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: colors.header, bold: true, children: "Token usage" }) }),
|
|
947
1213
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
948
1214
|
/* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " input: " }),
|
|
949
|
-
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children:
|
|
1215
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.inputTokens) })
|
|
950
1216
|
] }),
|
|
951
1217
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
952
1218
|
/* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " output: " }),
|
|
953
|
-
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children:
|
|
1219
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.outputTokens) })
|
|
954
1220
|
] }),
|
|
955
1221
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
956
1222
|
/* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache write: " }),
|
|
957
|
-
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children:
|
|
1223
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.cacheCreationTokens) })
|
|
958
1224
|
] }),
|
|
959
1225
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
960
1226
|
/* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache read: " }),
|
|
961
|
-
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children:
|
|
1227
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.cacheReadTokens) })
|
|
962
1228
|
] }),
|
|
963
1229
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
964
1230
|
/* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache hit: " }),
|
|
@@ -969,7 +1235,7 @@ var SessionDetail = React5.memo(({ session, focused }) => {
|
|
|
969
1235
|
] }),
|
|
970
1236
|
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
971
1237
|
/* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " total: " }),
|
|
972
|
-
/* @__PURE__ */ jsx5(Text5, { color: colors.bright, bold: true, children:
|
|
1238
|
+
/* @__PURE__ */ jsx5(Text5, { color: colors.bright, bold: true, children: formatTokens(totalTokens) })
|
|
973
1239
|
] })
|
|
974
1240
|
] })
|
|
975
1241
|
]
|
|
@@ -1109,10 +1375,6 @@ var FooterBar = React7.memo(
|
|
|
1109
1375
|
import React8, { useState as useState3 } from "react";
|
|
1110
1376
|
import { Box as Box8, Text as Text8, useInput as useInput2 } from "ink";
|
|
1111
1377
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1112
|
-
var formatTime3 = (ts) => {
|
|
1113
|
-
const d = new Date(ts);
|
|
1114
|
-
return d.toLocaleTimeString("en-GB", { hour12: false });
|
|
1115
|
-
};
|
|
1116
1378
|
var renderBash = (event) => {
|
|
1117
1379
|
const lines = [];
|
|
1118
1380
|
const cmd = String(event.call.toolInput.command || "");
|
|
@@ -1286,7 +1548,7 @@ var ToolCallDetail = React8.memo(({ event, focused, height }) => {
|
|
|
1286
1548
|
/* @__PURE__ */ jsx8(Text8, { color: getToolColor(event.call.toolName), bold: true, children: event.call.toolName }),
|
|
1287
1549
|
/* @__PURE__ */ jsxs8(Text8, { color: colors.muted, children: [
|
|
1288
1550
|
" ",
|
|
1289
|
-
|
|
1551
|
+
formatTime(event.call.timestamp)
|
|
1290
1552
|
] }),
|
|
1291
1553
|
/* @__PURE__ */ jsxs8(Text8, { color: colors.muted, children: [
|
|
1292
1554
|
" ",
|
|
@@ -1970,23 +2232,451 @@ var ThemeMenu = React11.memo(({ config, onClose }) => {
|
|
|
1970
2232
|
/* @__PURE__ */ jsx11(Text11, { color: theme.colors.error, children: "\u2588" })
|
|
1971
2233
|
] }, theme.name);
|
|
1972
2234
|
}),
|
|
1973
|
-
toast && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx11(Text11, { color: colors.warning, children: toast }) })
|
|
2235
|
+
toast && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx11(Text11, { color: colors.warning, children: toast }) }),
|
|
2236
|
+
/* @__PURE__ */ jsx11(Box11, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.muted, dimColor: true, italic: true, wrap: "truncate", children: "Some themes were inspired by pop culture. Names have been changed to protect the innocent (and our legal team)." }) })
|
|
1974
2237
|
]
|
|
1975
2238
|
}
|
|
1976
2239
|
) });
|
|
1977
2240
|
});
|
|
1978
2241
|
|
|
1979
|
-
// src/ui/components/
|
|
1980
|
-
import React12, { useState as useState7, useEffect as useEffect4 } from "react";
|
|
1981
|
-
import { Box as Box12, Text as Text12, useInput as useInput6 } from "ink";
|
|
2242
|
+
// src/ui/components/AlertRulesMenu.tsx
|
|
2243
|
+
import React12, { useState as useState7, useRef as useRef3, useEffect as useEffect4 } from "react";
|
|
2244
|
+
import { Box as Box12, Text as Text12, useInput as useInput6, useStdout as useStdout3 } from "ink";
|
|
1982
2245
|
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1983
|
-
var
|
|
1984
|
-
|
|
2246
|
+
var MATCH_OPTIONS = ["input", "output", "toolName", "all"];
|
|
2247
|
+
var SEVERITY_OPTIONS2 = ["info", "warn", "high", "critical"];
|
|
2248
|
+
var RULE_LABELS2 = {
|
|
2249
|
+
network: "Network detection",
|
|
2250
|
+
exfiltration: "Exfiltration detection",
|
|
2251
|
+
sensitiveFiles: "Sensitive files",
|
|
2252
|
+
shellEscape: "Shell escape",
|
|
2253
|
+
injection: "Prompt injection"
|
|
2254
|
+
};
|
|
2255
|
+
var BUILTIN_RULE_KEYS = Object.keys(RULE_LABELS2);
|
|
2256
|
+
var FORM_FIELDS = ["name", "pattern", "match", "severity", "message"];
|
|
2257
|
+
var FORM_LABELS = {
|
|
2258
|
+
name: "Name",
|
|
2259
|
+
pattern: "Pattern (regex)",
|
|
2260
|
+
match: "Match target",
|
|
2261
|
+
severity: "Severity",
|
|
2262
|
+
message: "Message"
|
|
2263
|
+
};
|
|
2264
|
+
var emptyForm = () => ({
|
|
2265
|
+
name: "",
|
|
2266
|
+
pattern: "",
|
|
2267
|
+
match: "all",
|
|
2268
|
+
severity: "warn",
|
|
2269
|
+
message: ""
|
|
2270
|
+
});
|
|
2271
|
+
var formFromRule = (rule) => ({
|
|
2272
|
+
name: rule.name,
|
|
2273
|
+
pattern: rule.pattern,
|
|
2274
|
+
match: rule.match,
|
|
2275
|
+
severity: rule.severity,
|
|
2276
|
+
message: rule.message
|
|
2277
|
+
});
|
|
2278
|
+
var validatePattern = (pattern) => {
|
|
2279
|
+
if (!pattern.trim()) return "Pattern cannot be empty";
|
|
2280
|
+
try {
|
|
2281
|
+
new RegExp(pattern);
|
|
2282
|
+
return null;
|
|
2283
|
+
} catch {
|
|
2284
|
+
return "Invalid regex";
|
|
2285
|
+
}
|
|
2286
|
+
};
|
|
2287
|
+
var AlertRulesMenu = React12.memo(({ config, onClose, onSave }) => {
|
|
2288
|
+
const { stdout } = useStdout3();
|
|
2289
|
+
const termHeight = stdout?.rows ?? 40;
|
|
2290
|
+
const [localConfig, setLocalConfig] = useState7(() => JSON.parse(JSON.stringify(config)));
|
|
2291
|
+
const [selectedIdx, setSelectedIdx] = useState7(0);
|
|
2292
|
+
const [view, setView] = useState7("list");
|
|
2293
|
+
const [toast, setToast] = useState7("");
|
|
2294
|
+
const toastTimer = useRef3(null);
|
|
2295
|
+
const [formData, setFormData] = useState7(emptyForm());
|
|
2296
|
+
const [formField, setFormField] = useState7(0);
|
|
2297
|
+
const [formError, setFormError] = useState7("");
|
|
2298
|
+
const [editingIndex, setEditingIndex] = useState7(null);
|
|
2299
|
+
useEffect4(
|
|
2300
|
+
() => () => {
|
|
2301
|
+
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
2302
|
+
},
|
|
2303
|
+
[]
|
|
2304
|
+
);
|
|
2305
|
+
const showToast = (msg) => {
|
|
2306
|
+
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
2307
|
+
setToast(msg);
|
|
2308
|
+
toastTimer.current = setTimeout(() => setToast(""), 2500);
|
|
2309
|
+
};
|
|
2310
|
+
const customRules = localConfig.alerts.custom || [];
|
|
2311
|
+
const totalItems = 1 + BUILTIN_RULE_KEYS.length + customRules.length;
|
|
2312
|
+
const isStaleRow = (idx) => idx === 0;
|
|
2313
|
+
const isBuiltinRow = (idx) => idx >= 1 && idx <= BUILTIN_RULE_KEYS.length;
|
|
2314
|
+
const isCustomRow = (idx) => idx > BUILTIN_RULE_KEYS.length;
|
|
2315
|
+
const getBuiltinKey = (idx) => BUILTIN_RULE_KEYS[idx - 1];
|
|
2316
|
+
const getCustomIndex = (idx) => idx - BUILTIN_RULE_KEYS.length - 1;
|
|
2317
|
+
useInput6((input, key) => {
|
|
2318
|
+
if (view === "form") {
|
|
2319
|
+
const currentField = FORM_FIELDS[formField];
|
|
2320
|
+
if (key.escape) {
|
|
2321
|
+
setView("list");
|
|
2322
|
+
setFormError("");
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
if (currentField === "match") {
|
|
2326
|
+
if (key.return || input === " ") {
|
|
2327
|
+
const idx = MATCH_OPTIONS.indexOf(formData.match);
|
|
2328
|
+
setFormData((f) => ({ ...f, match: MATCH_OPTIONS[(idx + 1) % MATCH_OPTIONS.length] }));
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
if (key.downArrow) {
|
|
2332
|
+
setFormField((f) => Math.min(f + 1, FORM_FIELDS.length - 1));
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
if (key.upArrow) {
|
|
2336
|
+
setFormField((f) => Math.max(f - 1, 0));
|
|
2337
|
+
return;
|
|
2338
|
+
}
|
|
2339
|
+
if (key.tab) {
|
|
2340
|
+
if (formField < FORM_FIELDS.length - 1) {
|
|
2341
|
+
setFormField((f) => f + 1);
|
|
2342
|
+
}
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
if (currentField === "severity") {
|
|
2348
|
+
if (key.return || input === " ") {
|
|
2349
|
+
const idx = SEVERITY_OPTIONS2.indexOf(formData.severity);
|
|
2350
|
+
setFormData((f) => ({ ...f, severity: SEVERITY_OPTIONS2[(idx + 1) % SEVERITY_OPTIONS2.length] }));
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
if (key.downArrow) {
|
|
2354
|
+
setFormField((f) => Math.min(f + 1, FORM_FIELDS.length - 1));
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
if (key.upArrow) {
|
|
2358
|
+
setFormField((f) => Math.max(f - 1, 0));
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
if (key.tab) {
|
|
2362
|
+
if (formField < FORM_FIELDS.length - 1) {
|
|
2363
|
+
setFormField((f) => f + 1);
|
|
2364
|
+
}
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
if (key.return) {
|
|
2370
|
+
if (currentField === "pattern") {
|
|
2371
|
+
const err = validatePattern(formData.pattern);
|
|
2372
|
+
if (err) {
|
|
2373
|
+
setFormError(err);
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
setFormError("");
|
|
2377
|
+
}
|
|
2378
|
+
if (formField < FORM_FIELDS.length - 1) {
|
|
2379
|
+
setFormField((f) => f + 1);
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
if (!formData.name.trim()) {
|
|
2383
|
+
setFormError("Name cannot be empty");
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
const patErr = validatePattern(formData.pattern);
|
|
2387
|
+
if (patErr) {
|
|
2388
|
+
setFormError(patErr);
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
const newRule = {
|
|
2392
|
+
name: formData.name.trim(),
|
|
2393
|
+
pattern: formData.pattern,
|
|
2394
|
+
match: formData.match,
|
|
2395
|
+
severity: formData.severity,
|
|
2396
|
+
message: formData.message.trim(),
|
|
2397
|
+
enabled: true
|
|
2398
|
+
};
|
|
2399
|
+
setLocalConfig((c) => {
|
|
2400
|
+
const customs = [...c.alerts.custom || []];
|
|
2401
|
+
if (editingIndex !== null) {
|
|
2402
|
+
newRule.enabled = customs[editingIndex].enabled;
|
|
2403
|
+
customs[editingIndex] = newRule;
|
|
2404
|
+
} else {
|
|
2405
|
+
customs.push(newRule);
|
|
2406
|
+
}
|
|
2407
|
+
const updated = { ...c, alerts: { ...c.alerts, custom: customs } };
|
|
2408
|
+
onSave(updated);
|
|
2409
|
+
return updated;
|
|
2410
|
+
});
|
|
2411
|
+
showToast(editingIndex !== null ? `Updated '${newRule.name}'` : `Added '${newRule.name}'`);
|
|
2412
|
+
setView("list");
|
|
2413
|
+
setFormError("");
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
if (key.backspace || key.delete) {
|
|
2417
|
+
setFormData((f) => ({ ...f, [currentField]: f[currentField].slice(0, -1) }));
|
|
2418
|
+
setFormError("");
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
if (key.upArrow) {
|
|
2422
|
+
setFormField((f) => Math.max(f - 1, 0));
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
if (key.downArrow || key.tab) {
|
|
2426
|
+
if (currentField === "pattern") {
|
|
2427
|
+
const err = validatePattern(formData.pattern);
|
|
2428
|
+
if (err && formData.pattern.trim()) {
|
|
2429
|
+
setFormError(err);
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
setFormField((f) => Math.min(f + 1, FORM_FIELDS.length - 1));
|
|
2434
|
+
return;
|
|
2435
|
+
}
|
|
2436
|
+
if (input && input.length === 1) {
|
|
2437
|
+
setFormData((f) => ({ ...f, [currentField]: f[currentField] + input }));
|
|
2438
|
+
setFormError("");
|
|
2439
|
+
return;
|
|
2440
|
+
}
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
if (key.escape) {
|
|
2444
|
+
onSave(localConfig);
|
|
2445
|
+
onClose();
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
if (key.upArrow) {
|
|
2449
|
+
setSelectedIdx((i) => Math.max(0, i - 1));
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
if (key.downArrow) {
|
|
2453
|
+
setSelectedIdx((i) => Math.min(totalItems - 1, i + 1));
|
|
2454
|
+
return;
|
|
2455
|
+
}
|
|
2456
|
+
if (input === " ") {
|
|
2457
|
+
if (isStaleRow(selectedIdx)) return;
|
|
2458
|
+
if (isBuiltinRow(selectedIdx)) {
|
|
2459
|
+
const ruleKey = getBuiltinKey(selectedIdx);
|
|
2460
|
+
setLocalConfig((c) => {
|
|
2461
|
+
const updated = {
|
|
2462
|
+
...c,
|
|
2463
|
+
security: { ...c.security, rules: { ...c.security.rules, [ruleKey]: !c.security.rules[ruleKey] } }
|
|
2464
|
+
};
|
|
2465
|
+
onSave(updated);
|
|
2466
|
+
return updated;
|
|
2467
|
+
});
|
|
2468
|
+
return;
|
|
2469
|
+
}
|
|
2470
|
+
if (isCustomRow(selectedIdx)) {
|
|
2471
|
+
const ci = getCustomIndex(selectedIdx);
|
|
2472
|
+
setLocalConfig((c) => {
|
|
2473
|
+
const customs = [...c.alerts.custom || []];
|
|
2474
|
+
customs[ci] = { ...customs[ci], enabled: !customs[ci].enabled };
|
|
2475
|
+
const updated = { ...c, alerts: { ...c.alerts, custom: customs } };
|
|
2476
|
+
onSave(updated);
|
|
2477
|
+
return updated;
|
|
2478
|
+
});
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
if (isStaleRow(selectedIdx)) {
|
|
2484
|
+
if (input === "+" || input === "=") {
|
|
2485
|
+
setLocalConfig((c) => {
|
|
2486
|
+
const updated = { ...c, alerts: { ...c.alerts, staleTimeout: c.alerts.staleTimeout + 15 } };
|
|
2487
|
+
onSave(updated);
|
|
2488
|
+
return updated;
|
|
2489
|
+
});
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
if (input === "-" || input === "_") {
|
|
2493
|
+
setLocalConfig((c) => {
|
|
2494
|
+
const newTimeout = Math.max(15, c.alerts.staleTimeout - 15);
|
|
2495
|
+
const updated = { ...c, alerts: { ...c.alerts, staleTimeout: newTimeout } };
|
|
2496
|
+
onSave(updated);
|
|
2497
|
+
return updated;
|
|
2498
|
+
});
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
if (input === "n") {
|
|
2503
|
+
setFormData(emptyForm());
|
|
2504
|
+
setFormField(0);
|
|
2505
|
+
setEditingIndex(null);
|
|
2506
|
+
setFormError("");
|
|
2507
|
+
setView("form");
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
if (input === "e" && isCustomRow(selectedIdx)) {
|
|
2511
|
+
const ci = getCustomIndex(selectedIdx);
|
|
2512
|
+
const rule = customRules[ci];
|
|
2513
|
+
setFormData(formFromRule(rule));
|
|
2514
|
+
setFormField(0);
|
|
2515
|
+
setEditingIndex(ci);
|
|
2516
|
+
setFormError("");
|
|
2517
|
+
setView("form");
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
if (input === "d" && isCustomRow(selectedIdx)) {
|
|
2521
|
+
const ci = getCustomIndex(selectedIdx);
|
|
2522
|
+
const name = customRules[ci].name;
|
|
2523
|
+
setLocalConfig((c) => {
|
|
2524
|
+
const customs = [...c.alerts.custom || []];
|
|
2525
|
+
customs.splice(ci, 1);
|
|
2526
|
+
const updated = { ...c, alerts: { ...c.alerts, custom: customs } };
|
|
2527
|
+
onSave(updated);
|
|
2528
|
+
return updated;
|
|
2529
|
+
});
|
|
2530
|
+
setSelectedIdx((i) => Math.min(i, totalItems - 2));
|
|
2531
|
+
showToast(`Deleted '${name}'`);
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
});
|
|
2535
|
+
if (view === "form") {
|
|
2536
|
+
const currentField = FORM_FIELDS[formField];
|
|
2537
|
+
return /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", height: termHeight, children: /* @__PURE__ */ jsxs12(
|
|
2538
|
+
Box12,
|
|
2539
|
+
{
|
|
2540
|
+
borderStyle: "round",
|
|
2541
|
+
borderColor: colors.primary,
|
|
2542
|
+
flexDirection: "column",
|
|
2543
|
+
paddingX: 2,
|
|
2544
|
+
paddingY: 1,
|
|
2545
|
+
height: termHeight,
|
|
2546
|
+
children: [
|
|
2547
|
+
/* @__PURE__ */ jsxs12(Box12, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
2548
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.header, bold: true, children: editingIndex !== null ? "EDIT RULE" : "NEW RULE" }),
|
|
2549
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: "enter:next/save esc:cancel" })
|
|
2550
|
+
] }),
|
|
2551
|
+
FORM_FIELDS.map((field, fi) => {
|
|
2552
|
+
const isCurrent = fi === formField;
|
|
2553
|
+
const value = formData[field];
|
|
2554
|
+
const isTextInput = field === "name" || field === "pattern" || field === "message";
|
|
2555
|
+
const displayValue = isTextInput ? isCurrent ? `${value}_` : value || "(empty)" : field === "match" ? `${value} (space to cycle)` : `${value} (space to cycle)`;
|
|
2556
|
+
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
2557
|
+
/* @__PURE__ */ jsxs12(Text12, { color: isCurrent ? colors.primary : colors.text, children: [
|
|
2558
|
+
isCurrent ? "> " : " ",
|
|
2559
|
+
FORM_LABELS[field],
|
|
2560
|
+
":",
|
|
2561
|
+
" "
|
|
2562
|
+
] }),
|
|
2563
|
+
/* @__PURE__ */ jsx12(Text12, { color: isCurrent ? colors.bright : colors.muted, children: displayValue })
|
|
2564
|
+
] }, field);
|
|
2565
|
+
}),
|
|
2566
|
+
formError && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx12(Text12, { color: colors.error, children: formError }) }),
|
|
2567
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: currentField === "match" || currentField === "severity" ? "space/enter: cycle options | up/down: move fields" : "type to edit | enter: next field | up/down: move fields" }) })
|
|
2568
|
+
]
|
|
2569
|
+
}
|
|
2570
|
+
) });
|
|
2571
|
+
}
|
|
2572
|
+
const contentHeight = termHeight - 6;
|
|
2573
|
+
const halfView = Math.floor(contentHeight / 2);
|
|
2574
|
+
const scrollStart = Math.max(0, Math.min(selectedIdx - halfView, totalItems - contentHeight));
|
|
2575
|
+
const visibleStart = Math.max(0, scrollStart);
|
|
2576
|
+
const visibleEnd = Math.min(totalItems, visibleStart + contentHeight);
|
|
2577
|
+
return /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", height: termHeight, children: /* @__PURE__ */ jsxs12(
|
|
2578
|
+
Box12,
|
|
2579
|
+
{
|
|
2580
|
+
borderStyle: "round",
|
|
2581
|
+
borderColor: colors.primary,
|
|
2582
|
+
flexDirection: "column",
|
|
2583
|
+
paddingX: 2,
|
|
2584
|
+
paddingY: 1,
|
|
2585
|
+
height: termHeight,
|
|
2586
|
+
children: [
|
|
2587
|
+
/* @__PURE__ */ jsxs12(Box12, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
2588
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.header, bold: true, children: "ALERT RULES" }),
|
|
2589
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: "space:toggle n:new e:edit d:delete esc:close" })
|
|
2590
|
+
] }),
|
|
2591
|
+
visibleStart === 0 && /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text12, { color: colors.accent, bold: true, children: [
|
|
2592
|
+
" ",
|
|
2593
|
+
"STALE SESSION"
|
|
2594
|
+
] }) }) }),
|
|
2595
|
+
Array.from({ length: visibleEnd - visibleStart }, (_, vi) => {
|
|
2596
|
+
const idx = visibleStart + vi;
|
|
2597
|
+
const isSelected = idx === selectedIdx;
|
|
2598
|
+
if (isStaleRow(idx)) {
|
|
2599
|
+
const timeout = localConfig.alerts.staleTimeout;
|
|
2600
|
+
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
2601
|
+
/* @__PURE__ */ jsxs12(Text12, { color: isSelected ? colors.primary : colors.text, children: [
|
|
2602
|
+
isSelected ? "> " : " ",
|
|
2603
|
+
" Stale timeout ",
|
|
2604
|
+
"............ "
|
|
2605
|
+
] }),
|
|
2606
|
+
/* @__PURE__ */ jsxs12(Text12, { color: colors.bright, children: [
|
|
2607
|
+
timeout,
|
|
2608
|
+
"s"
|
|
2609
|
+
] }),
|
|
2610
|
+
/* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: " (+/-)" })
|
|
2611
|
+
] }, "stale-timeout");
|
|
2612
|
+
}
|
|
2613
|
+
if (isBuiltinRow(idx)) {
|
|
2614
|
+
const ruleKey = getBuiltinKey(idx);
|
|
2615
|
+
const label2 = RULE_LABELS2[ruleKey];
|
|
2616
|
+
const enabled = localConfig.security.rules[ruleKey];
|
|
2617
|
+
const showHeader = idx === 1 && visibleStart <= 1;
|
|
2618
|
+
return /* @__PURE__ */ jsxs12(React12.Fragment, { children: [
|
|
2619
|
+
showHeader && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: colors.accent, bold: true, children: [
|
|
2620
|
+
" ",
|
|
2621
|
+
"BUILT-IN RULES"
|
|
2622
|
+
] }) }),
|
|
2623
|
+
/* @__PURE__ */ jsxs12(Box12, { children: [
|
|
2624
|
+
/* @__PURE__ */ jsxs12(Text12, { color: isSelected ? colors.primary : colors.text, children: [
|
|
2625
|
+
isSelected ? "> " : " ",
|
|
2626
|
+
" ",
|
|
2627
|
+
label2,
|
|
2628
|
+
" ",
|
|
2629
|
+
".".repeat(Math.max(2, 28 - label2.length)),
|
|
2630
|
+
" "
|
|
2631
|
+
] }),
|
|
2632
|
+
/* @__PURE__ */ jsx12(Text12, { color: enabled ? colors.secondary : colors.error, children: enabled ? "ON" : "OFF" })
|
|
2633
|
+
] })
|
|
2634
|
+
] }, `builtin-${ruleKey}`);
|
|
2635
|
+
}
|
|
2636
|
+
if (isCustomRow(idx)) {
|
|
2637
|
+
const ci = getCustomIndex(idx);
|
|
2638
|
+
const rule = customRules[ci];
|
|
2639
|
+
const showHeader = ci === 0;
|
|
2640
|
+
return /* @__PURE__ */ jsxs12(React12.Fragment, { children: [
|
|
2641
|
+
showHeader && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: colors.accent, bold: true, children: [
|
|
2642
|
+
" ",
|
|
2643
|
+
"CUSTOM RULES"
|
|
2644
|
+
] }) }),
|
|
2645
|
+
/* @__PURE__ */ jsxs12(Box12, { children: [
|
|
2646
|
+
/* @__PURE__ */ jsxs12(Text12, { color: isSelected ? colors.primary : colors.text, children: [
|
|
2647
|
+
isSelected ? "> " : " ",
|
|
2648
|
+
" ",
|
|
2649
|
+
rule.name,
|
|
2650
|
+
" "
|
|
2651
|
+
] }),
|
|
2652
|
+
/* @__PURE__ */ jsxs12(Text12, { color: colors.muted, children: [
|
|
2653
|
+
"/",
|
|
2654
|
+
rule.pattern,
|
|
2655
|
+
"/ "
|
|
2656
|
+
] }),
|
|
2657
|
+
/* @__PURE__ */ jsx12(Text12, { color: rule.enabled ? colors.secondary : colors.error, children: rule.enabled ? "ON" : "OFF" })
|
|
2658
|
+
] })
|
|
2659
|
+
] }, `custom-${ci}`);
|
|
2660
|
+
}
|
|
2661
|
+
return null;
|
|
2662
|
+
}),
|
|
2663
|
+
toast && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx12(Text12, { color: colors.warning, children: toast }) })
|
|
2664
|
+
]
|
|
2665
|
+
}
|
|
2666
|
+
) });
|
|
2667
|
+
});
|
|
2668
|
+
|
|
2669
|
+
// src/ui/components/ThemePickerModal.tsx
|
|
2670
|
+
import React13, { useState as useState8, useEffect as useEffect5 } from "react";
|
|
2671
|
+
import { Box as Box13, Text as Text13, useInput as useInput7 } from "ink";
|
|
2672
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2673
|
+
var ThemePickerModal = React13.memo(({ onSelect, onSkip, onDismiss }) => {
|
|
2674
|
+
const [selectedIndex, setSelectedIndex] = useState8(0);
|
|
1985
2675
|
const themes = BUILTIN_THEMES;
|
|
1986
|
-
|
|
2676
|
+
useEffect5(() => {
|
|
1987
2677
|
applyTheme(themes[selectedIndex]);
|
|
1988
2678
|
}, [selectedIndex]);
|
|
1989
|
-
|
|
2679
|
+
useInput7((input, key) => {
|
|
1990
2680
|
if (key.upArrow) setSelectedIndex((i) => Math.max(0, i - 1));
|
|
1991
2681
|
if (key.downArrow) setSelectedIndex((i) => Math.min(themes.length - 1, i + 1));
|
|
1992
2682
|
if (key.return) onSelect(themes[selectedIndex].name);
|
|
@@ -1995,33 +2685,33 @@ var ThemePickerModal = React12.memo(({ onSelect, onSkip, onDismiss }) => {
|
|
|
1995
2685
|
});
|
|
1996
2686
|
const renderSwatch = (theme) => {
|
|
1997
2687
|
const c = theme.colors;
|
|
1998
|
-
return /* @__PURE__ */
|
|
1999
|
-
/* @__PURE__ */
|
|
2000
|
-
/* @__PURE__ */
|
|
2001
|
-
/* @__PURE__ */
|
|
2002
|
-
/* @__PURE__ */
|
|
2003
|
-
/* @__PURE__ */
|
|
2688
|
+
return /* @__PURE__ */ jsxs13(Text13, { children: [
|
|
2689
|
+
/* @__PURE__ */ jsx13(Text13, { color: c.primary, children: "\u2588" }),
|
|
2690
|
+
/* @__PURE__ */ jsx13(Text13, { color: c.secondary, children: "\u2588" }),
|
|
2691
|
+
/* @__PURE__ */ jsx13(Text13, { color: c.accent, children: "\u2588" }),
|
|
2692
|
+
/* @__PURE__ */ jsx13(Text13, { color: c.warning, children: "\u2588" }),
|
|
2693
|
+
/* @__PURE__ */ jsx13(Text13, { color: c.error, children: "\u2588" })
|
|
2004
2694
|
] });
|
|
2005
2695
|
};
|
|
2006
|
-
return /* @__PURE__ */
|
|
2007
|
-
/* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
2009
|
-
themes.map((theme, i) => /* @__PURE__ */
|
|
2010
|
-
/* @__PURE__ */
|
|
2696
|
+
return /* @__PURE__ */ jsx13(Box13, { flexDirection: "column", paddingX: 4, paddingY: 1, children: /* @__PURE__ */ jsxs13(Box13, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", paddingX: 3, paddingY: 1, children: [
|
|
2697
|
+
/* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.header, bold: true, children: "Choose a theme" }) }),
|
|
2698
|
+
/* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.text, children: "Select a theme to get started. You can change this later in settings (s)." }) }),
|
|
2699
|
+
themes.map((theme, i) => /* @__PURE__ */ jsxs13(Box13, { children: [
|
|
2700
|
+
/* @__PURE__ */ jsx13(Text13, { color: i === selectedIndex ? colors.primary : colors.muted, children: i === selectedIndex ? "> " : " " }),
|
|
2011
2701
|
renderSwatch(theme),
|
|
2012
|
-
/* @__PURE__ */
|
|
2702
|
+
/* @__PURE__ */ jsxs13(Text13, { color: i === selectedIndex ? colors.bright : colors.text, children: [
|
|
2013
2703
|
" ",
|
|
2014
2704
|
theme.name
|
|
2015
2705
|
] })
|
|
2016
2706
|
] }, theme.name)),
|
|
2017
|
-
/* @__PURE__ */
|
|
2707
|
+
/* @__PURE__ */ jsx13(Box13, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx13(Text13, { color: colors.muted, children: "Enter = select | n = not now | d = don't ask again" }) })
|
|
2018
2708
|
] }) });
|
|
2019
2709
|
});
|
|
2020
2710
|
|
|
2021
2711
|
// src/ui/components/GuidedTour.tsx
|
|
2022
|
-
import
|
|
2023
|
-
import { Box as
|
|
2024
|
-
import { jsx as
|
|
2712
|
+
import React14, { useState as useState9 } from "react";
|
|
2713
|
+
import { Box as Box14, Text as Text14, useInput as useInput8 } from "ink";
|
|
2714
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2025
2715
|
var STEPS = [
|
|
2026
2716
|
{
|
|
2027
2717
|
title: "Session list",
|
|
@@ -2052,11 +2742,11 @@ var STEPS = [
|
|
|
2052
2742
|
body: "Press s to open settings. Customise keybindings, manage themes, and configure updates."
|
|
2053
2743
|
}
|
|
2054
2744
|
];
|
|
2055
|
-
var GuidedTour =
|
|
2056
|
-
const [stepIndex, setStepIndex] =
|
|
2745
|
+
var GuidedTour = React14.memo(({ onComplete, onSkip }) => {
|
|
2746
|
+
const [stepIndex, setStepIndex] = useState9(0);
|
|
2057
2747
|
const step = STEPS[stepIndex];
|
|
2058
2748
|
const isLast = stepIndex === STEPS.length - 1;
|
|
2059
|
-
|
|
2749
|
+
useInput8((input, key) => {
|
|
2060
2750
|
if (key.return || key.rightArrow) {
|
|
2061
2751
|
if (isLast) onComplete();
|
|
2062
2752
|
else setStepIndex((i) => i + 1);
|
|
@@ -2064,17 +2754,17 @@ var GuidedTour = React13.memo(({ onComplete, onSkip }) => {
|
|
|
2064
2754
|
if (key.leftArrow && stepIndex > 0) setStepIndex((i) => i - 1);
|
|
2065
2755
|
if (input === "q" || key.escape) onSkip();
|
|
2066
2756
|
});
|
|
2067
|
-
return /* @__PURE__ */
|
|
2068
|
-
/* @__PURE__ */
|
|
2757
|
+
return /* @__PURE__ */ jsx14(Box14, { flexDirection: "column", paddingX: 4, paddingY: 1, children: /* @__PURE__ */ jsxs14(Box14, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", paddingX: 3, paddingY: 1, children: [
|
|
2758
|
+
/* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsxs14(Text14, { color: colors.header, bold: true, children: [
|
|
2069
2759
|
"Quick tour (",
|
|
2070
2760
|
stepIndex + 1,
|
|
2071
2761
|
"/",
|
|
2072
2762
|
STEPS.length,
|
|
2073
2763
|
")"
|
|
2074
2764
|
] }) }),
|
|
2075
|
-
/* @__PURE__ */
|
|
2076
|
-
/* @__PURE__ */
|
|
2077
|
-
/* @__PURE__ */
|
|
2765
|
+
/* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.bright, bold: true, children: step.title }) }),
|
|
2766
|
+
/* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: step.body }) }),
|
|
2767
|
+
/* @__PURE__ */ jsxs14(Text14, { color: colors.muted, children: [
|
|
2078
2768
|
isLast ? "Enter = finish" : "Enter/\u2192 = next",
|
|
2079
2769
|
" | ",
|
|
2080
2770
|
stepIndex > 0 ? "\u2190 = back | " : "",
|
|
@@ -2084,19 +2774,19 @@ var GuidedTour = React13.memo(({ onComplete, onSkip }) => {
|
|
|
2084
2774
|
});
|
|
2085
2775
|
|
|
2086
2776
|
// src/ui/components/ConfirmModal.tsx
|
|
2087
|
-
import
|
|
2088
|
-
import { Box as
|
|
2089
|
-
import { jsx as
|
|
2090
|
-
var ConfirmModal =
|
|
2091
|
-
|
|
2777
|
+
import React15 from "react";
|
|
2778
|
+
import { Box as Box15, Text as Text15, useInput as useInput9 } from "ink";
|
|
2779
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2780
|
+
var ConfirmModal = React15.memo(({ title, message, onConfirm, onCancel }) => {
|
|
2781
|
+
useInput9((input, key) => {
|
|
2092
2782
|
if (input === "y" || input === "Y") {
|
|
2093
2783
|
onConfirm();
|
|
2094
2784
|
} else if (input === "n" || input === "N" || key.escape) {
|
|
2095
2785
|
onCancel();
|
|
2096
2786
|
}
|
|
2097
2787
|
});
|
|
2098
|
-
return /* @__PURE__ */
|
|
2099
|
-
|
|
2788
|
+
return /* @__PURE__ */ jsxs15(
|
|
2789
|
+
Box15,
|
|
2100
2790
|
{
|
|
2101
2791
|
borderStyle: "round",
|
|
2102
2792
|
borderColor: colors.warning,
|
|
@@ -2105,27 +2795,27 @@ var ConfirmModal = React14.memo(({ title, message, onConfirm, onCancel }) => {
|
|
|
2105
2795
|
paddingY: 1,
|
|
2106
2796
|
alignSelf: "center",
|
|
2107
2797
|
children: [
|
|
2108
|
-
/* @__PURE__ */
|
|
2109
|
-
/* @__PURE__ */
|
|
2110
|
-
/* @__PURE__ */
|
|
2798
|
+
/* @__PURE__ */ jsx15(Text15, { color: colors.warning, bold: true, children: title }),
|
|
2799
|
+
/* @__PURE__ */ jsx15(Text15, { color: colors.text, children: message }),
|
|
2800
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: colors.muted, children: "[y] confirm [n/esc] cancel" }) })
|
|
2111
2801
|
]
|
|
2112
2802
|
}
|
|
2113
2803
|
);
|
|
2114
2804
|
});
|
|
2115
2805
|
|
|
2116
2806
|
// src/ui/components/UpdateModal.tsx
|
|
2117
|
-
import
|
|
2118
|
-
import { Box as
|
|
2119
|
-
import { Fragment as Fragment2, jsx as
|
|
2807
|
+
import React16, { useState as useState10, useCallback as useCallback2 } from "react";
|
|
2808
|
+
import { Box as Box16, Text as Text16, useInput as useInput10 } from "ink";
|
|
2809
|
+
import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2120
2810
|
var options = [
|
|
2121
2811
|
{ key: "now", label: "Update now" },
|
|
2122
2812
|
{ key: "later", label: "Not now" },
|
|
2123
2813
|
{ key: "never", label: "Don't ask again" }
|
|
2124
2814
|
];
|
|
2125
|
-
var UpdateModal =
|
|
2126
|
-
const [selected, setSelected] =
|
|
2127
|
-
const [status, setStatus] =
|
|
2128
|
-
const [errorMsg, setErrorMsg] =
|
|
2815
|
+
var UpdateModal = React16.memo(({ current, latest, onNotNow, onDontAskAgain }) => {
|
|
2816
|
+
const [selected, setSelected] = useState10(0);
|
|
2817
|
+
const [status, setStatus] = useState10("idle");
|
|
2818
|
+
const [errorMsg, setErrorMsg] = useState10("");
|
|
2129
2819
|
const doUpdate = useCallback2(() => {
|
|
2130
2820
|
setStatus("updating");
|
|
2131
2821
|
installUpdate().then(() => {
|
|
@@ -2136,7 +2826,7 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
|
|
|
2136
2826
|
setStatus("error");
|
|
2137
2827
|
});
|
|
2138
2828
|
}, []);
|
|
2139
|
-
|
|
2829
|
+
useInput10((input, key) => {
|
|
2140
2830
|
if (status === "updating" || status === "restarting") return;
|
|
2141
2831
|
if (status === "error") {
|
|
2142
2832
|
if (key.escape || key.return) {
|
|
@@ -2168,8 +2858,8 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
|
|
|
2168
2858
|
}
|
|
2169
2859
|
}
|
|
2170
2860
|
});
|
|
2171
|
-
return /* @__PURE__ */
|
|
2172
|
-
|
|
2861
|
+
return /* @__PURE__ */ jsxs16(
|
|
2862
|
+
Box16,
|
|
2173
2863
|
{
|
|
2174
2864
|
borderStyle: "round",
|
|
2175
2865
|
borderColor: colors.primary,
|
|
@@ -2178,8 +2868,8 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
|
|
|
2178
2868
|
paddingY: 1,
|
|
2179
2869
|
alignSelf: "center",
|
|
2180
2870
|
children: [
|
|
2181
|
-
/* @__PURE__ */
|
|
2182
|
-
/* @__PURE__ */
|
|
2871
|
+
/* @__PURE__ */ jsx16(Text16, { color: colors.primary, bold: true, children: "Update available" }),
|
|
2872
|
+
/* @__PURE__ */ jsxs16(Text16, { color: colors.text, children: [
|
|
2183
2873
|
"v",
|
|
2184
2874
|
current,
|
|
2185
2875
|
" ",
|
|
@@ -2187,10 +2877,10 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
|
|
|
2187
2877
|
" v",
|
|
2188
2878
|
latest
|
|
2189
2879
|
] }),
|
|
2190
|
-
/* @__PURE__ */
|
|
2191
|
-
/* @__PURE__ */
|
|
2192
|
-
/* @__PURE__ */
|
|
2193
|
-
] }) : options.map((opt, i) => /* @__PURE__ */
|
|
2880
|
+
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, flexDirection: "column", children: status === "updating" ? /* @__PURE__ */ jsx16(Text16, { color: colors.warning, children: "updating..." }) : status === "restarting" ? /* @__PURE__ */ jsx16(Text16, { color: colors.secondary, children: "restarting..." }) : status === "error" ? /* @__PURE__ */ jsxs16(Fragment2, { children: [
|
|
2881
|
+
/* @__PURE__ */ jsx16(Text16, { color: colors.error, children: errorMsg }),
|
|
2882
|
+
/* @__PURE__ */ jsx16(Text16, { color: colors.muted, children: "press enter to continue" })
|
|
2883
|
+
] }) : options.map((opt, i) => /* @__PURE__ */ jsxs16(Text16, { color: i === selected ? colors.primary : colors.muted, children: [
|
|
2194
2884
|
i === selected ? "> " : " ",
|
|
2195
2885
|
opt.label
|
|
2196
2886
|
] }, opt.key)) })
|
|
@@ -2200,10 +2890,10 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
|
|
|
2200
2890
|
});
|
|
2201
2891
|
|
|
2202
2892
|
// src/ui/components/SplitPanel.tsx
|
|
2203
|
-
import
|
|
2204
|
-
import { Box as
|
|
2205
|
-
import { jsx as
|
|
2206
|
-
var SplitPanel =
|
|
2893
|
+
import React17 from "react";
|
|
2894
|
+
import { Box as Box17 } from "ink";
|
|
2895
|
+
import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2896
|
+
var SplitPanel = React17.memo(
|
|
2207
2897
|
({
|
|
2208
2898
|
activePanel,
|
|
2209
2899
|
leftSession,
|
|
@@ -2218,35 +2908,35 @@ var SplitPanel = React16.memo(
|
|
|
2218
2908
|
rightShowDetail,
|
|
2219
2909
|
height
|
|
2220
2910
|
}) => {
|
|
2221
|
-
const left = leftShowDetail && leftSession ? /* @__PURE__ */
|
|
2911
|
+
const left = leftShowDetail && leftSession ? /* @__PURE__ */ jsx17(SessionDetail, { session: leftSession, focused: activePanel === "left", height }) : /* @__PURE__ */ jsx17(
|
|
2222
2912
|
ActivityFeed,
|
|
2223
2913
|
{
|
|
2224
2914
|
events: leftEvents,
|
|
2225
2915
|
sessionSlug: leftSession?.slug ?? null,
|
|
2226
2916
|
sessionId: leftSession?.sessionId,
|
|
2227
|
-
|
|
2917
|
+
status: leftSession ? leftSession.status : void 0,
|
|
2228
2918
|
focused: activePanel === "left",
|
|
2229
2919
|
height,
|
|
2230
2920
|
scrollOffset: leftScroll,
|
|
2231
2921
|
filter: leftFilter || void 0
|
|
2232
2922
|
}
|
|
2233
2923
|
);
|
|
2234
|
-
const right = rightShowDetail && rightSession ? /* @__PURE__ */
|
|
2924
|
+
const right = rightShowDetail && rightSession ? /* @__PURE__ */ jsx17(SessionDetail, { session: rightSession, focused: activePanel === "right", height }) : /* @__PURE__ */ jsx17(
|
|
2235
2925
|
ActivityFeed,
|
|
2236
2926
|
{
|
|
2237
2927
|
events: rightEvents,
|
|
2238
2928
|
sessionSlug: rightSession?.slug ?? null,
|
|
2239
2929
|
sessionId: rightSession?.sessionId,
|
|
2240
|
-
|
|
2930
|
+
status: rightSession ? rightSession.status : void 0,
|
|
2241
2931
|
focused: activePanel === "right",
|
|
2242
2932
|
height,
|
|
2243
2933
|
scrollOffset: rightScroll,
|
|
2244
2934
|
filter: rightFilter || void 0
|
|
2245
2935
|
}
|
|
2246
2936
|
);
|
|
2247
|
-
return /* @__PURE__ */
|
|
2248
|
-
/* @__PURE__ */
|
|
2249
|
-
|
|
2937
|
+
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", flexGrow: 1, children: [
|
|
2938
|
+
/* @__PURE__ */ jsx17(
|
|
2939
|
+
Box17,
|
|
2250
2940
|
{
|
|
2251
2941
|
flexGrow: 1,
|
|
2252
2942
|
borderStyle: "single",
|
|
@@ -2264,10 +2954,10 @@ var SplitPanel = React16.memo(
|
|
|
2264
2954
|
);
|
|
2265
2955
|
|
|
2266
2956
|
// src/ui/hooks/useSessions.ts
|
|
2267
|
-
import { useState as
|
|
2957
|
+
import { useState as useState11, useEffect as useEffect6, useCallback as useCallback3, useRef as useRef4, useMemo as useMemo2 } from "react";
|
|
2268
2958
|
|
|
2269
2959
|
// src/discovery/sessionsAsync.ts
|
|
2270
|
-
import { readdir, stat as stat2 } from "fs/promises";
|
|
2960
|
+
import { readdir, stat as stat2, open as open2 } from "fs/promises";
|
|
2271
2961
|
import { join as join2, basename } from "path";
|
|
2272
2962
|
|
|
2273
2963
|
// src/discovery/cache.ts
|
|
@@ -2306,44 +2996,9 @@ var pruneFileMetaCache = (validPaths) => {
|
|
|
2306
2996
|
};
|
|
2307
2997
|
|
|
2308
2998
|
// src/discovery/asyncHelpers.ts
|
|
2309
|
-
import {
|
|
2310
|
-
import {
|
|
2311
|
-
var normalisePath = (p) => p.replace(
|
|
2312
|
-
var getClaudeProcessesAsync = async () => {
|
|
2313
|
-
const stdout = await new Promise((resolve) => {
|
|
2314
|
-
execFile2("ps", ["aux"], { encoding: "utf-8", timeout: 5e3, maxBuffer: 4 * 1024 * 1024 }, (err, out) => {
|
|
2315
|
-
resolve(err || !out ? "" : out);
|
|
2316
|
-
});
|
|
2317
|
-
});
|
|
2318
|
-
if (!stdout) return [];
|
|
2319
|
-
const procs = [];
|
|
2320
|
-
for (const line of stdout.split("\n")) {
|
|
2321
|
-
if (!line.includes("/claude") || line.includes("grep") || line.includes("agenttop")) continue;
|
|
2322
|
-
const parts = line.trim().split(/\s+/);
|
|
2323
|
-
const pid = parseInt(parts[1], 10);
|
|
2324
|
-
if (isNaN(pid)) continue;
|
|
2325
|
-
const command = parts.slice(10).join(" ");
|
|
2326
|
-
if (command.startsWith("sudo")) continue;
|
|
2327
|
-
procs.push({
|
|
2328
|
-
pid,
|
|
2329
|
-
cpu: parseFloat(parts[2]) || 0,
|
|
2330
|
-
mem: parseFloat(parts[3]) || 0,
|
|
2331
|
-
memKB: parseInt(parts[5], 10) || 0,
|
|
2332
|
-
startTime: parts[8] || "",
|
|
2333
|
-
command,
|
|
2334
|
-
cwd: ""
|
|
2335
|
-
});
|
|
2336
|
-
}
|
|
2337
|
-
await Promise.all(
|
|
2338
|
-
procs.map(async (p) => {
|
|
2339
|
-
try {
|
|
2340
|
-
p.cwd = await readlink(`/proc/${p.pid}/cwd`);
|
|
2341
|
-
} catch {
|
|
2342
|
-
}
|
|
2343
|
-
})
|
|
2344
|
-
);
|
|
2345
|
-
return procs;
|
|
2346
|
-
};
|
|
2999
|
+
import { open, stat } from "fs/promises";
|
|
3000
|
+
import { normalize } from "path";
|
|
3001
|
+
var normalisePath = (p) => normalize(p).replace(/[\\/]+$/, "");
|
|
2347
3002
|
var readFirstLinesAsync = async (filePath, bytes) => {
|
|
2348
3003
|
let fh;
|
|
2349
3004
|
try {
|
|
@@ -2461,7 +3116,47 @@ var findModelAndUsageAsync = async (filePath) => {
|
|
|
2461
3116
|
};
|
|
2462
3117
|
|
|
2463
3118
|
// src/discovery/sessionsAsync.ts
|
|
2464
|
-
var
|
|
3119
|
+
var readTailBytesAsync = async (filePath, bytes) => {
|
|
3120
|
+
let fh;
|
|
3121
|
+
try {
|
|
3122
|
+
fh = await open2(filePath, "r");
|
|
3123
|
+
const fstat = await stat2(filePath);
|
|
3124
|
+
const start = Math.max(0, fstat.size - bytes);
|
|
3125
|
+
const readSize = Math.min(bytes, fstat.size);
|
|
3126
|
+
const buf = Buffer.alloc(readSize);
|
|
3127
|
+
await fh.read(buf, 0, readSize, start);
|
|
3128
|
+
return buf.toString("utf-8");
|
|
3129
|
+
} catch {
|
|
3130
|
+
return "";
|
|
3131
|
+
} finally {
|
|
3132
|
+
await fh?.close();
|
|
3133
|
+
}
|
|
3134
|
+
};
|
|
3135
|
+
var detectStatusAsync = async (filePath, hasPid, lastActivity, staleTimeout) => {
|
|
3136
|
+
if (!hasPid) return "inactive";
|
|
3137
|
+
const tail = await readTailBytesAsync(filePath, 4096);
|
|
3138
|
+
const lines = tail.split("\n").filter(Boolean);
|
|
3139
|
+
if (lines.length > 0) {
|
|
3140
|
+
try {
|
|
3141
|
+
const lastEvent = JSON.parse(lines[lines.length - 1]);
|
|
3142
|
+
if (lastEvent.type === "assistant") {
|
|
3143
|
+
const content = lastEvent.message?.content;
|
|
3144
|
+
if (Array.isArray(content)) {
|
|
3145
|
+
const hasAskUser = content.some(
|
|
3146
|
+
(b) => b.type === "tool_use" && b.name === "AskUserQuestion"
|
|
3147
|
+
);
|
|
3148
|
+
if (hasAskUser) return "waiting";
|
|
3149
|
+
const hasToolUse = content.some((b) => b.type === "tool_use");
|
|
3150
|
+
if (!hasToolUse) return "waiting";
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
} catch {
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
if (Date.now() - lastActivity > staleTimeout * 1e3) return "stale";
|
|
3157
|
+
return "active";
|
|
3158
|
+
};
|
|
3159
|
+
var discoverFromProjectsAsync = async (allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder) => {
|
|
2465
3160
|
const projectsDirs = getProjectsDirs(allUsers);
|
|
2466
3161
|
for (const projectsDir of projectsDirs) {
|
|
2467
3162
|
let projectNames;
|
|
@@ -2516,14 +3211,16 @@ var discoverFromProjectsAsync = async (allUsers, processes, sessionMap, seenFile
|
|
|
2516
3211
|
outputFiles: [filePath],
|
|
2517
3212
|
startTime: fstat.birthtimeMs || fstat.ctimeMs,
|
|
2518
3213
|
lastActivity: fstat.mtimeMs,
|
|
2519
|
-
usage: meta.usage
|
|
3214
|
+
usage: meta.usage,
|
|
3215
|
+
status: await detectStatusAsync(filePath, matchingProcess !== void 0, fstat.mtimeMs, staleTimeout),
|
|
3216
|
+
pinned: pinnedOrder.includes(meta.sessionId)
|
|
2520
3217
|
};
|
|
2521
3218
|
sessionMap.set(meta.sessionId, session);
|
|
2522
3219
|
}
|
|
2523
3220
|
}
|
|
2524
3221
|
}
|
|
2525
3222
|
};
|
|
2526
|
-
var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) => {
|
|
3223
|
+
var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder) => {
|
|
2527
3224
|
const taskDirs = getTaskDirs(allUsers);
|
|
2528
3225
|
for (const taskDir of taskDirs) {
|
|
2529
3226
|
let projectDirs;
|
|
@@ -2613,6 +3310,13 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
|
|
|
2613
3310
|
if (sessionMap.has(sessionId || projectName)) continue;
|
|
2614
3311
|
const normCwd = normalisePath(cwd);
|
|
2615
3312
|
const matchingProcess = processes.find((p) => p.cwd && normalisePath(p.cwd) === normCwd);
|
|
3313
|
+
let latestFile = outputFiles[0];
|
|
3314
|
+
for (const f of outputFiles) {
|
|
3315
|
+
try {
|
|
3316
|
+
if ((await stat2(f)).mtimeMs > (await stat2(latestFile)).mtimeMs) latestFile = f;
|
|
3317
|
+
} catch {
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
2616
3320
|
const session = {
|
|
2617
3321
|
sessionId,
|
|
2618
3322
|
slug: slug || sessionId.slice(0, 12),
|
|
@@ -2630,7 +3334,9 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
|
|
|
2630
3334
|
outputFiles,
|
|
2631
3335
|
startTime: startTime === Infinity ? Date.now() : startTime,
|
|
2632
3336
|
lastActivity,
|
|
2633
|
-
usage: totalUsage
|
|
3337
|
+
usage: totalUsage,
|
|
3338
|
+
status: await detectStatusAsync(latestFile, matchingProcess !== void 0, lastActivity, staleTimeout),
|
|
3339
|
+
pinned: pinnedOrder.includes(sessionId)
|
|
2634
3340
|
};
|
|
2635
3341
|
sessionMap.set(sessionId || projectName, session);
|
|
2636
3342
|
}
|
|
@@ -2638,16 +3344,26 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
|
|
|
2638
3344
|
}
|
|
2639
3345
|
};
|
|
2640
3346
|
var discoverSessionsAsync = async (allUsers) => {
|
|
3347
|
+
const config = loadConfig();
|
|
3348
|
+
const staleTimeout = config.alerts.staleTimeout ?? 60;
|
|
3349
|
+
const pinnedOrder = config.pinnedSessions ?? [];
|
|
2641
3350
|
const processes = await getClaudeProcessesAsync();
|
|
2642
3351
|
const sessionMap = /* @__PURE__ */ new Map();
|
|
2643
3352
|
const seenFiles = /* @__PURE__ */ new Set();
|
|
2644
|
-
await discoverFromProjectsAsync(allUsers, processes, sessionMap, seenFiles);
|
|
2645
|
-
await discoverFromTmpAsync(allUsers, processes, sessionMap, seenFiles);
|
|
3353
|
+
await discoverFromProjectsAsync(allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder);
|
|
3354
|
+
await discoverFromTmpAsync(allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder);
|
|
2646
3355
|
pruneFileMetaCache(seenFiles);
|
|
2647
3356
|
return Array.from(sessionMap.values()).sort((a, b) => {
|
|
2648
|
-
const
|
|
2649
|
-
const
|
|
2650
|
-
|
|
3357
|
+
const aPin = pinnedOrder.indexOf(a.sessionId);
|
|
3358
|
+
const bPin = pinnedOrder.indexOf(b.sessionId);
|
|
3359
|
+
const aIsPinned = aPin !== -1;
|
|
3360
|
+
const bIsPinned = bPin !== -1;
|
|
3361
|
+
if (aIsPinned && !bIsPinned) return -1;
|
|
3362
|
+
if (!aIsPinned && bIsPinned) return 1;
|
|
3363
|
+
if (aIsPinned && bIsPinned) return aPin - bPin;
|
|
3364
|
+
const aPri = STATUS_PRIORITY[a.status];
|
|
3365
|
+
const bPri = STATUS_PRIORITY[b.status];
|
|
3366
|
+
if (aPri !== bPri) return aPri - bPri;
|
|
2651
3367
|
return b.lastActivity - a.lastActivity;
|
|
2652
3368
|
});
|
|
2653
3369
|
};
|
|
@@ -2692,7 +3408,9 @@ var buildGroups = (sessions2, expandedKeys) => {
|
|
|
2692
3408
|
totalInputTokens: totalIn,
|
|
2693
3409
|
totalOutputTokens: totalOut,
|
|
2694
3410
|
latestModel: list[0].model,
|
|
2695
|
-
|
|
3411
|
+
status: list.reduce((best, s) => {
|
|
3412
|
+
return (STATUS_PRIORITY[s.status] ?? 3) < (STATUS_PRIORITY[best] ?? 3) ? s.status : best;
|
|
3413
|
+
}, "inactive"),
|
|
2696
3414
|
latestActivity: list[0].lastActivity,
|
|
2697
3415
|
earliestStart: Math.min(...list.map((s) => s.startTime))
|
|
2698
3416
|
});
|
|
@@ -2749,17 +3467,17 @@ var enrichAndFilter = (found, usageOverrides, filter, archivedIds, viewingArchiv
|
|
|
2749
3467
|
return enriched;
|
|
2750
3468
|
};
|
|
2751
3469
|
var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
|
|
2752
|
-
const [sessions2, setSessions] =
|
|
2753
|
-
const [selectedIndex, setSelectedIndex] =
|
|
2754
|
-
const [expandedKeys, setExpandedKeys] =
|
|
2755
|
-
const usageOverrides =
|
|
3470
|
+
const [sessions2, setSessions] = useState11([]);
|
|
3471
|
+
const [selectedIndex, setSelectedIndex] = useState11(0);
|
|
3472
|
+
const [expandedKeys, setExpandedKeys] = useState11(/* @__PURE__ */ new Set());
|
|
3473
|
+
const usageOverrides = useRef4(/* @__PURE__ */ new Map());
|
|
2756
3474
|
const refresh = useCallback3(() => {
|
|
2757
3475
|
const found = getCachedSessions();
|
|
2758
3476
|
const filtered = enrichAndFilter(found, usageOverrides.current, filter, archivedIds, viewingArchive);
|
|
2759
3477
|
setSessions(filtered);
|
|
2760
3478
|
triggerRefresh(allUsers);
|
|
2761
3479
|
}, [allUsers, filter, archivedIds, viewingArchive]);
|
|
2762
|
-
|
|
3480
|
+
useEffect6(() => {
|
|
2763
3481
|
const unsubscribe = subscribe(() => {
|
|
2764
3482
|
const found = getCachedSessions();
|
|
2765
3483
|
const filtered = enrichAndFilter(found, usageOverrides.current, filter, archivedIds, viewingArchive);
|
|
@@ -2767,7 +3485,7 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
|
|
|
2767
3485
|
});
|
|
2768
3486
|
return unsubscribe;
|
|
2769
3487
|
}, [filter, archivedIds, viewingArchive]);
|
|
2770
|
-
|
|
3488
|
+
useEffect6(() => {
|
|
2771
3489
|
refresh();
|
|
2772
3490
|
const pollMs = sessions2.length > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS;
|
|
2773
3491
|
const interval = setInterval(() => triggerRefresh(allUsers), pollMs);
|
|
@@ -2775,7 +3493,7 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
|
|
|
2775
3493
|
}, [refresh, sessions2.length > 0]);
|
|
2776
3494
|
const groups = useMemo2(() => buildGroups(sessions2, expandedKeys), [sessions2, expandedKeys]);
|
|
2777
3495
|
const visibleItems = useMemo2(() => buildVisibleItems(groups), [groups]);
|
|
2778
|
-
const itemCountRef =
|
|
3496
|
+
const itemCountRef = useRef4(visibleItems.length);
|
|
2779
3497
|
itemCountRef.current = visibleItems.length;
|
|
2780
3498
|
const selectedItem = visibleItems[selectedIndex] ?? null;
|
|
2781
3499
|
const selectedSession = selectedItem?.type === "ungrouped" ? selectedItem.session : selectedItem?.type === "session" ? selectedItem.session : null;
|
|
@@ -2827,16 +3545,16 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
|
|
|
2827
3545
|
};
|
|
2828
3546
|
|
|
2829
3547
|
// src/ui/hooks/useActivityStream.ts
|
|
2830
|
-
import { useState as
|
|
3548
|
+
import { useState as useState12, useEffect as useEffect7, useRef as useRef5, useMemo as useMemo3 } from "react";
|
|
2831
3549
|
var MAX_EVENTS = 200;
|
|
2832
3550
|
var useActivityStream = (session, allUsers) => {
|
|
2833
|
-
const [calls, setCalls] =
|
|
2834
|
-
const [results, setResults] =
|
|
2835
|
-
const watcherRef =
|
|
3551
|
+
const [calls, setCalls] = useState12([]);
|
|
3552
|
+
const [results, setResults] = useState12([]);
|
|
3553
|
+
const watcherRef = useRef5(null);
|
|
2836
3554
|
const sessions2 = session === null ? [] : Array.isArray(session) ? session : [session];
|
|
2837
3555
|
const sessionKey = sessions2.map((s) => s.sessionId).sort().join(",");
|
|
2838
3556
|
const sessionIdSet = new Set(sessions2.map((s) => s.sessionId));
|
|
2839
|
-
|
|
3557
|
+
useEffect7(() => {
|
|
2840
3558
|
setCalls([]);
|
|
2841
3559
|
setResults([]);
|
|
2842
3560
|
if (sessions2.length === 0) return;
|
|
@@ -2870,7 +3588,7 @@ var useActivityStream = (session, allUsers) => {
|
|
|
2870
3588
|
const resultHandler = (newResults) => {
|
|
2871
3589
|
const matched = newResults.filter((r) => sessionIdSet.has(r.sessionId));
|
|
2872
3590
|
if (matched.length === 0) return;
|
|
2873
|
-
setResults((prev) => [...prev, ...matched]);
|
|
3591
|
+
setResults((prev) => [...prev, ...matched].slice(-MAX_EVENTS * 2));
|
|
2874
3592
|
};
|
|
2875
3593
|
const watcher = new Watcher(callHandler, allUsers, void 0, void 0, resultHandler);
|
|
2876
3594
|
watcherRef.current = watcher;
|
|
@@ -2905,10 +3623,10 @@ var applyFilter = (events, filter) => {
|
|
|
2905
3623
|
var useFilteredEvents = (rawEvents, filter) => useMemo4(() => applyFilter(rawEvents, filter), [rawEvents, filter]);
|
|
2906
3624
|
|
|
2907
3625
|
// src/ui/hooks/useAlerts.ts
|
|
2908
|
-
import { useState as
|
|
3626
|
+
import { useState as useState13, useEffect as useEffect8, useRef as useRef6 } from "react";
|
|
2909
3627
|
|
|
2910
3628
|
// src/notifications.ts
|
|
2911
|
-
import {
|
|
3629
|
+
import { execFile as execFile2 } from "child_process";
|
|
2912
3630
|
import { platform } from "os";
|
|
2913
3631
|
var SEVERITY_ORDER = {
|
|
2914
3632
|
info: 0,
|
|
@@ -2931,11 +3649,23 @@ var notify = (alert, config) => {
|
|
|
2931
3649
|
const body = `${alert.sessionSlug}: ${alert.message.slice(0, 100)}`;
|
|
2932
3650
|
const os = platform();
|
|
2933
3651
|
if (os === "linux") {
|
|
2934
|
-
|
|
3652
|
+
execFile2("notify-send", [title, body], { timeout: 3e3 });
|
|
2935
3653
|
} else if (os === "darwin") {
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
3654
|
+
execFile2(
|
|
3655
|
+
"osascript",
|
|
3656
|
+
["-e", `display notification ${JSON.stringify(body)} with title ${JSON.stringify(title)}`],
|
|
3657
|
+
{ timeout: 3e3 }
|
|
3658
|
+
);
|
|
3659
|
+
} else if (os === "win32") {
|
|
3660
|
+
execFile2(
|
|
3661
|
+
"powershell",
|
|
3662
|
+
[
|
|
3663
|
+
"-NoProfile",
|
|
3664
|
+
"-Command",
|
|
3665
|
+
`Add-Type -AssemblyName System.Windows.Forms; $n = New-Object System.Windows.Forms.NotifyIcon; $n.Icon = [System.Drawing.SystemIcons]::Information; $n.Visible = $true; $n.ShowBalloonTip(5000, ${JSON.stringify(title)}, ${JSON.stringify(body)}, 'Warning'); Start-Sleep -Seconds 6; $n.Dispose()`
|
|
3666
|
+
],
|
|
3667
|
+
{ timeout: 1e4 }
|
|
3668
|
+
);
|
|
2939
3669
|
}
|
|
2940
3670
|
}
|
|
2941
3671
|
};
|
|
@@ -2973,11 +3703,11 @@ var AlertLogger = class {
|
|
|
2973
3703
|
// src/ui/hooks/useAlerts.ts
|
|
2974
3704
|
var MAX_ALERTS = 100;
|
|
2975
3705
|
var useAlerts = (enabled, alertLevel, allUsers, config) => {
|
|
2976
|
-
const [alerts, setAlerts] =
|
|
2977
|
-
const engineRef =
|
|
2978
|
-
const watcherRef =
|
|
2979
|
-
const loggerRef =
|
|
2980
|
-
|
|
3706
|
+
const [alerts, setAlerts] = useState13([]);
|
|
3707
|
+
const engineRef = useRef6(new SecurityEngine(alertLevel));
|
|
3708
|
+
const watcherRef = useRef6(null);
|
|
3709
|
+
const loggerRef = useRef6(null);
|
|
3710
|
+
useEffect8(() => {
|
|
2981
3711
|
if (!enabled) return;
|
|
2982
3712
|
engineRef.current = new SecurityEngine(alertLevel);
|
|
2983
3713
|
if (config?.alerts.enabled) {
|
|
@@ -3007,16 +3737,16 @@ var useAlerts = (enabled, alertLevel, allUsers, config) => {
|
|
|
3007
3737
|
watcherRef.current = null;
|
|
3008
3738
|
loggerRef.current = null;
|
|
3009
3739
|
};
|
|
3010
|
-
}, [enabled, alertLevel, allUsers]);
|
|
3740
|
+
}, [enabled, alertLevel, allUsers, config]);
|
|
3011
3741
|
const clearAlerts = () => setAlerts([]);
|
|
3012
3742
|
return { alerts, clearAlerts };
|
|
3013
3743
|
};
|
|
3014
3744
|
|
|
3015
3745
|
// src/ui/hooks/useTextInput.ts
|
|
3016
|
-
import { useState as
|
|
3746
|
+
import { useState as useState14, useCallback as useCallback4 } from "react";
|
|
3017
3747
|
var useTextInput = (onConfirm, onCancel) => {
|
|
3018
|
-
const [value, setValue] =
|
|
3019
|
-
const [isActive, setIsActive] =
|
|
3748
|
+
const [value, setValue] = useState14("");
|
|
3749
|
+
const [isActive, setIsActive] = useState14(false);
|
|
3020
3750
|
const start = useCallback4((initial = "") => {
|
|
3021
3751
|
setValue(initial);
|
|
3022
3752
|
setIsActive(true);
|
|
@@ -3066,16 +3796,17 @@ var useTextInput = (onConfirm, onCancel) => {
|
|
|
3066
3796
|
};
|
|
3067
3797
|
|
|
3068
3798
|
// src/ui/hooks/useKeyHandler.ts
|
|
3069
|
-
import { useInput as
|
|
3799
|
+
import { useInput as useInput11 } from "ink";
|
|
3070
3800
|
var matchKey = (binding, input, key) => {
|
|
3071
3801
|
if (binding === "tab") return Boolean(key.tab);
|
|
3072
3802
|
if (binding === "shift+tab") return Boolean(key.shift && key.tab);
|
|
3073
3803
|
if (binding === "enter") return Boolean(key.return);
|
|
3804
|
+
if (binding.startsWith("ctrl+")) return Boolean(key.ctrl) && input === binding.slice(5);
|
|
3074
3805
|
return input === binding;
|
|
3075
3806
|
};
|
|
3076
3807
|
var useKeyHandler = (deps) => {
|
|
3077
3808
|
const d = deps;
|
|
3078
|
-
|
|
3809
|
+
useInput11((input, key) => {
|
|
3079
3810
|
if (d.showSetup || d.showSettings || d.confirmAction || d.showUpdateModal) return;
|
|
3080
3811
|
if (d.inputMode === "nickname") {
|
|
3081
3812
|
d.nicknameInput.handleInput(input, key);
|
|
@@ -3246,10 +3977,26 @@ var useKeyHandler = (deps) => {
|
|
|
3246
3977
|
d.setShowSettings(true);
|
|
3247
3978
|
return;
|
|
3248
3979
|
}
|
|
3980
|
+
if (matchKey(d.kb.alertRules, input, key)) {
|
|
3981
|
+
d.setShowAlertRules(true);
|
|
3982
|
+
return;
|
|
3983
|
+
}
|
|
3249
3984
|
if (matchKey(d.kb.viewArchive, input, key)) {
|
|
3250
3985
|
d.setViewingArchive((v) => !v);
|
|
3251
3986
|
return;
|
|
3252
3987
|
}
|
|
3988
|
+
if (matchKey(d.kb.pin, input, key) && d.selectedSession) {
|
|
3989
|
+
d.onTogglePin(d.selectedSession.sessionId);
|
|
3990
|
+
return;
|
|
3991
|
+
}
|
|
3992
|
+
if (matchKey(d.kb.pinMoveUp, input, key) && d.selectedSession?.pinned) {
|
|
3993
|
+
d.onMovePinned(d.selectedSession.sessionId, "up");
|
|
3994
|
+
return;
|
|
3995
|
+
}
|
|
3996
|
+
if (matchKey(d.kb.pinMoveDown, input, key) && d.selectedSession?.pinned) {
|
|
3997
|
+
d.onMovePinned(d.selectedSession.sessionId, "down");
|
|
3998
|
+
return;
|
|
3999
|
+
}
|
|
3253
4000
|
if (matchKey(d.kb.archive, input, key) && d.selectedSession) {
|
|
3254
4001
|
if (d.viewingArchive) d.onUnarchive(d.selectedSession.sessionId);
|
|
3255
4002
|
else d.onArchive(d.selectedSession.sessionId);
|
|
@@ -3308,10 +4055,10 @@ var useKeyHandler = (deps) => {
|
|
|
3308
4055
|
};
|
|
3309
4056
|
|
|
3310
4057
|
// src/ui/hooks/useUpdateChecker.ts
|
|
3311
|
-
import { useState as
|
|
4058
|
+
import { useState as useState15, useEffect as useEffect9 } from "react";
|
|
3312
4059
|
var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
|
|
3313
|
-
const [updateInfo, setUpdateInfo] =
|
|
3314
|
-
|
|
4060
|
+
const [updateInfo, setUpdateInfo] = useState15(null);
|
|
4061
|
+
useEffect9(() => {
|
|
3315
4062
|
if (disabled || !checkOnLaunch) return;
|
|
3316
4063
|
let cancelled = false;
|
|
3317
4064
|
const check = () => {
|
|
@@ -3326,12 +4073,12 @@ var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
|
|
|
3326
4073
|
cancelled = true;
|
|
3327
4074
|
clearInterval(iv);
|
|
3328
4075
|
};
|
|
3329
|
-
}, []);
|
|
4076
|
+
}, [disabled, checkOnLaunch, checkInterval]);
|
|
3330
4077
|
return updateInfo;
|
|
3331
4078
|
};
|
|
3332
4079
|
|
|
3333
4080
|
// src/ui/hooks/useSetupFlow.ts
|
|
3334
|
-
import { useState as
|
|
4081
|
+
import { useState as useState16, useCallback as useCallback5 } from "react";
|
|
3335
4082
|
|
|
3336
4083
|
// src/hooks/installer.ts
|
|
3337
4084
|
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync as mkdirSync2, chmodSync } from "fs";
|
|
@@ -3451,12 +4198,12 @@ var installMcpConfig = () => {
|
|
|
3451
4198
|
|
|
3452
4199
|
// src/ui/hooks/useSetupFlow.ts
|
|
3453
4200
|
var useSetupFlow = (initialConfig, firstRun) => {
|
|
3454
|
-
const [liveConfig, setLiveConfig] =
|
|
3455
|
-
const [showSetup, setShowSetup] =
|
|
3456
|
-
const [showThemePicker, setShowThemePicker] =
|
|
3457
|
-
const [showTour, setShowTour] =
|
|
3458
|
-
const [showSettings, setShowSettings] =
|
|
3459
|
-
const [showThemeMenu, setShowThemeMenu] =
|
|
4201
|
+
const [liveConfig, setLiveConfig] = useState16(initialConfig);
|
|
4202
|
+
const [showSetup, setShowSetup] = useState16(firstRun);
|
|
4203
|
+
const [showThemePicker, setShowThemePicker] = useState16(false);
|
|
4204
|
+
const [showTour, setShowTour] = useState16(false);
|
|
4205
|
+
const [showSettings, setShowSettings] = useState16(false);
|
|
4206
|
+
const [showThemeMenu, setShowThemeMenu] = useState16(false);
|
|
3460
4207
|
const handleSettingsClose = useCallback5((c) => {
|
|
3461
4208
|
setLiveConfig(c);
|
|
3462
4209
|
saveConfig(c);
|
|
@@ -3547,17 +4294,17 @@ var useSetupFlow = (initialConfig, firstRun) => {
|
|
|
3547
4294
|
};
|
|
3548
4295
|
|
|
3549
4296
|
// src/ui/hooks/useSplitPanel.ts
|
|
3550
|
-
import { useState as
|
|
4297
|
+
import { useState as useState17, useCallback as useCallback6 } from "react";
|
|
3551
4298
|
var useSplitPanel = () => {
|
|
3552
|
-
const [splitMode, setSplitMode] =
|
|
3553
|
-
const [leftSession, setLeftSession] =
|
|
3554
|
-
const [rightSession, setRightSession] =
|
|
3555
|
-
const [leftScroll, setLeftScroll] =
|
|
3556
|
-
const [rightScroll, setRightScroll] =
|
|
3557
|
-
const [leftFilter, setLeftFilter] =
|
|
3558
|
-
const [rightFilter, setRightFilter] =
|
|
3559
|
-
const [leftShowDetail, setLeftShowDetail] =
|
|
3560
|
-
const [rightShowDetail, setRightShowDetail] =
|
|
4299
|
+
const [splitMode, setSplitMode] = useState17(false);
|
|
4300
|
+
const [leftSession, setLeftSession] = useState17(null);
|
|
4301
|
+
const [rightSession, setRightSession] = useState17(null);
|
|
4302
|
+
const [leftScroll, setLeftScroll] = useState17(0);
|
|
4303
|
+
const [rightScroll, setRightScroll] = useState17(0);
|
|
4304
|
+
const [leftFilter, setLeftFilter] = useState17("");
|
|
4305
|
+
const [rightFilter, setRightFilter] = useState17("");
|
|
4306
|
+
const [leftShowDetail, setLeftShowDetail] = useState17(false);
|
|
4307
|
+
const [rightShowDetail, setRightShowDetail] = useState17(false);
|
|
3561
4308
|
const clearSplitState = useCallback6(() => {
|
|
3562
4309
|
setSplitMode(false);
|
|
3563
4310
|
setLeftSession(null);
|
|
@@ -3623,30 +4370,31 @@ var useSplitPanel = () => {
|
|
|
3623
4370
|
};
|
|
3624
4371
|
|
|
3625
4372
|
// src/ui/App.tsx
|
|
3626
|
-
import { jsx as
|
|
4373
|
+
import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
3627
4374
|
var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
3628
4375
|
const { exit } = useApp();
|
|
3629
|
-
const { stdout } =
|
|
4376
|
+
const { stdout } = useStdout4();
|
|
3630
4377
|
const termHeight = stdout?.rows ?? 40;
|
|
3631
4378
|
const setup = useSetupFlow(initialConfig, firstRun);
|
|
3632
4379
|
const split = useSplitPanel();
|
|
3633
4380
|
const kb = setup.liveConfig.keybindings;
|
|
3634
|
-
const [activePanel, setActivePanel] =
|
|
3635
|
-
const [activityScroll, setActivityScroll] =
|
|
3636
|
-
const [inputMode, setInputMode] =
|
|
3637
|
-
const [filter, setFilter] =
|
|
3638
|
-
const [activityFilter, setActivityFilter] =
|
|
3639
|
-
const [updateStatus, setUpdateStatus] =
|
|
3640
|
-
const [showDetail, setShowDetail] =
|
|
3641
|
-
const [viewingArchive, setViewingArchive] =
|
|
3642
|
-
const [confirmAction, setConfirmAction] =
|
|
4381
|
+
const [activePanel, setActivePanel] = useState18("sessions");
|
|
4382
|
+
const [activityScroll, setActivityScroll] = useState18(0);
|
|
4383
|
+
const [inputMode, setInputMode] = useState18("normal");
|
|
4384
|
+
const [filter, setFilter] = useState18("");
|
|
4385
|
+
const [activityFilter, setActivityFilter] = useState18("");
|
|
4386
|
+
const [updateStatus, setUpdateStatus] = useState18("");
|
|
4387
|
+
const [showDetail, setShowDetail] = useState18(false);
|
|
4388
|
+
const [viewingArchive, setViewingArchive] = useState18(false);
|
|
4389
|
+
const [confirmAction, setConfirmAction] = useState18(
|
|
3643
4390
|
null
|
|
3644
4391
|
);
|
|
3645
|
-
const [archivedIds, setArchivedIds] =
|
|
3646
|
-
const [sidebarWidth, setSidebarWidth] =
|
|
3647
|
-
const [selectedEventIndex, setSelectedEventIndex] =
|
|
3648
|
-
const [showEventDetail, setShowEventDetail] =
|
|
3649
|
-
const [showUpdateModal, setShowUpdateModal] =
|
|
4392
|
+
const [archivedIds, setArchivedIds] = useState18(() => new Set(Object.keys(getArchived())));
|
|
4393
|
+
const [sidebarWidth, setSidebarWidth] = useState18(() => initialConfig.sidebarWidth ?? 30);
|
|
4394
|
+
const [selectedEventIndex, setSelectedEventIndex] = useState18(0);
|
|
4395
|
+
const [showEventDetail, setShowEventDetail] = useState18(false);
|
|
4396
|
+
const [showUpdateModal, setShowUpdateModal] = useState18(false);
|
|
4397
|
+
const [showAlertRules, setShowAlertRules] = useState18(false);
|
|
3650
4398
|
const refreshArchived = useCallback7(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
|
|
3651
4399
|
const persistSidebarWidth = useCallback7((v) => {
|
|
3652
4400
|
setSidebarWidth((prev) => {
|
|
@@ -3664,10 +4412,10 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3664
4412
|
setup.liveConfig.updates.checkOnLaunch,
|
|
3665
4413
|
setup.liveConfig.updates.checkInterval
|
|
3666
4414
|
);
|
|
3667
|
-
|
|
4415
|
+
useEffect10(() => {
|
|
3668
4416
|
applyTheme(resolveTheme(setup.liveConfig.theme, setup.liveConfig.customThemes));
|
|
3669
4417
|
}, [setup.liveConfig.theme, setup.liveConfig.customThemes]);
|
|
3670
|
-
|
|
4418
|
+
useEffect10(() => {
|
|
3671
4419
|
if (updateInfo?.available && setup.liveConfig.prompts.autoUpdate === "pending") {
|
|
3672
4420
|
setShowUpdateModal(true);
|
|
3673
4421
|
}
|
|
@@ -3717,16 +4465,16 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3717
4465
|
setInputMode("normal");
|
|
3718
4466
|
}
|
|
3719
4467
|
);
|
|
3720
|
-
|
|
4468
|
+
useEffect10(() => {
|
|
3721
4469
|
purgeExpiredArchives();
|
|
3722
4470
|
refreshArchived();
|
|
3723
4471
|
}, []);
|
|
3724
|
-
|
|
4472
|
+
useEffect10(() => {
|
|
3725
4473
|
setActivityScroll(0);
|
|
3726
4474
|
setSelectedEventIndex(0);
|
|
3727
4475
|
setShowEventDetail(false);
|
|
3728
4476
|
}, [selectedSession?.sessionId, selectedGroup?.key]);
|
|
3729
|
-
|
|
4477
|
+
useEffect10(() => {
|
|
3730
4478
|
const totalEvents = events.length;
|
|
3731
4479
|
const vRows = termHeight - 3 - (options2.noSecurity ? 0 : 6) - 1 - (inputMode !== "normal" ? 1 : 0) - 2;
|
|
3732
4480
|
if (vRows <= 0 || totalEvents === 0) return;
|
|
@@ -3738,7 +4486,7 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3738
4486
|
setActivityScroll(totalEvents - vRows - (selectedEventIndex - vRows + 1));
|
|
3739
4487
|
}
|
|
3740
4488
|
}, [selectedEventIndex, events.length]);
|
|
3741
|
-
|
|
4489
|
+
useEffect10(() => {
|
|
3742
4490
|
if (events.length > 0 && selectedEventIndex >= events.length) {
|
|
3743
4491
|
setSelectedEventIndex(events.length - 1);
|
|
3744
4492
|
}
|
|
@@ -3760,13 +4508,32 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3760
4508
|
if (activePanel === "right") return split.rightFilter;
|
|
3761
4509
|
return activityFilter;
|
|
3762
4510
|
}, [activePanel, filter, split.leftFilter, split.rightFilter, activityFilter]);
|
|
4511
|
+
const handleTogglePin = useCallback7(
|
|
4512
|
+
(sessionId) => {
|
|
4513
|
+
const cfg = loadConfig();
|
|
4514
|
+
if (cfg.pinnedSessions.includes(sessionId)) {
|
|
4515
|
+
unpinSession(sessionId);
|
|
4516
|
+
} else {
|
|
4517
|
+
pinSession(sessionId);
|
|
4518
|
+
}
|
|
4519
|
+
refresh();
|
|
4520
|
+
},
|
|
4521
|
+
[refresh]
|
|
4522
|
+
);
|
|
4523
|
+
const handleMovePinned = useCallback7(
|
|
4524
|
+
(sessionId, dir) => {
|
|
4525
|
+
movePinned(sessionId, dir);
|
|
4526
|
+
refresh();
|
|
4527
|
+
},
|
|
4528
|
+
[refresh]
|
|
4529
|
+
);
|
|
3763
4530
|
useKeyHandler({
|
|
3764
4531
|
kb,
|
|
3765
4532
|
activePanel,
|
|
3766
4533
|
splitMode: split.splitMode,
|
|
3767
4534
|
inputMode,
|
|
3768
4535
|
showSetup: setup.showSetup,
|
|
3769
|
-
showSettings: setup.showSettings || setup.showThemeMenu || setup.showThemePicker || setup.showTour,
|
|
4536
|
+
showSettings: setup.showSettings || setup.showThemeMenu || setup.showThemePicker || setup.showTour || showAlertRules,
|
|
3770
4537
|
showDetail,
|
|
3771
4538
|
showEventDetail,
|
|
3772
4539
|
leftShowDetail: split.leftShowDetail,
|
|
@@ -3810,6 +4577,8 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3810
4577
|
setLeftShowDetail: split.setLeftShowDetail,
|
|
3811
4578
|
setRightShowDetail: split.setRightShowDetail,
|
|
3812
4579
|
setShowSettings: setup.setShowSettings,
|
|
4580
|
+
showAlertRules,
|
|
4581
|
+
setShowAlertRules,
|
|
3813
4582
|
setViewingArchive,
|
|
3814
4583
|
setSplitMode: split.setSplitMode,
|
|
3815
4584
|
setLeftSession: split.setLeftSession,
|
|
@@ -3824,14 +4593,12 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3824
4593
|
setSidebarWidth: persistSidebarWidth,
|
|
3825
4594
|
nicknameInput,
|
|
3826
4595
|
filterInput,
|
|
3827
|
-
onNickname: (id) => {
|
|
3828
|
-
clearNickname(id);
|
|
3829
|
-
refresh();
|
|
3830
|
-
},
|
|
3831
4596
|
onClearNickname: (id) => {
|
|
3832
4597
|
clearNickname(id);
|
|
3833
4598
|
refresh();
|
|
3834
4599
|
},
|
|
4600
|
+
onTogglePin: handleTogglePin,
|
|
4601
|
+
onMovePinned: handleMovePinned,
|
|
3835
4602
|
onArchive: (id) => {
|
|
3836
4603
|
archiveSession(id);
|
|
3837
4604
|
refreshArchived();
|
|
@@ -3883,10 +4650,10 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3883
4650
|
setup.setShowSetup(false);
|
|
3884
4651
|
return null;
|
|
3885
4652
|
}
|
|
3886
|
-
return /* @__PURE__ */
|
|
4653
|
+
return /* @__PURE__ */ jsx18(SetupModal, { steps, onComplete: setup.handleSetupComplete });
|
|
3887
4654
|
}
|
|
3888
4655
|
if (setup.showThemePicker)
|
|
3889
|
-
return /* @__PURE__ */
|
|
4656
|
+
return /* @__PURE__ */ jsx18(
|
|
3890
4657
|
ThemePickerModal,
|
|
3891
4658
|
{
|
|
3892
4659
|
onSelect: setup.handleThemePickerSelect,
|
|
@@ -3894,10 +4661,22 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3894
4661
|
onDismiss: setup.handleThemePickerDismiss
|
|
3895
4662
|
}
|
|
3896
4663
|
);
|
|
3897
|
-
if (setup.showTour) return /* @__PURE__ */
|
|
3898
|
-
if (setup.showThemeMenu) return /* @__PURE__ */
|
|
4664
|
+
if (setup.showTour) return /* @__PURE__ */ jsx18(GuidedTour, { onComplete: setup.handleTourComplete, onSkip: setup.handleTourSkip });
|
|
4665
|
+
if (setup.showThemeMenu) return /* @__PURE__ */ jsx18(ThemeMenu, { config: setup.liveConfig, onClose: setup.handleThemeMenuClose });
|
|
4666
|
+
if (showAlertRules)
|
|
4667
|
+
return /* @__PURE__ */ jsx18(
|
|
4668
|
+
AlertRulesMenu,
|
|
4669
|
+
{
|
|
4670
|
+
config: setup.liveConfig,
|
|
4671
|
+
onClose: () => setShowAlertRules(false),
|
|
4672
|
+
onSave: (newConfig) => {
|
|
4673
|
+
saveConfig(newConfig);
|
|
4674
|
+
setup.setLiveConfig(newConfig);
|
|
4675
|
+
}
|
|
4676
|
+
}
|
|
4677
|
+
);
|
|
3899
4678
|
if (setup.showSettings)
|
|
3900
|
-
return /* @__PURE__ */
|
|
4679
|
+
return /* @__PURE__ */ jsx18(
|
|
3901
4680
|
SettingsMenu,
|
|
3902
4681
|
{
|
|
3903
4682
|
config: setup.liveConfig,
|
|
@@ -3906,7 +4685,7 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3906
4685
|
}
|
|
3907
4686
|
);
|
|
3908
4687
|
if (showUpdateModal && updateInfo?.available) {
|
|
3909
|
-
return /* @__PURE__ */
|
|
4688
|
+
return /* @__PURE__ */ jsx18(Box18, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx18(
|
|
3910
4689
|
UpdateModal,
|
|
3911
4690
|
{
|
|
3912
4691
|
current: updateInfo.current,
|
|
@@ -3923,7 +4702,7 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3923
4702
|
) });
|
|
3924
4703
|
}
|
|
3925
4704
|
if (confirmAction) {
|
|
3926
|
-
return /* @__PURE__ */
|
|
4705
|
+
return /* @__PURE__ */ jsx18(Box18, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx18(
|
|
3927
4706
|
ConfirmModal,
|
|
3928
4707
|
{
|
|
3929
4708
|
title: confirmAction.title,
|
|
@@ -3936,7 +4715,7 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3936
4715
|
const activitySlug = selectedGroup ? selectedGroup.key : selectedSession?.slug ?? null;
|
|
3937
4716
|
const isMerged = selectedGroup !== null;
|
|
3938
4717
|
const selectedEvent = events[selectedEventIndex];
|
|
3939
|
-
const rightPanel = split.splitMode ? /* @__PURE__ */
|
|
4718
|
+
const rightPanel = split.splitMode ? /* @__PURE__ */ jsx18(
|
|
3940
4719
|
SplitPanel,
|
|
3941
4720
|
{
|
|
3942
4721
|
activePanel,
|
|
@@ -3952,13 +4731,13 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3952
4731
|
rightShowDetail: split.rightShowDetail,
|
|
3953
4732
|
height: mainHeight
|
|
3954
4733
|
}
|
|
3955
|
-
) : showEventDetail && selectedEvent ? /* @__PURE__ */
|
|
4734
|
+
) : showEventDetail && selectedEvent ? /* @__PURE__ */ jsx18(ToolCallDetail, { event: selectedEvent, focused: activePanel === "activity", height: mainHeight }) : showDetail && selectedSession ? /* @__PURE__ */ jsx18(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx18(
|
|
3956
4735
|
ActivityFeed,
|
|
3957
4736
|
{
|
|
3958
4737
|
events,
|
|
3959
4738
|
sessionSlug: activitySlug,
|
|
3960
4739
|
sessionId: selectedSession?.sessionId,
|
|
3961
|
-
|
|
4740
|
+
status: selectedGroup ? selectedGroup.status : selectedSession ? selectedSession.status : void 0,
|
|
3962
4741
|
focused: activePanel === "activity",
|
|
3963
4742
|
height: mainHeight,
|
|
3964
4743
|
scrollOffset: activityScroll,
|
|
@@ -3968,10 +4747,10 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3968
4747
|
selectedEventIndex
|
|
3969
4748
|
}
|
|
3970
4749
|
);
|
|
3971
|
-
return /* @__PURE__ */
|
|
3972
|
-
/* @__PURE__ */
|
|
3973
|
-
/* @__PURE__ */
|
|
3974
|
-
/* @__PURE__ */
|
|
4750
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", height: termHeight, children: [
|
|
4751
|
+
/* @__PURE__ */ jsx18(StatusBar, { sessionCount: sessions2.length, alertCount: alerts.length, version, updateInfo }),
|
|
4752
|
+
/* @__PURE__ */ jsxs18(Box18, { flexGrow: 1, height: mainHeight, children: [
|
|
4753
|
+
/* @__PURE__ */ jsx18(
|
|
3975
4754
|
SessionList,
|
|
3976
4755
|
{
|
|
3977
4756
|
visibleItems,
|
|
@@ -3984,21 +4763,21 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
3984
4763
|
sidebarWidth
|
|
3985
4764
|
}
|
|
3986
4765
|
),
|
|
3987
|
-
/* @__PURE__ */
|
|
4766
|
+
/* @__PURE__ */ jsx18(Box18, { flexGrow: 1, flexShrink: 1, minWidth: 0, overflow: "hidden", children: rightPanel })
|
|
3988
4767
|
] }),
|
|
3989
|
-
!options2.noSecurity && /* @__PURE__ */
|
|
3990
|
-
inputMode === "nickname" && /* @__PURE__ */
|
|
3991
|
-
/* @__PURE__ */
|
|
3992
|
-
/* @__PURE__ */
|
|
3993
|
-
/* @__PURE__ */
|
|
4768
|
+
!options2.noSecurity && /* @__PURE__ */ jsx18(AlertBar, { alerts }),
|
|
4769
|
+
inputMode === "nickname" && /* @__PURE__ */ jsxs18(Box18, { paddingX: 1, children: [
|
|
4770
|
+
/* @__PURE__ */ jsx18(Text17, { color: colors.primary, children: "nickname: " }),
|
|
4771
|
+
/* @__PURE__ */ jsx18(Text17, { color: colors.bright, children: nicknameInput.value }),
|
|
4772
|
+
/* @__PURE__ */ jsx18(Text17, { color: colors.muted, children: "_" })
|
|
3994
4773
|
] }),
|
|
3995
|
-
inputMode === "filter" && /* @__PURE__ */
|
|
3996
|
-
/* @__PURE__ */
|
|
3997
|
-
/* @__PURE__ */
|
|
3998
|
-
/* @__PURE__ */
|
|
3999
|
-
/* @__PURE__ */
|
|
4774
|
+
inputMode === "filter" && /* @__PURE__ */ jsxs18(Box18, { paddingX: 1, children: [
|
|
4775
|
+
/* @__PURE__ */ jsx18(Text17, { color: colors.muted, children: activePanel === "sessions" ? "sessions" : activePanel === "left" ? "left" : activePanel === "right" ? "right" : "activity" }),
|
|
4776
|
+
/* @__PURE__ */ jsx18(Text17, { color: colors.primary, children: "/" }),
|
|
4777
|
+
/* @__PURE__ */ jsx18(Text17, { color: colors.bright, children: filterInput.value }),
|
|
4778
|
+
/* @__PURE__ */ jsx18(Text17, { color: colors.muted, children: "_" })
|
|
4000
4779
|
] }),
|
|
4001
|
-
inputMode === "normal" && /* @__PURE__ */
|
|
4780
|
+
inputMode === "normal" && /* @__PURE__ */ jsx18(
|
|
4002
4781
|
FooterBar,
|
|
4003
4782
|
{
|
|
4004
4783
|
keybindings: kb,
|
|
@@ -4014,11 +4793,6 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
|
|
|
4014
4793
|
var write = (msg) => {
|
|
4015
4794
|
process.stdout.write(msg + "\n");
|
|
4016
4795
|
};
|
|
4017
|
-
var formatTokens3 = (n) => {
|
|
4018
|
-
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
|
4019
|
-
if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
|
|
4020
|
-
return String(n);
|
|
4021
|
-
};
|
|
4022
4796
|
var runStreamMode = (options2, isJson) => {
|
|
4023
4797
|
const engine = options2.noSecurity ? null : new SecurityEngine(options2.alertLevel);
|
|
4024
4798
|
const sessions2 = discoverSessions(options2.allUsers);
|
|
@@ -4027,7 +4801,7 @@ var runStreamMode = (options2, isJson) => {
|
|
|
4027
4801
|
} else {
|
|
4028
4802
|
for (const s of sessions2) {
|
|
4029
4803
|
write(
|
|
4030
|
-
`SESSION ${s.slug} | ${s.model} | ${s.cwd} | CPU ${s.cpu}% | ${s.memMB}MB | ${
|
|
4804
|
+
`SESSION ${s.slug} | ${s.status} | ${s.model} | ${s.cwd} | CPU ${s.cpu}% | ${s.memMB}MB | ${formatTokens(s.usage.inputTokens)} in / ${formatTokens(s.usage.outputTokens)} out`
|
|
4031
4805
|
);
|
|
4032
4806
|
}
|
|
4033
4807
|
}
|
|
@@ -4060,7 +4834,7 @@ var runStreamMode = (options2, isJson) => {
|
|
|
4060
4834
|
write(JSON.stringify({ type: "usage", data: { sessionId, usage } }));
|
|
4061
4835
|
} else {
|
|
4062
4836
|
write(
|
|
4063
|
-
`USAGE ${sessionId.slice(0, 12)} +${
|
|
4837
|
+
`USAGE ${sessionId.slice(0, 12)} +${formatTokens(usage.inputTokens)} in / +${formatTokens(usage.outputTokens)} out`
|
|
4064
4838
|
);
|
|
4065
4839
|
}
|
|
4066
4840
|
};
|
|
@@ -4244,7 +5018,7 @@ var main = () => {
|
|
|
4244
5018
|
if (options2.noUpdates) config.updates.checkOnLaunch = false;
|
|
4245
5019
|
if (options2.noSecurity) config.security.enabled = false;
|
|
4246
5020
|
if (firstRun) saveConfig(config);
|
|
4247
|
-
render(
|
|
5021
|
+
render(React19.createElement(App, { options: options2, config, version: VERSION, firstRun }));
|
|
4248
5022
|
};
|
|
4249
5023
|
main();
|
|
4250
5024
|
//# sourceMappingURL=index.js.map
|