agenttop 0.4.0 → 0.7.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 +89 -7
- package/dist/{chunk-H2JOTO54.js → chunk-FCCWHOJO.js} +58 -5
- package/dist/chunk-FCCWHOJO.js.map +1 -0
- package/dist/index.js +1742 -326
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-H2JOTO54.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
[](https://nodejs.org)
|
|
8
8
|
[](https://www.typescriptlang.org/)
|
|
9
9
|
[](https://github.com/wrxck/agenttop/pulls)
|
|
10
|
-
[](https://github.com/wrxck/agenttop)
|
|
11
11
|
|
|
12
12
|
Real-time terminal dashboard for monitoring AI coding agent sessions — like `htop` for agents.
|
|
13
13
|
|
|
14
14
|
Currently supports [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Reads local session data only — no network requests, no API keys.
|
|
15
15
|
|
|
16
|
+
Read more about [why agenttop was built](https://blog.matthesketh.pro/blog/agenttop).
|
|
17
|
+
|
|
16
18
|
> **Experimental** — this project is under active development. APIs and configuration may change between releases. Feature requests, bug reports, and contributions are very welcome. See [Contributing](#contributing).
|
|
17
19
|
|
|
18
20
|
## Table of contents
|
|
@@ -22,7 +24,10 @@ Currently supports [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
|
|
|
22
24
|
- [Token usage](#token-usage)
|
|
23
25
|
- [Session nicknames](#session-nicknames)
|
|
24
26
|
- [Session filtering](#session-filtering)
|
|
27
|
+
- [Session management](#session-management)
|
|
25
28
|
- [Session detail view](#session-detail-view)
|
|
29
|
+
- [Split view](#split-view)
|
|
30
|
+
- [Themes](#themes)
|
|
26
31
|
- [Streaming modes](#streaming-modes)
|
|
27
32
|
- [Active protection](#active-protection)
|
|
28
33
|
- [Security rules](#security-rules)
|
|
@@ -55,7 +60,7 @@ npm install -g agenttop
|
|
|
55
60
|
Run `agenttop` in one terminal while running Claude Code sessions in other tabs.
|
|
56
61
|
|
|
57
62
|
```
|
|
58
|
-
-- agenttop v0.
|
|
63
|
+
-- agenttop v0.6.0 ---- 3 sessions ---- 14:32:08 ---------------------
|
|
59
64
|
| SESSIONS | ACTIVITY (cuddly-wiggling-sundae) |
|
|
60
65
|
| | |
|
|
61
66
|
| > cuddly-wiggling-sundae | 14:32:05 Bash ls /tmp/claude-0/ |
|
|
@@ -117,9 +122,30 @@ Nicknames are stored in `~/.config/agenttop/config.json`.
|
|
|
117
122
|
|
|
118
123
|
## Session filtering
|
|
119
124
|
|
|
120
|
-
Press `/` to open the filter input.
|
|
125
|
+
Press `/` to open the filter input. The filter applies to whichever panel is focused:
|
|
126
|
+
|
|
127
|
+
- **Sessions panel** — filters by slug, nickname, project, or model
|
|
128
|
+
- **Activity panel** — filters by tool name or tool input content
|
|
129
|
+
|
|
130
|
+
Case-insensitive substring match. Press `Esc` to clear the filter.
|
|
131
|
+
|
|
132
|
+
## Session management
|
|
133
|
+
|
|
134
|
+
### Archive
|
|
135
|
+
|
|
136
|
+
Press `a` to archive a session. Archived sessions are hidden from the main view but not deleted.
|
|
121
137
|
|
|
122
|
-
Press `
|
|
138
|
+
Press `A` to toggle the archive view, which shows only archived sessions. In the archive view, press `a` to restore a session back to the main view.
|
|
139
|
+
|
|
140
|
+
Archive expiry can be configured in settings (`s`) under GENERAL — options are never, 7d, 14d, 30d, 60d, or 90d. Expired archives are purged on launch.
|
|
141
|
+
|
|
142
|
+
### Delete
|
|
143
|
+
|
|
144
|
+
Press `d` to delete a session. A confirmation prompt appears. On confirm, the session's output files are removed from disk and any nickname or archive entry is cleared.
|
|
145
|
+
|
|
146
|
+
### Active/inactive indicators
|
|
147
|
+
|
|
148
|
+
Sessions with a running process are shown in green. Inactive sessions (no process) are shown in red.
|
|
123
149
|
|
|
124
150
|
## Session detail view
|
|
125
151
|
|
|
@@ -131,6 +157,39 @@ Press `Enter` on a session to open the detail view, which shows:
|
|
|
131
157
|
|
|
132
158
|
Press `Esc` or `Enter` to return to the activity feed.
|
|
133
159
|
|
|
160
|
+
## Split view
|
|
161
|
+
|
|
162
|
+
Press `x` to toggle split-screen mode. The right area splits into two independent activity panels, each pinned to a different session with its own scroll position and filter.
|
|
163
|
+
|
|
164
|
+
When split mode is active:
|
|
165
|
+
|
|
166
|
+
- **Pin sessions** — navigate to a session in the sidebar and press `1` to pin it to the left panel, or `2` for the right panel
|
|
167
|
+
- **Navigate panels** — use `Tab` or left/right arrows to move focus between sidebar, left panel, and right panel
|
|
168
|
+
- **Independent filtering** — press `/` to filter whichever panel is focused
|
|
169
|
+
- **Swap panels** — press `S` to swap the left and right panels (sessions, scroll, filters)
|
|
170
|
+
- **Close a panel** — press `X` to clear the focused panel's session. If both panels are empty, split mode exits automatically
|
|
171
|
+
- **Detail view** — press `Enter` on a split panel to toggle the detail view for that panel
|
|
172
|
+
|
|
173
|
+
Press `x` again to exit split mode and return to the single-panel layout.
|
|
174
|
+
|
|
175
|
+
## Themes
|
|
176
|
+
|
|
177
|
+
agenttop ships with 15 built-in colour themes and a full theme management system. Open Settings (`s`) and select **Manage themes...** to access the theme menu.
|
|
178
|
+
|
|
179
|
+
Built-in themes: One Dark (default), Dracula, Monokai Pro, Solarized Dark, Solarized Light, Nord, Gruvbox Dark, Tokyo Night, Catppuccin Mocha, Catppuccin Latte, Rose Pine, Rose Pine Moon, Pastel Dark, Kanagawa, and Everforest.
|
|
180
|
+
|
|
181
|
+
In the theme menu:
|
|
182
|
+
|
|
183
|
+
- **Browse and preview** — navigate with up/down arrows. Each theme previews live as you select it
|
|
184
|
+
- **Apply** — press `Enter` to set the selected theme as active
|
|
185
|
+
- **Copy** — press `c` to create a custom theme based on the selected one
|
|
186
|
+
- **Edit** — press `e` to edit a custom theme's colours (individual hex values)
|
|
187
|
+
- **Rename** — press `r` to rename a custom theme
|
|
188
|
+
- **Delete** — press `d` to delete a custom theme
|
|
189
|
+
- **Restore default** — press `Backspace` to reset to One Dark
|
|
190
|
+
|
|
191
|
+
Custom themes are saved to `~/.config/agenttop/config.json` and persist across sessions.
|
|
192
|
+
|
|
134
193
|
## Streaming modes
|
|
135
194
|
|
|
136
195
|
For piping into other tools or integrating with external systems, agenttop supports two non-TUI streaming modes:
|
|
@@ -283,9 +342,22 @@ Config file at `~/.config/agenttop/config.json` (respects `XDG_CONFIG_HOME`):
|
|
|
283
342
|
"nickname": "n",
|
|
284
343
|
"clearNickname": "N",
|
|
285
344
|
"detail": "enter",
|
|
286
|
-
"update": "u"
|
|
345
|
+
"update": "u",
|
|
346
|
+
"settings": "s",
|
|
347
|
+
"archive": "a",
|
|
348
|
+
"delete": "d",
|
|
349
|
+
"viewArchive": "A",
|
|
350
|
+
"split": "x",
|
|
351
|
+
"pinLeft": "1",
|
|
352
|
+
"pinRight": "2",
|
|
353
|
+
"swapPanels": "S",
|
|
354
|
+
"closePanel": "X"
|
|
287
355
|
},
|
|
356
|
+
"theme": "one-dark",
|
|
357
|
+
"customThemes": {},
|
|
288
358
|
"nicknames": {},
|
|
359
|
+
"archived": {},
|
|
360
|
+
"archiveExpiryDays": 0,
|
|
289
361
|
"security": {
|
|
290
362
|
"enabled": true,
|
|
291
363
|
"rules": {
|
|
@@ -303,7 +375,9 @@ CLI flags override config values. Config is created with defaults on first run.
|
|
|
303
375
|
|
|
304
376
|
## Keybindings
|
|
305
377
|
|
|
306
|
-
All keybindings are configurable via `
|
|
378
|
+
All keybindings are configurable via Settings (`s`). The footer bar updates to reflect your custom bindings.
|
|
379
|
+
|
|
380
|
+
When rebinding a key, duplicate assignments are rejected with a toast notification — change the conflicting binding first. Press `Backspace` during rebind to reset a single key to its default, or use **Reset all keybindings** at the bottom of the keybindings section.
|
|
307
381
|
|
|
308
382
|
Default keybindings:
|
|
309
383
|
|
|
@@ -313,11 +387,19 @@ Default keybindings:
|
|
|
313
387
|
| `Tab` / left / right | Switch panel focus |
|
|
314
388
|
| `Enter` | Open session detail view |
|
|
315
389
|
| `Esc` | Close detail view / clear filter |
|
|
316
|
-
| `/` | Filter sessions |
|
|
390
|
+
| `/` | Filter active panel (sessions or activity) |
|
|
317
391
|
| `n` | Set nickname for selected session |
|
|
318
392
|
| `N` | Clear nickname |
|
|
393
|
+
| `a` | Archive session (restore in archive view) |
|
|
394
|
+
| `d` | Delete session (with confirmation) |
|
|
395
|
+
| `A` | Toggle archive view |
|
|
396
|
+
| `s` | Open settings |
|
|
319
397
|
| `u` | Install available update |
|
|
320
398
|
| `g` / `G` | Scroll to top / bottom of activity |
|
|
399
|
+
| `x` | Toggle split view |
|
|
400
|
+
| `1` / `2` | Pin session to left / right panel (split mode) |
|
|
401
|
+
| `S` | Swap left and right panels (split mode) |
|
|
402
|
+
| `X` | Close focused panel (split mode) |
|
|
321
403
|
| `q` | Quit |
|
|
322
404
|
|
|
323
405
|
## How it works
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/config/store.ts
|
|
4
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync, renameSync } from "fs";
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync, renameSync, unlinkSync } from "fs";
|
|
5
5
|
import { join } from "path";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
var getConfigDir = () => {
|
|
@@ -41,7 +41,15 @@ var defaultConfig = () => ({
|
|
|
41
41
|
clearNickname: "N",
|
|
42
42
|
detail: "enter",
|
|
43
43
|
update: "u",
|
|
44
|
-
settings: "s"
|
|
44
|
+
settings: "s",
|
|
45
|
+
archive: "a",
|
|
46
|
+
delete: "d",
|
|
47
|
+
viewArchive: "A",
|
|
48
|
+
split: "x",
|
|
49
|
+
pinLeft: "1",
|
|
50
|
+
pinRight: "2",
|
|
51
|
+
swapPanels: "S",
|
|
52
|
+
closePanel: "X"
|
|
45
53
|
},
|
|
46
54
|
security: {
|
|
47
55
|
enabled: true,
|
|
@@ -55,8 +63,14 @@ var defaultConfig = () => ({
|
|
|
55
63
|
},
|
|
56
64
|
prompts: {
|
|
57
65
|
hook: "pending",
|
|
58
|
-
mcp: "pending"
|
|
59
|
-
|
|
66
|
+
mcp: "pending",
|
|
67
|
+
theme: "pending",
|
|
68
|
+
tour: "pending"
|
|
69
|
+
},
|
|
70
|
+
archived: {},
|
|
71
|
+
archiveExpiryDays: 0,
|
|
72
|
+
theme: "one-dark",
|
|
73
|
+
customThemes: {}
|
|
60
74
|
});
|
|
61
75
|
var deepMerge = (target, source) => {
|
|
62
76
|
const result = { ...target };
|
|
@@ -133,6 +147,40 @@ var rotateLogFile = (filePath, maxBytes = 10 * 1024 * 1024) => {
|
|
|
133
147
|
} catch {
|
|
134
148
|
}
|
|
135
149
|
};
|
|
150
|
+
var archiveSession = (sessionId) => {
|
|
151
|
+
const config = loadConfig();
|
|
152
|
+
config.archived[sessionId] = Date.now();
|
|
153
|
+
saveConfig(config);
|
|
154
|
+
};
|
|
155
|
+
var unarchiveSession = (sessionId) => {
|
|
156
|
+
const config = loadConfig();
|
|
157
|
+
delete config.archived[sessionId];
|
|
158
|
+
saveConfig(config);
|
|
159
|
+
};
|
|
160
|
+
var getArchived = () => {
|
|
161
|
+
return loadConfig().archived;
|
|
162
|
+
};
|
|
163
|
+
var purgeExpiredArchives = () => {
|
|
164
|
+
const config = loadConfig();
|
|
165
|
+
if (config.archiveExpiryDays <= 0) return;
|
|
166
|
+
const cutoff = Date.now() - config.archiveExpiryDays * 864e5;
|
|
167
|
+
let changed = false;
|
|
168
|
+
for (const [id, ts] of Object.entries(config.archived)) {
|
|
169
|
+
if (ts < cutoff) {
|
|
170
|
+
delete config.archived[id];
|
|
171
|
+
changed = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (changed) saveConfig(config);
|
|
175
|
+
};
|
|
176
|
+
var deleteSessionFiles = (outputFiles) => {
|
|
177
|
+
for (const file of outputFiles) {
|
|
178
|
+
try {
|
|
179
|
+
unlinkSync(file);
|
|
180
|
+
} catch {
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
136
184
|
|
|
137
185
|
// src/mcp/server.ts
|
|
138
186
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -1012,9 +1060,14 @@ export {
|
|
|
1012
1060
|
getNicknames,
|
|
1013
1061
|
resolveAlertLogPath,
|
|
1014
1062
|
rotateLogFile,
|
|
1063
|
+
archiveSession,
|
|
1064
|
+
unarchiveSession,
|
|
1065
|
+
getArchived,
|
|
1066
|
+
purgeExpiredArchives,
|
|
1067
|
+
deleteSessionFiles,
|
|
1015
1068
|
discoverSessions,
|
|
1016
1069
|
Watcher,
|
|
1017
1070
|
SecurityEngine,
|
|
1018
1071
|
startMcpServer
|
|
1019
1072
|
};
|
|
1020
|
-
//# sourceMappingURL=chunk-
|
|
1073
|
+
//# sourceMappingURL=chunk-FCCWHOJO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/store.ts","../src/mcp/server.ts","../src/discovery/sessions.ts","../src/config.ts","../src/discovery/types.ts","../src/analysis/rules/network.ts","../src/analysis/rules/exfiltration.ts","../src/analysis/rules/sensitive-files.ts","../src/analysis/rules/shell-escape.ts","../src/analysis/rules/injection.ts","../src/analysis/security.ts","../src/ingestion/watcher.ts","../src/ingestion/tail.ts","../src/ingestion/parser.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync, renameSync, unlinkSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nimport type { ThemeColors, ToolColors } from './themes.js';\n\nexport interface SecurityRulesConfig {\n network: boolean;\n exfiltration: boolean;\n sensitiveFiles: boolean;\n shellEscape: boolean;\n injection: boolean;\n}\n\nexport interface NotificationsConfig {\n bell: boolean;\n desktop: boolean;\n minSeverity: 'info' | 'warn' | 'high' | 'critical';\n}\n\nexport interface AlertsConfig {\n logFile: string;\n enabled: boolean;\n}\n\nexport interface UpdatesConfig {\n checkOnLaunch: boolean;\n checkInterval: number;\n}\n\nexport interface PromptsConfig {\n hook: 'pending' | 'installed' | 'dismissed';\n mcp: 'pending' | 'installed' | 'dismissed';\n theme: 'pending' | 'done' | 'dismissed';\n tour: 'pending' | 'done' | 'dismissed';\n}\n\nexport interface KeybindingsConfig {\n quit: string;\n navUp: string;\n navDown: string;\n panelNext: string;\n panelPrev: string;\n scrollTop: string;\n scrollBottom: string;\n filter: string;\n nickname: string;\n clearNickname: string;\n detail: string;\n update: string;\n settings: string;\n archive: string;\n delete: string;\n viewArchive: string;\n split: string;\n pinLeft: string;\n pinRight: string;\n swapPanels: string;\n closePanel: string;\n}\n\nexport interface Config {\n pollInterval: number;\n maxEvents: number;\n maxAlerts: number;\n alertLevel: 'info' | 'warn' | 'high' | 'critical';\n notifications: NotificationsConfig;\n alerts: AlertsConfig;\n updates: UpdatesConfig;\n nicknames: Record<string, string>;\n keybindings: KeybindingsConfig;\n security: {\n enabled: boolean;\n rules: SecurityRulesConfig;\n };\n prompts: PromptsConfig;\n archived: Record<string, number>;\n archiveExpiryDays: number;\n theme: string;\n customThemes: Record<string, { name: string; colors: ThemeColors; toolColors: ToolColors }>;\n}\n\nexport const getConfigDir = (): string => {\n const xdg = process.env.XDG_CONFIG_HOME;\n return xdg ? join(xdg, 'agenttop') : join(homedir(), '.config', 'agenttop');\n};\n\nexport const getConfigPath = (): string => join(getConfigDir(), 'config.json');\n\nconst defaultConfig = (): Config => ({\n pollInterval: 10000,\n maxEvents: 200,\n maxAlerts: 100,\n alertLevel: 'warn',\n notifications: {\n bell: true,\n desktop: false,\n minSeverity: 'high',\n },\n alerts: {\n logFile: join(getConfigDir(), 'alerts.jsonl'),\n enabled: true,\n },\n updates: {\n checkOnLaunch: true,\n checkInterval: 21600000,\n },\n nicknames: {},\n keybindings: {\n quit: 'q',\n navUp: 'k',\n navDown: 'j',\n panelNext: 'tab',\n panelPrev: 'shift+tab',\n scrollTop: 'g',\n scrollBottom: 'G',\n filter: '/',\n nickname: 'n',\n clearNickname: 'N',\n detail: 'enter',\n update: 'u',\n settings: 's',\n archive: 'a',\n delete: 'd',\n viewArchive: 'A',\n split: 'x',\n pinLeft: '1',\n pinRight: '2',\n swapPanels: 'S',\n closePanel: 'X',\n },\n security: {\n enabled: true,\n rules: {\n network: true,\n exfiltration: true,\n sensitiveFiles: true,\n shellEscape: true,\n injection: true,\n },\n },\n prompts: {\n hook: 'pending',\n mcp: 'pending',\n theme: 'pending',\n tour: 'pending',\n },\n archived: {},\n archiveExpiryDays: 0,\n theme: 'one-dark',\n customThemes: {},\n});\n\nconst deepMerge = (target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> => {\n const result = { ...target };\n for (const key of Object.keys(target)) {\n if (key in source) {\n const tVal = target[key];\n const sVal = source[key];\n if (tVal && sVal && typeof tVal === 'object' && typeof sVal === 'object' && !Array.isArray(tVal)) {\n result[key] = deepMerge(tVal as Record<string, unknown>, sVal as Record<string, unknown>);\n } else {\n result[key] = sVal;\n }\n }\n }\n for (const key of Object.keys(source)) {\n if (!(key in target)) {\n result[key] = source[key];\n }\n }\n return result;\n};\n\nexport const loadConfig = (): Config => {\n const configPath = getConfigPath();\n const defaults = defaultConfig();\n\n if (!existsSync(configPath)) {\n return defaults;\n }\n\n try {\n const raw = JSON.parse(readFileSync(configPath, 'utf-8'));\n return deepMerge(defaults as unknown as Record<string, unknown>, raw) as unknown as Config;\n } catch {\n return defaults;\n }\n};\n\nexport const saveConfig = (config: Config): void => {\n const configDir = getConfigDir();\n mkdirSync(configDir, { recursive: true });\n writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + '\\n');\n};\n\nexport const isFirstRun = (): boolean => !existsSync(getConfigPath());\n\nexport const setNickname = (sessionId: string, nickname: string): void => {\n const config = loadConfig();\n config.nicknames[sessionId] = nickname;\n saveConfig(config);\n};\n\nexport const clearNickname = (sessionId: string): void => {\n const config = loadConfig();\n delete config.nicknames[sessionId];\n saveConfig(config);\n};\n\nexport const getNicknames = (): Record<string, string> => {\n return loadConfig().nicknames;\n};\n\nexport const resolveAlertLogPath = (config: Config): string => {\n const logFile = config.alerts.logFile;\n if (logFile.startsWith('~')) {\n return join(homedir(), logFile.slice(1));\n }\n return logFile;\n};\n\nexport const rotateLogFile = (filePath: string, maxBytes = 10 * 1024 * 1024): void => {\n try {\n const stat = statSync(filePath);\n if (stat.size > maxBytes) {\n const rotated = filePath + '.1';\n if (existsSync(rotated)) {\n try {\n renameSync(rotated, filePath + '.2');\n } catch {\n /* ignore */\n }\n }\n renameSync(filePath, rotated);\n }\n } catch {\n // file doesn't exist yet\n }\n};\n\nexport const archiveSession = (sessionId: string): void => {\n const config = loadConfig();\n config.archived[sessionId] = Date.now();\n saveConfig(config);\n};\n\nexport const unarchiveSession = (sessionId: string): void => {\n const config = loadConfig();\n delete config.archived[sessionId];\n saveConfig(config);\n};\n\nexport const getArchived = (): Record<string, number> => {\n return loadConfig().archived;\n};\n\nexport const purgeExpiredArchives = (): void => {\n const config = loadConfig();\n if (config.archiveExpiryDays <= 0) return;\n const cutoff = Date.now() - config.archiveExpiryDays * 86_400_000;\n let changed = false;\n for (const [id, ts] of Object.entries(config.archived)) {\n if (ts < cutoff) {\n delete config.archived[id];\n changed = true;\n }\n }\n if (changed) saveConfig(config);\n};\n\nexport const deleteSessionFiles = (outputFiles: string[]): void => {\n for (const file of outputFiles) {\n try {\n unlinkSync(file);\n } catch {\n /* file may already be gone */\n }\n }\n};\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';\n\nimport { discoverSessions } from '../discovery/sessions.js';\nimport type { Alert, AlertSeverity, SecurityEvent, ToolCall } from '../discovery/types.js';\nimport { SecurityEngine } from '../analysis/security.js';\nimport { Watcher } from '../ingestion/watcher.js';\n\nconst MAX_ALERTS = 100;\nconst MAX_ACTIVITY = 200;\n\nexport const startMcpServer = async (allUsers: boolean, noSecurity: boolean): Promise<void> => {\n const alerts: Alert[] = [];\n const activity = new Map<string, ToolCall[]>();\n const engine = noSecurity ? null : new SecurityEngine('info');\n\n const toolHandler = (calls: ToolCall[]) => {\n for (const call of calls) {\n const existing = activity.get(call.sessionId) ?? [];\n existing.push(call);\n if (existing.length > MAX_ACTIVITY) existing.splice(0, existing.length - MAX_ACTIVITY);\n activity.set(call.sessionId, existing);\n }\n };\n\n const securityHandler = engine\n ? (events: SecurityEvent[]) => {\n for (const event of events) {\n const newAlerts = engine.analyzeEvent(event);\n alerts.push(...newAlerts);\n if (alerts.length > MAX_ALERTS) alerts.splice(0, alerts.length - MAX_ALERTS);\n }\n }\n : undefined;\n\n const watcher = new Watcher(toolHandler, allUsers, securityHandler);\n watcher.start();\n\n const server = new Server(\n { name: 'agenttop', version: '0.3.0' },\n {\n capabilities: { tools: {} },\n },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: 'agenttop_sessions',\n description: 'List active Claude Code sessions with model, CPU, MEM, tokens, nickname',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'agenttop_alerts',\n description: 'Get recent security alerts, optionally filtered by severity',\n inputSchema: {\n type: 'object' as const,\n properties: {\n severity: {\n type: 'string',\n enum: ['info', 'warn', 'high', 'critical'],\n description: 'Minimum severity filter',\n },\n limit: { type: 'number', description: 'Max alerts to return (default 20)' },\n },\n },\n },\n {\n name: 'agenttop_usage',\n description: 'Get token usage for a session or all sessions',\n inputSchema: {\n type: 'object' as const,\n properties: {\n sessionId: { type: 'string', description: 'Session ID (omit for all)' },\n },\n },\n },\n {\n name: 'agenttop_activity',\n description: 'Get recent tool calls for a session',\n inputSchema: {\n type: 'object' as const,\n properties: {\n sessionId: { type: 'string', description: 'Session ID' },\n limit: { type: 'number', description: 'Max events to return (default 20)' },\n },\n required: ['sessionId'],\n },\n },\n ],\n }));\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n switch (name) {\n case 'agenttop_sessions': {\n const sessions = discoverSessions(allUsers);\n const data = sessions.map((s) => ({\n sessionId: s.sessionId,\n slug: s.slug,\n model: s.model,\n cwd: s.cwd,\n cpu: s.cpu,\n memMB: s.memMB,\n agents: s.agentCount,\n tokens: { input: s.usage.inputTokens, output: s.usage.outputTokens, cacheRead: s.usage.cacheReadTokens },\n }));\n return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };\n }\n\n case 'agenttop_alerts': {\n const severity = (args?.severity as AlertSeverity) ?? 'info';\n const limit = (args?.limit as number) ?? 20;\n const order: Record<string, number> = { info: 0, warn: 1, high: 2, critical: 3 };\n const minOrder = order[severity] ?? 0;\n const filtered = alerts\n .filter((a) => (order[a.severity] ?? 0) >= minOrder)\n .slice(-limit)\n .map((a) => ({\n severity: a.severity,\n rule: a.rule,\n message: a.message,\n sessionSlug: a.sessionSlug,\n timestamp: new Date(a.timestamp).toISOString(),\n }));\n return { content: [{ type: 'text', text: JSON.stringify(filtered, null, 2) }] };\n }\n\n case 'agenttop_usage': {\n const sessions = discoverSessions(allUsers);\n const sessionId = args?.sessionId as string | undefined;\n const targets = sessionId ? sessions.filter((s) => s.sessionId === sessionId) : sessions;\n const data = targets.map((s) => ({\n sessionId: s.sessionId,\n slug: s.slug,\n usage: s.usage,\n }));\n return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };\n }\n\n case 'agenttop_activity': {\n const sid = args?.sessionId as string;\n const limit = (args?.limit as number) ?? 20;\n const events = (activity.get(sid) ?? []).slice(-limit).map((e) => ({\n timestamp: new Date(e.timestamp).toISOString(),\n tool: e.toolName,\n input: e.toolInput,\n }));\n return { content: [{ type: 'text', text: JSON.stringify(events, null, 2) }] };\n }\n\n default:\n return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };\n }\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n process.on('SIGINT', () => {\n watcher.stop();\n process.exit(0);\n });\n process.on('SIGTERM', () => {\n watcher.stop();\n process.exit(0);\n });\n};\n","import { readdirSync, statSync, readlinkSync, openSync, readSync, closeSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nimport { getTaskDirs } from '../config.js';\nimport type { Session, ProcessInfo, TokenUsage } from './types.js';\n\nexport const getClaudeProcesses = (): ProcessInfo[] => {\n try {\n const output = execSync('ps aux', { encoding: 'utf-8', timeout: 5000 });\n const procs = output\n .split('\\n')\n .filter((line) => line.includes('/claude') && !line.includes('grep') && !line.includes('agenttop'))\n .map((line) => {\n const parts = line.trim().split(/\\s+/);\n const pid = parseInt(parts[1], 10);\n let cwd = '';\n try {\n cwd = readlinkSync(`/proc/${pid}/cwd`);\n } catch {\n // no access or process gone\n }\n return {\n pid,\n cpu: parseFloat(parts[2]) || 0,\n mem: parseFloat(parts[3]) || 0,\n memKB: parseInt(parts[5], 10) || 0,\n startTime: parts[8] || '',\n command: parts.slice(10).join(' '),\n cwd,\n };\n })\n .filter((p) => !isNaN(p.pid))\n .filter((p) => !p.command.startsWith('sudo'));\n return procs;\n } catch {\n return [];\n }\n};\n\nconst readFirstEvent = (filePath: string): Record<string, unknown> | null => {\n try {\n const fd = openSync(filePath, 'r');\n const buf = Buffer.alloc(16384);\n const bytesRead = readSync(fd, buf, 0, 16384, 0);\n closeSync(fd);\n const line = buf.subarray(0, bytesRead).toString('utf-8').split('\\n')[0];\n if (!line) return null;\n return JSON.parse(line) as Record<string, unknown>;\n } catch {\n return null;\n }\n};\n\nconst findModelAndUsage = (filePath: string): { model: string; usage: TokenUsage } => {\n const usage: TokenUsage = { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };\n let model = '';\n\n try {\n const fd = openSync(filePath, 'r');\n const buf = Buffer.alloc(65536);\n const bytesRead = readSync(fd, buf, 0, 65536, 0);\n closeSync(fd);\n const text = buf.subarray(0, bytesRead).toString('utf-8');\n const lines = text.split('\\n');\n for (const line of lines) {\n if (!line) continue;\n try {\n const evt = JSON.parse(line);\n if (evt.type === 'assistant') {\n if (!model && evt.message?.model) {\n model = String(evt.message.model);\n }\n const u = evt.message?.usage;\n if (u) {\n usage.inputTokens += u.input_tokens ?? 0;\n usage.cacheCreationTokens += u.cache_creation_input_tokens ?? 0;\n usage.cacheReadTokens += u.cache_read_input_tokens ?? 0;\n usage.outputTokens += u.output_tokens ?? 0;\n }\n }\n } catch {\n continue;\n }\n }\n } catch {\n // ignore\n }\n\n return { model, usage };\n};\n\nconst normalisePath = (p: string): string => {\n return p.replace(/\\/+$/, '');\n};\n\nexport const discoverSessions = (allUsers: boolean): Session[] => {\n const taskDirs = getTaskDirs(allUsers);\n const processes = getClaudeProcesses();\n const sessionMap = new Map<string, Session>();\n\n for (const taskDir of taskDirs) {\n let projectDirs: string[];\n try {\n projectDirs = readdirSync(taskDir);\n } catch {\n continue;\n }\n\n for (const projectName of projectDirs) {\n const projectPath = join(taskDir, projectName);\n let stat;\n try {\n stat = statSync(projectPath);\n } catch {\n continue;\n }\n if (!stat.isDirectory()) continue;\n\n const tasksDir = join(projectPath, 'tasks');\n let outputFiles: string[];\n try {\n outputFiles = readdirSync(tasksDir)\n .filter((f) => f.endsWith('.output'))\n .map((f) => join(tasksDir, f));\n } catch {\n continue;\n }\n\n if (outputFiles.length === 0) continue;\n\n const agentIds: string[] = [];\n let sessionId = '';\n let slug = '';\n let cwd = '';\n let model = '';\n let version = '';\n let gitBranch = '';\n let startTime = Infinity;\n let lastActivity = 0;\n const totalUsage: TokenUsage = { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };\n\n for (const outputFile of outputFiles) {\n const agentId = basename(outputFile, '.output');\n agentIds.push(agentId);\n\n const firstEvent = readFirstEvent(outputFile);\n if (firstEvent) {\n if (!sessionId) sessionId = String(firstEvent.sessionId || '');\n if (!slug) slug = String(firstEvent.slug || '');\n if (!cwd) cwd = String(firstEvent.cwd || '');\n if (!version) version = String(firstEvent.version || '');\n if (!gitBranch) gitBranch = String(firstEvent.gitBranch || '');\n }\n\n try {\n const fstat = statSync(outputFile);\n const created = fstat.birthtimeMs || fstat.ctimeMs;\n if (created < startTime) startTime = created;\n if (fstat.mtimeMs > lastActivity) lastActivity = fstat.mtimeMs;\n } catch {\n // ignore\n }\n\n const result = findModelAndUsage(outputFile);\n if (!model && result.model) {\n model = result.model;\n }\n totalUsage.inputTokens += result.usage.inputTokens;\n totalUsage.cacheCreationTokens += result.usage.cacheCreationTokens;\n totalUsage.cacheReadTokens += result.usage.cacheReadTokens;\n totalUsage.outputTokens += result.usage.outputTokens;\n }\n\n if (!model) {\n model = 'unknown';\n }\n\n const normCwd = normalisePath(cwd);\n const matchingProcess = processes.find((p) => {\n if (!p.cwd) return false;\n return normalisePath(p.cwd) === normCwd;\n });\n\n const session: Session = {\n sessionId,\n slug: slug || sessionId.slice(0, 12),\n project: projectName.replace(/-/g, '/'),\n cwd,\n model,\n version,\n gitBranch,\n pid: matchingProcess?.pid ?? null,\n cpu: matchingProcess?.cpu ?? 0,\n mem: matchingProcess?.mem ?? 0,\n memMB: matchingProcess ? Math.round(matchingProcess.memKB / 1024) : 0,\n agentCount: agentIds.length,\n agentIds,\n outputFiles,\n startTime: startTime === Infinity ? Date.now() : startTime,\n lastActivity,\n usage: totalUsage,\n };\n\n sessionMap.set(sessionId || projectName, session);\n }\n }\n\n return Array.from(sessionMap.values()).sort((a, b) => b.lastActivity - a.lastActivity);\n};\n","import { realpathSync, readdirSync } from 'node:fs';\nimport { homedir, platform } from 'node:os';\nimport { join } from 'node:path';\n\nconst resolvePath = (p: string): string => {\n try {\n return realpathSync(p);\n } catch {\n return p;\n }\n};\n\nexport const getUid = (): number => process.getuid?.() ?? 0;\n\nexport const isRoot = (): boolean => getUid() === 0;\n\nexport const getTmpDir = (): string => resolvePath(platform() === 'darwin' ? '/private/tmp' : '/tmp');\n\nexport const getClaudeHome = (): string => join(homedir(), '.claude');\n\nexport const getHistoryPath = (): string => join(getClaudeHome(), 'history.jsonl');\n\nexport const getTaskDirs = (allUsers: boolean): string[] => {\n const tmp = getTmpDir();\n const uid = getUid();\n\n if (allUsers && isRoot()) {\n try {\n return readdirSync(tmp)\n .filter((d: string) => d.startsWith('claude-'))\n .filter((d: string) => !d.endsWith('-cwd'))\n .map((d: string) => join(tmp, d));\n } catch {\n return [join(tmp, `claude-${uid}`)];\n }\n }\n\n return [join(tmp, `claude-${uid}`)];\n};\n\nexport const getPlatform = (): 'linux' | 'darwin' | 'win32' => {\n const p = platform();\n if (p === 'darwin') return 'darwin';\n if (p === 'win32') return 'win32';\n return 'linux';\n};\n\nexport { getConfigDir } from './config/store.js';\n","export interface TokenUsage {\n inputTokens: number;\n cacheCreationTokens: number;\n cacheReadTokens: number;\n outputTokens: number;\n}\n\nexport interface Session {\n sessionId: string;\n slug: string;\n project: string;\n cwd: string;\n model: string;\n version: string;\n gitBranch: string;\n pid: number | null;\n cpu: number;\n mem: number;\n memMB: number;\n agentCount: number;\n agentIds: string[];\n outputFiles: string[];\n startTime: number;\n lastActivity: number;\n usage: TokenUsage;\n nickname?: string;\n}\n\nexport interface ToolCall {\n sessionId: string;\n agentId: string;\n slug: string;\n timestamp: number;\n toolName: string;\n toolInput: Record<string, unknown>;\n cwd: string;\n}\n\nexport interface ToolResult {\n sessionId: string;\n agentId: string;\n slug: string;\n timestamp: number;\n toolUseId: string;\n content: string;\n isError: boolean;\n cwd: string;\n}\n\nexport type SecurityEvent = ToolCall | ToolResult;\n\nexport const isToolResult = (event: SecurityEvent): event is ToolResult => 'toolUseId' in event;\n\nexport const isToolCall = (event: SecurityEvent): event is ToolCall => 'toolName' in event;\n\nexport interface RawEvent {\n type: 'user' | 'assistant' | 'tool_result';\n sessionId: string;\n agentId: string;\n slug: string;\n timestamp?: string;\n cwd: string;\n version: string;\n gitBranch: string;\n message: {\n role: string;\n content: unknown;\n model?: string;\n usage?: {\n input_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n output_tokens?: number;\n };\n };\n}\n\nexport type AlertSeverity = 'info' | 'warn' | 'high' | 'critical';\n\nexport interface Alert {\n id: string;\n severity: AlertSeverity;\n rule: string;\n message: string;\n sessionSlug: string;\n sessionId: string;\n event: SecurityEvent;\n timestamp: number;\n}\n\nexport interface ProcessInfo {\n pid: number;\n cpu: number;\n mem: number;\n memKB: number;\n command: string;\n startTime: string;\n cwd: string;\n}\n\nexport interface CLIOptions {\n allUsers: boolean;\n noSecurity: boolean;\n json: boolean;\n plain: boolean;\n alertLevel: AlertSeverity;\n installHooks: boolean;\n uninstallHooks: boolean;\n help: boolean;\n version: boolean;\n noNotify: boolean;\n noAlertLog: boolean;\n noUpdates: boolean;\n pollInterval: number;\n mcp: boolean;\n installMcp: boolean;\n}\n","import type { ToolCall, Alert } from '../../discovery/types.js';\n\nconst NETWORK_PATTERNS = [\n /\\bcurl\\b/,\n /\\bwget\\b/,\n /\\bfetch\\s*\\(/,\n /\\bnc\\b/,\n /\\bnetcat\\b/,\n /\\bpython3?\\s+-m\\s+http\\.server\\b/,\n /\\bncat\\b/,\n /\\bsocat\\b/,\n /\\btelnet\\b/,\n];\n\nconst LOCALHOST = /\\b(localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|\\[::1\\])\\b/;\n\nexport const checkNetwork = (call: ToolCall): Alert | null => {\n if (call.toolName !== 'Bash') return null;\n\n const command = String(call.toolInput.command || '');\n const matched = NETWORK_PATTERNS.some((p) => p.test(command));\n if (!matched) return null;\n\n const isLocal = LOCALHOST.test(command);\n const severity = isLocal ? 'info' : 'warn';\n\n return {\n id: `net-${call.timestamp}-${call.agentId}`,\n severity,\n rule: 'network',\n message: isLocal\n ? `Network command to localhost: ${command.slice(0, 80)}`\n : `Network command to external target: ${command.slice(0, 80)}`,\n sessionSlug: call.slug,\n sessionId: call.sessionId,\n event: call,\n timestamp: call.timestamp,\n };\n};\n","import type { ToolCall, Alert } from '../../discovery/types.js';\n\nconst EXFIL_PATTERNS = [\n /base64.*\\|\\s*(curl|wget|nc)/,\n /cat\\s+.*\\|\\s*(curl|wget|nc)/,\n /(tar|zip|gzip).*\\|\\s*(curl|wget|nc)/,\n /\\bcurl\\b.*-d\\s*@/,\n /\\bcurl\\b.*--data-binary/,\n /\\bscp\\b/,\n /\\brsync\\b.*[^/]@/,\n />\\s*\\/dev\\/tcp\\//,\n];\n\nexport const checkExfiltration = (call: ToolCall): Alert | null => {\n if (call.toolName !== 'Bash') return null;\n\n const command = String(call.toolInput.command || '');\n const matched = EXFIL_PATTERNS.some((p) => p.test(command));\n if (!matched) return null;\n\n return {\n id: `exfil-${call.timestamp}-${call.agentId}`,\n severity: 'high',\n rule: 'exfiltration',\n message: `Potential data exfiltration: ${command.slice(0, 80)}`,\n sessionSlug: call.slug,\n sessionId: call.sessionId,\n event: call,\n timestamp: call.timestamp,\n };\n};\n","import type { ToolCall, Alert } from '../../discovery/types.js';\n\nconst SENSITIVE_PATTERNS = [\n /\\.env\\b/,\n /\\.env\\.\\w+/,\n /\\.ssh\\//,\n /id_rsa/,\n /id_ed25519/,\n /\\.pem$/,\n /\\.key$/,\n /credentials/i,\n /\\/etc\\/shadow/,\n /\\/etc\\/passwd/,\n /\\.aws\\/credentials/,\n /\\.kube\\/config/,\n /\\.docker\\/config\\.json/,\n /\\.npmrc/,\n /\\.pypirc/,\n /\\.netrc/,\n /secrets?\\.\\w+/i,\n /token\\.\\w+/i,\n];\n\nconst TOOLS_THAT_READ = ['Read', 'Bash', 'Grep', 'Glob'];\n\nexport const checkSensitiveFiles = (call: ToolCall): Alert | null => {\n if (!TOOLS_THAT_READ.includes(call.toolName)) return null;\n\n const inputs = JSON.stringify(call.toolInput);\n const matched = SENSITIVE_PATTERNS.some((p) => p.test(inputs));\n if (!matched) return null;\n\n const target = String(call.toolInput.file_path || call.toolInput.command || call.toolInput.pattern || '').slice(\n 0,\n 60,\n );\n\n return {\n id: `sens-${call.timestamp}-${call.agentId}`,\n severity: 'warn',\n rule: 'sensitive-files',\n message: `Accessing sensitive file: ${target}`,\n sessionSlug: call.slug,\n sessionId: call.sessionId,\n event: call,\n timestamp: call.timestamp,\n };\n};\n","import type { ToolCall, Alert } from '../../discovery/types.js';\n\nconst SHELL_PATTERNS: Array<{ pattern: RegExp; severity: 'high' | 'critical'; label: string }> = [\n { pattern: /\\beval\\s*[(\"']/, severity: 'high', label: 'eval execution' },\n { pattern: /\\bchmod\\s+777\\b/, severity: 'high', label: 'chmod 777' },\n { pattern: /\\bchmod\\s+\\+s\\b/, severity: 'critical', label: 'setuid chmod' },\n { pattern: /\\bsudo\\b/, severity: 'high', label: 'sudo usage' },\n { pattern: /\\bsu\\s+-?\\s*\\w/, severity: 'high', label: 'su usage' },\n { pattern: />\\s*\\/etc\\//, severity: 'critical', label: 'writing to /etc/' },\n { pattern: />\\s*\\/usr\\//, severity: 'critical', label: 'writing to /usr/' },\n { pattern: /--privileged/, severity: 'critical', label: 'privileged flag' },\n { pattern: /\\brm\\s+-rf\\s+\\/(?!\\w)/, severity: 'critical', label: 'rm -rf /' },\n { pattern: /\\bdd\\s+.*of=\\/dev\\//, severity: 'critical', label: 'dd to device' },\n { pattern: /\\bmkfs\\b/, severity: 'critical', label: 'filesystem format' },\n { pattern: /\\biptables\\b/, severity: 'high', label: 'firewall modification' },\n];\n\nexport const checkShellEscape = (call: ToolCall): Alert | null => {\n if (call.toolName !== 'Bash') return null;\n\n const command = String(call.toolInput.command || '');\n\n for (const rule of SHELL_PATTERNS) {\n if (rule.pattern.test(command)) {\n return {\n id: `shell-${call.timestamp}-${call.agentId}`,\n severity: rule.severity,\n rule: 'shell-escape',\n message: `${rule.label}: ${command.slice(0, 80)}`,\n sessionSlug: call.slug,\n sessionId: call.sessionId,\n event: call,\n timestamp: call.timestamp,\n };\n }\n }\n\n return null;\n};\n","import type { ToolCall, ToolResult, Alert, SecurityEvent } from '../../discovery/types.js';\nimport { isToolResult, isToolCall } from '../../discovery/types.js';\n\nconst INJECTION_PATTERNS = [\n /ignore\\s+(all\\s+)?previous\\s+instructions/i,\n /ignore\\s+(all\\s+)?prior\\s+instructions/i,\n /disregard\\s+(all\\s+)?previous/i,\n /you\\s+are\\s+now\\s+/i,\n /new\\s+instructions?\\s*:/i,\n /system\\s*:\\s*you/i,\n /\\bdo\\s+not\\s+follow\\s+(your|the)\\s+(original|previous)/i,\n /override\\s+(your\\s+)?(instructions|rules|guidelines)/i,\n /forget\\s+(your\\s+)?(instructions|rules|guidelines)/i,\n /act\\s+as\\s+(if\\s+)?(you\\s+are|a)\\s+/i,\n /pretend\\s+(you\\s+are|to\\s+be)\\s+/i,\n /\\bAI\\s+assistant\\b.*\\bmust\\b/i,\n /\\bhuman\\s*:\\s*/i,\n /\\bassistant\\s*:\\s*/i,\n /<\\s*system\\s*>/i,\n /\\[\\s*INST\\s*\\]/i,\n /BEGIN\\s+HIDDEN\\s+INSTRUCTIONS/i,\n];\n\nconst ENCODED_PATTERNS = [\n /aWdub3JlIHByZXZpb3Vz/, // base64 \"ignore previous\"\n /&#x[0-9a-f]+;/i, // html hex entities\n /&#\\d+;/, // html decimal entities\n /\\\\u[0-9a-f]{4}/i, // unicode escapes\n];\n\nexport const checkInjection = (event: SecurityEvent): Alert | null => {\n if (isToolCall(event)) {\n return checkToolCallInjection(event);\n }\n if (isToolResult(event)) {\n return checkToolResultInjection(event);\n }\n return null;\n};\n\nconst checkToolCallInjection = (call: ToolCall): Alert | null => {\n const inputs = JSON.stringify(call.toolInput);\n\n const matched = INJECTION_PATTERNS.some((p) => p.test(inputs));\n if (!matched) return null;\n\n return {\n id: `inject-call-${call.timestamp}-${call.agentId}`,\n severity: 'critical',\n rule: 'injection',\n message: `Prompt injection in ${call.toolName} input`,\n sessionSlug: call.slug,\n sessionId: call.sessionId,\n event: call,\n timestamp: call.timestamp,\n };\n};\n\nconst checkToolResultInjection = (result: ToolResult): Alert | null => {\n const content = result.content;\n if (!content || content.length < 10) return null;\n\n const textPatternMatch = INJECTION_PATTERNS.some((p) => p.test(content));\n const encodedMatch = ENCODED_PATTERNS.some((p) => p.test(content));\n\n if (!textPatternMatch && !encodedMatch) return null;\n\n const matchedPattern = INJECTION_PATTERNS.find((p) => p.test(content));\n const snippet = matchedPattern ? content.match(matchedPattern)?.[0]?.slice(0, 50) || '' : 'encoded pattern';\n\n return {\n id: `inject-result-${result.timestamp}-${result.agentId}`,\n severity: 'critical',\n rule: 'injection-in-result',\n message: `Prompt injection in tool result: \"${snippet}\"`,\n sessionSlug: result.slug,\n sessionId: result.sessionId,\n event: result,\n timestamp: result.timestamp,\n };\n};\n","import type { ToolCall, ToolResult, SecurityEvent, Alert, AlertSeverity } from '../discovery/types.js';\nimport { isToolCall } from '../discovery/types.js';\nimport type { SecurityRulesConfig } from '../config/store.js';\n\nimport { checkNetwork } from './rules/network.js';\nimport { checkExfiltration } from './rules/exfiltration.js';\nimport { checkSensitiveFiles } from './rules/sensitive-files.js';\nimport { checkShellEscape } from './rules/shell-escape.js';\nimport { checkInjection } from './rules/injection.js';\n\ntype ToolCallRule = (call: ToolCall) => Alert | null;\ntype SecurityEventRule = (event: SecurityEvent) => Alert | null;\n\ninterface NamedRule<T> {\n key: keyof SecurityRulesConfig;\n fn: T;\n}\n\nconst toolCallRules: NamedRule<ToolCallRule>[] = [\n { key: 'network', fn: checkNetwork },\n { key: 'exfiltration', fn: checkExfiltration },\n { key: 'sensitiveFiles', fn: checkSensitiveFiles },\n { key: 'shellEscape', fn: checkShellEscape },\n];\n\nconst allEventRules: NamedRule<SecurityEventRule>[] = [{ key: 'injection', fn: checkInjection }];\n\nconst SEVERITY_ORDER: Record<AlertSeverity, number> = {\n info: 0,\n warn: 1,\n high: 2,\n critical: 3,\n};\n\nconst DEDUP_WINDOW_MS = 30_000;\n\nexport class SecurityEngine {\n private recentAlerts = new Map<string, number>();\n private minLevel: AlertSeverity;\n private rulesConfig: SecurityRulesConfig;\n\n constructor(minLevel: AlertSeverity = 'warn', rulesConfig?: SecurityRulesConfig) {\n this.minLevel = minLevel;\n this.rulesConfig = rulesConfig ?? {\n network: true,\n exfiltration: true,\n sensitiveFiles: true,\n shellEscape: true,\n injection: true,\n };\n }\n\n analyze(call: ToolCall): Alert[] {\n return this.analyzeEvent(call);\n }\n\n analyzeResult(result: ToolResult): Alert[] {\n return this.analyzeEvent(result);\n }\n\n analyzeEvent(event: SecurityEvent): Alert[] {\n const alerts: Alert[] = [];\n\n if (isToolCall(event)) {\n for (const rule of toolCallRules) {\n if (!this.rulesConfig[rule.key]) continue;\n const alert = rule.fn(event);\n if (alert) alerts.push(alert);\n }\n }\n\n for (const rule of allEventRules) {\n if (!this.rulesConfig[rule.key]) continue;\n const alert = rule.fn(event);\n if (alert) alerts.push(alert);\n }\n\n return alerts.filter((alert) => {\n if (SEVERITY_ORDER[alert.severity] < SEVERITY_ORDER[this.minLevel]) return false;\n\n const dedupKey = `${alert.rule}-${alert.sessionId}-${alert.message.slice(0, 40)}`;\n const lastSeen = this.recentAlerts.get(dedupKey);\n if (lastSeen && alert.timestamp - lastSeen < DEDUP_WINDOW_MS) return false;\n\n this.recentAlerts.set(dedupKey, alert.timestamp);\n return true;\n });\n }\n\n pruneOldAlerts(): void {\n const cutoff = Date.now() - DEDUP_WINDOW_MS * 2;\n for (const [key, ts] of this.recentAlerts) {\n if (ts < cutoff) this.recentAlerts.delete(key);\n }\n }\n}\n","import { watch } from 'chokidar';\nimport type { FSWatcher } from 'chokidar';\n\nimport { getTaskDirs } from '../config.js';\nimport type { ToolCall, SecurityEvent, TokenUsage } from '../discovery/types.js';\nimport { FileTailer } from './tail.js';\nimport { parseLines, parseAllEvents, parseUsageFromLines } from './parser.js';\n\nexport type ToolCallHandler = (calls: ToolCall[]) => void;\nexport type SecurityEventHandler = (events: SecurityEvent[]) => void;\nexport type UsageHandler = (sessionId: string, usage: TokenUsage) => void;\n\nexport class Watcher {\n private watcher: FSWatcher | null = null;\n private tailer = new FileTailer();\n private handler: ToolCallHandler;\n private securityHandler: SecurityEventHandler | null;\n private usageHandler: UsageHandler | null;\n private allUsers: boolean;\n private knownFiles = new Set<string>();\n\n constructor(\n handler: ToolCallHandler,\n allUsers: boolean,\n securityHandler?: SecurityEventHandler,\n usageHandler?: UsageHandler,\n ) {\n this.handler = handler;\n this.allUsers = allUsers;\n this.securityHandler = securityHandler ?? null;\n this.usageHandler = usageHandler ?? null;\n }\n\n start(): void {\n const taskDirs = getTaskDirs(this.allUsers);\n const globs = taskDirs.map((d) => `${d}/**/tasks/*.output`);\n\n this.watcher = watch(globs, {\n persistent: true,\n ignoreInitial: false,\n awaitWriteFinish: false,\n usePolling: false,\n });\n\n this.watcher.on('add', (filePath: string) => {\n if (this.knownFiles.has(filePath)) return;\n this.knownFiles.add(filePath);\n this.tailer.seekToEnd(filePath);\n });\n\n this.watcher.on('change', (filePath: string) => {\n const lines = this.tailer.readNewLines(filePath);\n if (lines.length === 0) return;\n\n const calls = parseLines(lines);\n if (calls.length > 0) {\n this.handler(calls);\n }\n\n if (this.securityHandler) {\n const allEvents = parseAllEvents(lines);\n if (allEvents.length > 0) {\n this.securityHandler(allEvents);\n }\n }\n\n if (this.usageHandler) {\n const usage = parseUsageFromLines(lines);\n if (usage.inputTokens > 0 || usage.outputTokens > 0) {\n const firstCall = calls[0];\n if (firstCall) {\n this.usageHandler(firstCall.sessionId, usage);\n }\n }\n }\n });\n }\n\n stop(): void {\n this.watcher?.close();\n this.watcher = null;\n this.tailer.resetAll();\n this.knownFiles.clear();\n }\n\n readExisting(filePath: string): ToolCall[] {\n this.tailer.reset(filePath);\n const lines = this.tailer.readNewLines(filePath);\n return parseLines(lines);\n }\n}\n","import { openSync, readSync, closeSync, statSync } from 'node:fs';\n\nexport class FileTailer {\n private offsets = new Map<string, number>();\n\n readNewLines(filePath: string): string[] {\n let currentSize: number;\n try {\n currentSize = statSync(filePath).size;\n } catch {\n return [];\n }\n\n const lastOffset = this.offsets.get(filePath) ?? 0;\n if (currentSize <= lastOffset) return [];\n\n const bytesToRead = currentSize - lastOffset;\n const buf = Buffer.alloc(bytesToRead);\n\n let fd: number;\n try {\n fd = openSync(filePath, 'r');\n } catch {\n return [];\n }\n\n try {\n readSync(fd, buf, 0, bytesToRead, lastOffset);\n } finally {\n closeSync(fd);\n }\n\n this.offsets.set(filePath, currentSize);\n\n const text = buf.toString('utf-8');\n const lines = text.split('\\n').filter((l) => l.trim().length > 0);\n return lines;\n }\n\n seekToEnd(filePath: string): void {\n try {\n const size = statSync(filePath).size;\n this.offsets.set(filePath, size);\n } catch {\n // file doesn't exist yet\n }\n }\n\n reset(filePath: string): void {\n this.offsets.delete(filePath);\n }\n\n resetAll(): void {\n this.offsets.clear();\n }\n}\n","import type { RawEvent, ToolCall, ToolResult, SecurityEvent, TokenUsage } from '../discovery/types.js';\n\nconst parseEventTimestamp = (event: RawEvent): number => {\n if (event.timestamp) {\n const parsed = new Date(event.timestamp).getTime();\n if (!isNaN(parsed)) return parsed;\n }\n return Date.now();\n};\n\nexport const parseLine = (line: string): RawEvent | null => {\n try {\n return JSON.parse(line) as RawEvent;\n } catch {\n return null;\n }\n};\n\nexport const extractToolCalls = (event: RawEvent): ToolCall[] => {\n if (event.type !== 'assistant') return [];\n\n const content = event.message?.content;\n if (!Array.isArray(content)) return [];\n\n const ts = parseEventTimestamp(event);\n const calls: ToolCall[] = [];\n\n for (const block of content) {\n if (\n typeof block === 'object' &&\n block !== null &&\n 'type' in block &&\n (block as Record<string, unknown>).type === 'tool_use'\n ) {\n const toolBlock = block as Record<string, unknown>;\n calls.push({\n sessionId: event.sessionId,\n agentId: event.agentId,\n slug: ((event as Record<string, unknown>).slug as string) || '',\n timestamp: ts,\n toolName: (toolBlock.name as string) || 'unknown',\n toolInput: (toolBlock.input as Record<string, unknown>) || {},\n cwd: event.cwd,\n });\n }\n }\n\n return calls;\n};\n\nexport const extractToolResults = (event: RawEvent): ToolResult[] => {\n if (event.type !== 'user') return [];\n\n const content = event.message?.content;\n if (!Array.isArray(content)) return [];\n\n const ts = parseEventTimestamp(event);\n const results: ToolResult[] = [];\n\n for (const block of content) {\n if (\n typeof block === 'object' &&\n block !== null &&\n 'type' in block &&\n (block as Record<string, unknown>).type === 'tool_result'\n ) {\n const resultBlock = block as Record<string, unknown>;\n const resultContent = resultBlock.content;\n let text = '';\n if (typeof resultContent === 'string') {\n text = resultContent;\n } else if (Array.isArray(resultContent)) {\n text = resultContent\n .map((c) => (typeof c === 'object' && c !== null ? (c as Record<string, unknown>).text || '' : String(c)))\n .join('\\n');\n }\n\n results.push({\n sessionId: event.sessionId,\n agentId: event.agentId,\n slug: ((event as Record<string, unknown>).slug as string) || '',\n timestamp: ts,\n toolUseId: String(resultBlock.tool_use_id || ''),\n content: text,\n isError: Boolean(resultBlock.is_error),\n cwd: event.cwd,\n });\n }\n }\n\n return results;\n};\n\nexport const extractUsage = (event: RawEvent): TokenUsage | null => {\n if (event.type !== 'assistant') return null;\n\n const usage = event.message?.usage;\n if (!usage) return null;\n\n return {\n inputTokens: usage.input_tokens ?? 0,\n cacheCreationTokens: usage.cache_creation_input_tokens ?? 0,\n cacheReadTokens: usage.cache_read_input_tokens ?? 0,\n outputTokens: usage.output_tokens ?? 0,\n };\n};\n\nexport const parseLines = (lines: string[]): ToolCall[] => {\n const calls: ToolCall[] = [];\n for (const line of lines) {\n const event = parseLine(line);\n if (event) {\n calls.push(...extractToolCalls(event));\n }\n }\n return calls;\n};\n\nexport const parseAllEvents = (lines: string[]): SecurityEvent[] => {\n const events: SecurityEvent[] = [];\n for (const line of lines) {\n const event = parseLine(line);\n if (event) {\n events.push(...extractToolCalls(event));\n events.push(...extractToolResults(event));\n }\n }\n return events;\n};\n\nexport const parseUsageFromLines = (lines: string[]): TokenUsage => {\n const total: TokenUsage = { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0 };\n for (const line of lines) {\n const event = parseLine(line);\n if (event) {\n const usage = extractUsage(event);\n if (usage) {\n total.inputTokens += usage.inputTokens;\n total.cacheCreationTokens += usage.cacheCreationTokens;\n total.cacheReadTokens += usage.cacheReadTokens;\n total.outputTokens += usage.outputTokens;\n }\n }\n }\n return total;\n};\n"],"mappings":";;;AAAA,SAAS,YAAY,cAAc,eAAe,WAAW,UAAU,YAAY,kBAAkB;AACrG,SAAS,YAAY;AACrB,SAAS,eAAe;AAgFjB,IAAM,eAAe,MAAc;AACxC,QAAM,MAAM,QAAQ,IAAI;AACxB,SAAO,MAAM,KAAK,KAAK,UAAU,IAAI,KAAK,QAAQ,GAAG,WAAW,UAAU;AAC5E;AAEO,IAAM,gBAAgB,MAAc,KAAK,aAAa,GAAG,aAAa;AAE7E,IAAM,gBAAgB,OAAe;AAAA,EACnC,cAAc;AAAA,EACd,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,eAAe;AAAA,IACb,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,SAAS,KAAK,aAAa,GAAG,cAAc;AAAA,IAC5C,SAAS;AAAA,EACX;AAAA,EACA,SAAS;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,EACjB;AAAA,EACA,WAAW,CAAC;AAAA,EACZ,aAAa;AAAA,IACX,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,IACX,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,OAAO;AAAA,IACP,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,OAAO;AAAA,MACL,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA,UAAU,CAAC;AAAA,EACX,mBAAmB;AAAA,EACnB,OAAO;AAAA,EACP,cAAc,CAAC;AACjB;AAEA,IAAM,YAAY,CAAC,QAAiC,WAA6D;AAC/G,QAAM,SAAS,EAAE,GAAG,OAAO;AAC3B,aAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,QAAI,OAAO,QAAQ;AACjB,YAAM,OAAO,OAAO,GAAG;AACvB,YAAM,OAAO,OAAO,GAAG;AACvB,UAAI,QAAQ,QAAQ,OAAO,SAAS,YAAY,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAAG;AAChG,eAAO,GAAG,IAAI,UAAU,MAAiC,IAA+B;AAAA,MAC1F,OAAO;AACL,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACA,aAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,QAAI,EAAE,OAAO,SAAS;AACpB,aAAO,GAAG,IAAI,OAAO,GAAG;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,aAAa,MAAc;AACtC,QAAM,aAAa,cAAc;AACjC,QAAM,WAAW,cAAc;AAE/B,MAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AACxD,WAAO,UAAU,UAAgD,GAAG;AAAA,EACtE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,aAAa,CAAC,WAAyB;AAClD,QAAM,YAAY,aAAa;AAC/B,YAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,gBAAc,cAAc,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AACvE;AAEO,IAAM,aAAa,MAAe,CAAC,WAAW,cAAc,CAAC;AAE7D,IAAM,cAAc,CAAC,WAAmB,aAA2B;AACxE,QAAM,SAAS,WAAW;AAC1B,SAAO,UAAU,SAAS,IAAI;AAC9B,aAAW,MAAM;AACnB;AAEO,IAAM,gBAAgB,CAAC,cAA4B;AACxD,QAAM,SAAS,WAAW;AAC1B,SAAO,OAAO,UAAU,SAAS;AACjC,aAAW,MAAM;AACnB;AAEO,IAAM,eAAe,MAA8B;AACxD,SAAO,WAAW,EAAE;AACtB;AAEO,IAAM,sBAAsB,CAAC,WAA2B;AAC7D,QAAM,UAAU,OAAO,OAAO;AAC9B,MAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,WAAO,KAAK,QAAQ,GAAG,QAAQ,MAAM,CAAC,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AAEO,IAAM,gBAAgB,CAAC,UAAkB,WAAW,KAAK,OAAO,SAAe;AACpF,MAAI;AACF,UAAM,OAAO,SAAS,QAAQ;AAC9B,QAAI,KAAK,OAAO,UAAU;AACxB,YAAM,UAAU,WAAW;AAC3B,UAAI,WAAW,OAAO,GAAG;AACvB,YAAI;AACF,qBAAW,SAAS,WAAW,IAAI;AAAA,QACrC,QAAQ;AAAA,QAER;AAAA,MACF;AACA,iBAAW,UAAU,OAAO;AAAA,IAC9B;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEO,IAAM,iBAAiB,CAAC,cAA4B;AACzD,QAAM,SAAS,WAAW;AAC1B,SAAO,SAAS,SAAS,IAAI,KAAK,IAAI;AACtC,aAAW,MAAM;AACnB;AAEO,IAAM,mBAAmB,CAAC,cAA4B;AAC3D,QAAM,SAAS,WAAW;AAC1B,SAAO,OAAO,SAAS,SAAS;AAChC,aAAW,MAAM;AACnB;AAEO,IAAM,cAAc,MAA8B;AACvD,SAAO,WAAW,EAAE;AACtB;AAEO,IAAM,uBAAuB,MAAY;AAC9C,QAAM,SAAS,WAAW;AAC1B,MAAI,OAAO,qBAAqB,EAAG;AACnC,QAAM,SAAS,KAAK,IAAI,IAAI,OAAO,oBAAoB;AACvD,MAAI,UAAU;AACd,aAAW,CAAC,IAAI,EAAE,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AACtD,QAAI,KAAK,QAAQ;AACf,aAAO,OAAO,SAAS,EAAE;AACzB,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI,QAAS,YAAW,MAAM;AAChC;AAEO,IAAM,qBAAqB,CAAC,gBAAgC;AACjE,aAAW,QAAQ,aAAa;AAC9B,QAAI;AACF,iBAAW,IAAI;AAAA,IACjB,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;ACvRA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC,SAAS,uBAAuB,8BAA8B;;;ACF9D,SAAS,eAAAA,cAAa,YAAAC,WAAU,cAAc,UAAU,UAAU,iBAAiB;AACnF,SAAS,QAAAC,OAAM,gBAAgB;AAC/B,SAAS,gBAAgB;;;ACFzB,SAAS,cAAc,mBAAmB;AAC1C,SAAS,WAAAC,UAAS,gBAAgB;AAClC,SAAS,QAAAC,aAAY;AAErB,IAAM,cAAc,CAAC,MAAsB;AACzC,MAAI;AACF,WAAO,aAAa,CAAC;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,SAAS,MAAc,QAAQ,SAAS,KAAK;AAEnD,IAAM,SAAS,MAAe,OAAO,MAAM;AAE3C,IAAM,YAAY,MAAc,YAAY,SAAS,MAAM,WAAW,iBAAiB,MAAM;AAM7F,IAAM,cAAc,CAAC,aAAgC;AAC1D,QAAM,MAAM,UAAU;AACtB,QAAM,MAAM,OAAO;AAEnB,MAAI,YAAY,OAAO,GAAG;AACxB,QAAI;AACF,aAAO,YAAY,GAAG,EACnB,OAAO,CAAC,MAAc,EAAE,WAAW,SAAS,CAAC,EAC7C,OAAO,CAAC,MAAc,CAAC,EAAE,SAAS,MAAM,CAAC,EACzC,IAAI,CAAC,MAAcC,MAAK,KAAK,CAAC,CAAC;AAAA,IACpC,QAAQ;AACN,aAAO,CAACA,MAAK,KAAK,UAAU,GAAG,EAAE,CAAC;AAAA,IACpC;AAAA,EACF;AAEA,SAAO,CAACA,MAAK,KAAK,UAAU,GAAG,EAAE,CAAC;AACpC;;;AD/BO,IAAM,qBAAqB,MAAqB;AACrD,MAAI;AACF,UAAM,SAAS,SAAS,UAAU,EAAE,UAAU,SAAS,SAAS,IAAK,CAAC;AACtE,UAAM,QAAQ,OACX,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,KAAK,SAAS,SAAS,KAAK,CAAC,KAAK,SAAS,MAAM,KAAK,CAAC,KAAK,SAAS,UAAU,CAAC,EACjG,IAAI,CAAC,SAAS;AACb,YAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK;AACrC,YAAM,MAAM,SAAS,MAAM,CAAC,GAAG,EAAE;AACjC,UAAI,MAAM;AACV,UAAI;AACF,cAAM,aAAa,SAAS,GAAG,MAAM;AAAA,MACvC,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,QACL;AAAA,QACA,KAAK,WAAW,MAAM,CAAC,CAAC,KAAK;AAAA,QAC7B,KAAK,WAAW,MAAM,CAAC,CAAC,KAAK;AAAA,QAC7B,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,KAAK;AAAA,QACjC,WAAW,MAAM,CAAC,KAAK;AAAA,QACvB,SAAS,MAAM,MAAM,EAAE,EAAE,KAAK,GAAG;AAAA,QACjC;AAAA,MACF;AAAA,IACF,CAAC,EACA,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,WAAW,MAAM,CAAC;AAC9C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,IAAM,iBAAiB,CAAC,aAAqD;AAC3E,MAAI;AACF,UAAM,KAAK,SAAS,UAAU,GAAG;AACjC,UAAM,MAAM,OAAO,MAAM,KAAK;AAC9B,UAAM,YAAY,SAAS,IAAI,KAAK,GAAG,OAAO,CAAC;AAC/C,cAAU,EAAE;AACZ,UAAM,OAAO,IAAI,SAAS,GAAG,SAAS,EAAE,SAAS,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC;AACvE,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,oBAAoB,CAAC,aAA2D;AACpF,QAAM,QAAoB,EAAE,aAAa,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,cAAc,EAAE;AACxG,MAAI,QAAQ;AAEZ,MAAI;AACF,UAAM,KAAK,SAAS,UAAU,GAAG;AACjC,UAAM,MAAM,OAAO,MAAM,KAAK;AAC9B,UAAM,YAAY,SAAS,IAAI,KAAK,GAAG,OAAO,CAAC;AAC/C,cAAU,EAAE;AACZ,UAAM,OAAO,IAAI,SAAS,GAAG,SAAS,EAAE,SAAS,OAAO;AACxD,UAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,KAAM;AACX,UAAI;AACF,cAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,YAAI,IAAI,SAAS,aAAa;AAC5B,cAAI,CAAC,SAAS,IAAI,SAAS,OAAO;AAChC,oBAAQ,OAAO,IAAI,QAAQ,KAAK;AAAA,UAClC;AACA,gBAAM,IAAI,IAAI,SAAS;AACvB,cAAI,GAAG;AACL,kBAAM,eAAe,EAAE,gBAAgB;AACvC,kBAAM,uBAAuB,EAAE,+BAA+B;AAC9D,kBAAM,mBAAmB,EAAE,2BAA2B;AACtD,kBAAM,gBAAgB,EAAE,iBAAiB;AAAA,UAC3C;AAAA,QACF;AAAA,MACF,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO,EAAE,OAAO,MAAM;AACxB;AAEA,IAAM,gBAAgB,CAAC,MAAsB;AAC3C,SAAO,EAAE,QAAQ,QAAQ,EAAE;AAC7B;AAEO,IAAM,mBAAmB,CAAC,aAAiC;AAChE,QAAM,WAAW,YAAY,QAAQ;AACrC,QAAM,YAAY,mBAAmB;AACrC,QAAM,aAAa,oBAAI,IAAqB;AAE5C,aAAW,WAAW,UAAU;AAC9B,QAAI;AACJ,QAAI;AACF,oBAAcC,aAAY,OAAO;AAAA,IACnC,QAAQ;AACN;AAAA,IACF;AAEA,eAAW,eAAe,aAAa;AACrC,YAAM,cAAcC,MAAK,SAAS,WAAW;AAC7C,UAAI;AACJ,UAAI;AACF,eAAOC,UAAS,WAAW;AAAA,MAC7B,QAAQ;AACN;AAAA,MACF;AACA,UAAI,CAAC,KAAK,YAAY,EAAG;AAEzB,YAAM,WAAWD,MAAK,aAAa,OAAO;AAC1C,UAAI;AACJ,UAAI;AACF,sBAAcD,aAAY,QAAQ,EAC/B,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,CAAC,EACnC,IAAI,CAAC,MAAMC,MAAK,UAAU,CAAC,CAAC;AAAA,MACjC,QAAQ;AACN;AAAA,MACF;AAEA,UAAI,YAAY,WAAW,EAAG;AAE9B,YAAM,WAAqB,CAAC;AAC5B,UAAI,YAAY;AAChB,UAAI,OAAO;AACX,UAAI,MAAM;AACV,UAAI,QAAQ;AACZ,UAAI,UAAU;AACd,UAAI,YAAY;AAChB,UAAI,YAAY;AAChB,UAAI,eAAe;AACnB,YAAM,aAAyB,EAAE,aAAa,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,cAAc,EAAE;AAE7G,iBAAW,cAAc,aAAa;AACpC,cAAM,UAAU,SAAS,YAAY,SAAS;AAC9C,iBAAS,KAAK,OAAO;AAErB,cAAM,aAAa,eAAe,UAAU;AAC5C,YAAI,YAAY;AACd,cAAI,CAAC,UAAW,aAAY,OAAO,WAAW,aAAa,EAAE;AAC7D,cAAI,CAAC,KAAM,QAAO,OAAO,WAAW,QAAQ,EAAE;AAC9C,cAAI,CAAC,IAAK,OAAM,OAAO,WAAW,OAAO,EAAE;AAC3C,cAAI,CAAC,QAAS,WAAU,OAAO,WAAW,WAAW,EAAE;AACvD,cAAI,CAAC,UAAW,aAAY,OAAO,WAAW,aAAa,EAAE;AAAA,QAC/D;AAEA,YAAI;AACF,gBAAM,QAAQC,UAAS,UAAU;AACjC,gBAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,cAAI,UAAU,UAAW,aAAY;AACrC,cAAI,MAAM,UAAU,aAAc,gBAAe,MAAM;AAAA,QACzD,QAAQ;AAAA,QAER;AAEA,cAAM,SAAS,kBAAkB,UAAU;AAC3C,YAAI,CAAC,SAAS,OAAO,OAAO;AAC1B,kBAAQ,OAAO;AAAA,QACjB;AACA,mBAAW,eAAe,OAAO,MAAM;AACvC,mBAAW,uBAAuB,OAAO,MAAM;AAC/C,mBAAW,mBAAmB,OAAO,MAAM;AAC3C,mBAAW,gBAAgB,OAAO,MAAM;AAAA,MAC1C;AAEA,UAAI,CAAC,OAAO;AACV,gBAAQ;AAAA,MACV;AAEA,YAAM,UAAU,cAAc,GAAG;AACjC,YAAM,kBAAkB,UAAU,KAAK,CAAC,MAAM;AAC5C,YAAI,CAAC,EAAE,IAAK,QAAO;AACnB,eAAO,cAAc,EAAE,GAAG,MAAM;AAAA,MAClC,CAAC;AAED,YAAM,UAAmB;AAAA,QACvB;AAAA,QACA,MAAM,QAAQ,UAAU,MAAM,GAAG,EAAE;AAAA,QACnC,SAAS,YAAY,QAAQ,MAAM,GAAG;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK,iBAAiB,OAAO;AAAA,QAC7B,KAAK,iBAAiB,OAAO;AAAA,QAC7B,KAAK,iBAAiB,OAAO;AAAA,QAC7B,OAAO,kBAAkB,KAAK,MAAM,gBAAgB,QAAQ,IAAI,IAAI;AAAA,QACpE,YAAY,SAAS;AAAA,QACrB;AAAA,QACA;AAAA,QACA,WAAW,cAAc,WAAW,KAAK,IAAI,IAAI;AAAA,QACjD;AAAA,QACA,OAAO;AAAA,MACT;AAEA,iBAAW,IAAI,aAAa,aAAa,OAAO;AAAA,IAClD;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,WAAW,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY;AACvF;;;AE9JO,IAAM,eAAe,CAAC,UAA8C,eAAe;AAEnF,IAAM,aAAa,CAAC,UAA4C,cAAc;;;ACnDrF,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,YAAY;AAEX,IAAM,eAAe,CAAC,SAAiC;AAC5D,MAAI,KAAK,aAAa,OAAQ,QAAO;AAErC,QAAM,UAAU,OAAO,KAAK,UAAU,WAAW,EAAE;AACnD,QAAM,UAAU,iBAAiB,KAAK,CAAC,MAAM,EAAE,KAAK,OAAO,CAAC;AAC5D,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,UAAU,UAAU,KAAK,OAAO;AACtC,QAAM,WAAW,UAAU,SAAS;AAEpC,SAAO;AAAA,IACL,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,OAAO;AAAA,IACzC;AAAA,IACA,MAAM;AAAA,IACN,SAAS,UACL,iCAAiC,QAAQ,MAAM,GAAG,EAAE,CAAC,KACrD,uCAAuC,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAC/D,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,OAAO;AAAA,IACP,WAAW,KAAK;AAAA,EAClB;AACF;;;ACpCA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,oBAAoB,CAAC,SAAiC;AACjE,MAAI,KAAK,aAAa,OAAQ,QAAO;AAErC,QAAM,UAAU,OAAO,KAAK,UAAU,WAAW,EAAE;AACnD,QAAM,UAAU,eAAe,KAAK,CAAC,MAAM,EAAE,KAAK,OAAO,CAAC;AAC1D,MAAI,CAAC,QAAS,QAAO;AAErB,SAAO;AAAA,IACL,IAAI,SAAS,KAAK,SAAS,IAAI,KAAK,OAAO;AAAA,IAC3C,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,gCAAgC,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAC7D,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,OAAO;AAAA,IACP,WAAW,KAAK;AAAA,EAClB;AACF;;;AC5BA,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,kBAAkB,CAAC,QAAQ,QAAQ,QAAQ,MAAM;AAEhD,IAAM,sBAAsB,CAAC,SAAiC;AACnE,MAAI,CAAC,gBAAgB,SAAS,KAAK,QAAQ,EAAG,QAAO;AAErD,QAAM,SAAS,KAAK,UAAU,KAAK,SAAS;AAC5C,QAAM,UAAU,mBAAmB,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAC7D,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,SAAS,OAAO,KAAK,UAAU,aAAa,KAAK,UAAU,WAAW,KAAK,UAAU,WAAW,EAAE,EAAE;AAAA,IACxG;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI,QAAQ,KAAK,SAAS,IAAI,KAAK,OAAO;AAAA,IAC1C,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,6BAA6B,MAAM;AAAA,IAC5C,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,OAAO;AAAA,IACP,WAAW,KAAK;AAAA,EAClB;AACF;;;AC7CA,IAAM,iBAA2F;AAAA,EAC/F,EAAE,SAAS,kBAAkB,UAAU,QAAQ,OAAO,iBAAiB;AAAA,EACvE,EAAE,SAAS,mBAAmB,UAAU,QAAQ,OAAO,YAAY;AAAA,EACnE,EAAE,SAAS,mBAAmB,UAAU,YAAY,OAAO,eAAe;AAAA,EAC1E,EAAE,SAAS,YAAY,UAAU,QAAQ,OAAO,aAAa;AAAA,EAC7D,EAAE,SAAS,kBAAkB,UAAU,QAAQ,OAAO,WAAW;AAAA,EACjE,EAAE,SAAS,eAAe,UAAU,YAAY,OAAO,mBAAmB;AAAA,EAC1E,EAAE,SAAS,eAAe,UAAU,YAAY,OAAO,mBAAmB;AAAA,EAC1E,EAAE,SAAS,gBAAgB,UAAU,YAAY,OAAO,kBAAkB;AAAA,EAC1E,EAAE,SAAS,yBAAyB,UAAU,YAAY,OAAO,WAAW;AAAA,EAC5E,EAAE,SAAS,uBAAuB,UAAU,YAAY,OAAO,eAAe;AAAA,EAC9E,EAAE,SAAS,YAAY,UAAU,YAAY,OAAO,oBAAoB;AAAA,EACxE,EAAE,SAAS,gBAAgB,UAAU,QAAQ,OAAO,wBAAwB;AAC9E;AAEO,IAAM,mBAAmB,CAAC,SAAiC;AAChE,MAAI,KAAK,aAAa,OAAQ,QAAO;AAErC,QAAM,UAAU,OAAO,KAAK,UAAU,WAAW,EAAE;AAEnD,aAAW,QAAQ,gBAAgB;AACjC,QAAI,KAAK,QAAQ,KAAK,OAAO,GAAG;AAC9B,aAAO;AAAA,QACL,IAAI,SAAS,KAAK,SAAS,IAAI,KAAK,OAAO;AAAA,QAC3C,UAAU,KAAK;AAAA,QACf,MAAM;AAAA,QACN,SAAS,GAAG,KAAK,KAAK,KAAK,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,QAC/C,aAAa,KAAK;AAAA,QAClB,WAAW,KAAK;AAAA,QAChB,OAAO;AAAA,QACP,WAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACnCA,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,mBAAmB;AAAA,EACvB;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAEO,IAAM,iBAAiB,CAAC,UAAuC;AACpE,MAAI,WAAW,KAAK,GAAG;AACrB,WAAO,uBAAuB,KAAK;AAAA,EACrC;AACA,MAAI,aAAa,KAAK,GAAG;AACvB,WAAO,yBAAyB,KAAK;AAAA,EACvC;AACA,SAAO;AACT;AAEA,IAAM,yBAAyB,CAAC,SAAiC;AAC/D,QAAM,SAAS,KAAK,UAAU,KAAK,SAAS;AAE5C,QAAM,UAAU,mBAAmB,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAC7D,MAAI,CAAC,QAAS,QAAO;AAErB,SAAO;AAAA,IACL,IAAI,eAAe,KAAK,SAAS,IAAI,KAAK,OAAO;AAAA,IACjD,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,uBAAuB,KAAK,QAAQ;AAAA,IAC7C,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,OAAO;AAAA,IACP,WAAW,KAAK;AAAA,EAClB;AACF;AAEA,IAAM,2BAA2B,CAAC,WAAqC;AACrE,QAAM,UAAU,OAAO;AACvB,MAAI,CAAC,WAAW,QAAQ,SAAS,GAAI,QAAO;AAE5C,QAAM,mBAAmB,mBAAmB,KAAK,CAAC,MAAM,EAAE,KAAK,OAAO,CAAC;AACvE,QAAM,eAAe,iBAAiB,KAAK,CAAC,MAAM,EAAE,KAAK,OAAO,CAAC;AAEjE,MAAI,CAAC,oBAAoB,CAAC,aAAc,QAAO;AAE/C,QAAM,iBAAiB,mBAAmB,KAAK,CAAC,MAAM,EAAE,KAAK,OAAO,CAAC;AACrE,QAAM,UAAU,iBAAiB,QAAQ,MAAM,cAAc,IAAI,CAAC,GAAG,MAAM,GAAG,EAAE,KAAK,KAAK;AAE1F,SAAO;AAAA,IACL,IAAI,iBAAiB,OAAO,SAAS,IAAI,OAAO,OAAO;AAAA,IACvD,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS,qCAAqC,OAAO;AAAA,IACrD,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO;AAAA,IAClB,OAAO;AAAA,IACP,WAAW,OAAO;AAAA,EACpB;AACF;;;AC9DA,IAAM,gBAA2C;AAAA,EAC/C,EAAE,KAAK,WAAW,IAAI,aAAa;AAAA,EACnC,EAAE,KAAK,gBAAgB,IAAI,kBAAkB;AAAA,EAC7C,EAAE,KAAK,kBAAkB,IAAI,oBAAoB;AAAA,EACjD,EAAE,KAAK,eAAe,IAAI,iBAAiB;AAC7C;AAEA,IAAM,gBAAgD,CAAC,EAAE,KAAK,aAAa,IAAI,eAAe,CAAC;AAE/F,IAAM,iBAAgD;AAAA,EACpD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,UAAU;AACZ;AAEA,IAAM,kBAAkB;AAEjB,IAAM,iBAAN,MAAqB;AAAA,EAClB,eAAe,oBAAI,IAAoB;AAAA,EACvC;AAAA,EACA;AAAA,EAER,YAAY,WAA0B,QAAQ,aAAmC;AAC/E,SAAK,WAAW;AAChB,SAAK,cAAc,eAAe;AAAA,MAChC,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EAEA,QAAQ,MAAyB;AAC/B,WAAO,KAAK,aAAa,IAAI;AAAA,EAC/B;AAAA,EAEA,cAAc,QAA6B;AACzC,WAAO,KAAK,aAAa,MAAM;AAAA,EACjC;AAAA,EAEA,aAAa,OAA+B;AAC1C,UAAM,SAAkB,CAAC;AAEzB,QAAI,WAAW,KAAK,GAAG;AACrB,iBAAW,QAAQ,eAAe;AAChC,YAAI,CAAC,KAAK,YAAY,KAAK,GAAG,EAAG;AACjC,cAAM,QAAQ,KAAK,GAAG,KAAK;AAC3B,YAAI,MAAO,QAAO,KAAK,KAAK;AAAA,MAC9B;AAAA,IACF;AAEA,eAAW,QAAQ,eAAe;AAChC,UAAI,CAAC,KAAK,YAAY,KAAK,GAAG,EAAG;AACjC,YAAM,QAAQ,KAAK,GAAG,KAAK;AAC3B,UAAI,MAAO,QAAO,KAAK,KAAK;AAAA,IAC9B;AAEA,WAAO,OAAO,OAAO,CAAC,UAAU;AAC9B,UAAI,eAAe,MAAM,QAAQ,IAAI,eAAe,KAAK,QAAQ,EAAG,QAAO;AAE3E,YAAM,WAAW,GAAG,MAAM,IAAI,IAAI,MAAM,SAAS,IAAI,MAAM,QAAQ,MAAM,GAAG,EAAE,CAAC;AAC/E,YAAM,WAAW,KAAK,aAAa,IAAI,QAAQ;AAC/C,UAAI,YAAY,MAAM,YAAY,WAAW,gBAAiB,QAAO;AAErE,WAAK,aAAa,IAAI,UAAU,MAAM,SAAS;AAC/C,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,iBAAuB;AACrB,UAAM,SAAS,KAAK,IAAI,IAAI,kBAAkB;AAC9C,eAAW,CAAC,KAAK,EAAE,KAAK,KAAK,cAAc;AACzC,UAAI,KAAK,OAAQ,MAAK,aAAa,OAAO,GAAG;AAAA,IAC/C;AAAA,EACF;AACF;;;AC/FA,SAAS,aAAa;;;ACAtB,SAAS,YAAAC,WAAU,YAAAC,WAAU,aAAAC,YAAW,YAAAC,iBAAgB;AAEjD,IAAM,aAAN,MAAiB;AAAA,EACd,UAAU,oBAAI,IAAoB;AAAA,EAE1C,aAAa,UAA4B;AACvC,QAAI;AACJ,QAAI;AACF,oBAAcA,UAAS,QAAQ,EAAE;AAAA,IACnC,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,aAAa,KAAK,QAAQ,IAAI,QAAQ,KAAK;AACjD,QAAI,eAAe,WAAY,QAAO,CAAC;AAEvC,UAAM,cAAc,cAAc;AAClC,UAAM,MAAM,OAAO,MAAM,WAAW;AAEpC,QAAI;AACJ,QAAI;AACF,WAAKH,UAAS,UAAU,GAAG;AAAA,IAC7B,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAEA,QAAI;AACF,MAAAC,UAAS,IAAI,KAAK,GAAG,aAAa,UAAU;AAAA,IAC9C,UAAE;AACA,MAAAC,WAAU,EAAE;AAAA,IACd;AAEA,SAAK,QAAQ,IAAI,UAAU,WAAW;AAEtC,UAAM,OAAO,IAAI,SAAS,OAAO;AACjC,UAAM,QAAQ,KAAK,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC;AAChE,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,UAAwB;AAChC,QAAI;AACF,YAAM,OAAOC,UAAS,QAAQ,EAAE;AAChC,WAAK,QAAQ,IAAI,UAAU,IAAI;AAAA,IACjC,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,UAAwB;AAC5B,SAAK,QAAQ,OAAO,QAAQ;AAAA,EAC9B;AAAA,EAEA,WAAiB;AACf,SAAK,QAAQ,MAAM;AAAA,EACrB;AACF;;;ACrDA,IAAM,sBAAsB,CAAC,UAA4B;AACvD,MAAI,MAAM,WAAW;AACnB,UAAM,SAAS,IAAI,KAAK,MAAM,SAAS,EAAE,QAAQ;AACjD,QAAI,CAAC,MAAM,MAAM,EAAG,QAAO;AAAA,EAC7B;AACA,SAAO,KAAK,IAAI;AAClB;AAEO,IAAM,YAAY,CAAC,SAAkC;AAC1D,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,mBAAmB,CAAC,UAAgC;AAC/D,MAAI,MAAM,SAAS,YAAa,QAAO,CAAC;AAExC,QAAM,UAAU,MAAM,SAAS;AAC/B,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO,CAAC;AAErC,QAAM,KAAK,oBAAoB,KAAK;AACpC,QAAM,QAAoB,CAAC;AAE3B,aAAW,SAAS,SAAS;AAC3B,QACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACT,MAAkC,SAAS,YAC5C;AACA,YAAM,YAAY;AAClB,YAAM,KAAK;AAAA,QACT,WAAW,MAAM;AAAA,QACjB,SAAS,MAAM;AAAA,QACf,MAAQ,MAAkC,QAAmB;AAAA,QAC7D,WAAW;AAAA,QACX,UAAW,UAAU,QAAmB;AAAA,QACxC,WAAY,UAAU,SAAqC,CAAC;AAAA,QAC5D,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAEO,IAAM,qBAAqB,CAAC,UAAkC;AACnE,MAAI,MAAM,SAAS,OAAQ,QAAO,CAAC;AAEnC,QAAM,UAAU,MAAM,SAAS;AAC/B,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO,CAAC;AAErC,QAAM,KAAK,oBAAoB,KAAK;AACpC,QAAM,UAAwB,CAAC;AAE/B,aAAW,SAAS,SAAS;AAC3B,QACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACT,MAAkC,SAAS,eAC5C;AACA,YAAM,cAAc;AACpB,YAAM,gBAAgB,YAAY;AAClC,UAAI,OAAO;AACX,UAAI,OAAO,kBAAkB,UAAU;AACrC,eAAO;AAAA,MACT,WAAW,MAAM,QAAQ,aAAa,GAAG;AACvC,eAAO,cACJ,IAAI,CAAC,MAAO,OAAO,MAAM,YAAY,MAAM,OAAQ,EAA8B,QAAQ,KAAK,OAAO,CAAC,CAAE,EACxG,KAAK,IAAI;AAAA,MACd;AAEA,cAAQ,KAAK;AAAA,QACX,WAAW,MAAM;AAAA,QACjB,SAAS,MAAM;AAAA,QACf,MAAQ,MAAkC,QAAmB;AAAA,QAC7D,WAAW;AAAA,QACX,WAAW,OAAO,YAAY,eAAe,EAAE;AAAA,QAC/C,SAAS;AAAA,QACT,SAAS,QAAQ,YAAY,QAAQ;AAAA,QACrC,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAEO,IAAM,eAAe,CAAC,UAAuC;AAClE,MAAI,MAAM,SAAS,YAAa,QAAO;AAEvC,QAAM,QAAQ,MAAM,SAAS;AAC7B,MAAI,CAAC,MAAO,QAAO;AAEnB,SAAO;AAAA,IACL,aAAa,MAAM,gBAAgB;AAAA,IACnC,qBAAqB,MAAM,+BAA+B;AAAA,IAC1D,iBAAiB,MAAM,2BAA2B;AAAA,IAClD,cAAc,MAAM,iBAAiB;AAAA,EACvC;AACF;AAEO,IAAM,aAAa,CAAC,UAAgC;AACzD,QAAM,QAAoB,CAAC;AAC3B,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,UAAU,IAAI;AAC5B,QAAI,OAAO;AACT,YAAM,KAAK,GAAG,iBAAiB,KAAK,CAAC;AAAA,IACvC;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,iBAAiB,CAAC,UAAqC;AAClE,QAAM,SAA0B,CAAC;AACjC,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,UAAU,IAAI;AAC5B,QAAI,OAAO;AACT,aAAO,KAAK,GAAG,iBAAiB,KAAK,CAAC;AACtC,aAAO,KAAK,GAAG,mBAAmB,KAAK,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,sBAAsB,CAAC,UAAgC;AAClE,QAAM,QAAoB,EAAE,aAAa,GAAG,qBAAqB,GAAG,iBAAiB,GAAG,cAAc,EAAE;AACxG,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,UAAU,IAAI;AAC5B,QAAI,OAAO;AACT,YAAM,QAAQ,aAAa,KAAK;AAChC,UAAI,OAAO;AACT,cAAM,eAAe,MAAM;AAC3B,cAAM,uBAAuB,MAAM;AACnC,cAAM,mBAAmB,MAAM;AAC/B,cAAM,gBAAgB,MAAM;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AFrIO,IAAM,UAAN,MAAc;AAAA,EACX,UAA4B;AAAA,EAC5B,SAAS,IAAI,WAAW;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa,oBAAI,IAAY;AAAA,EAErC,YACE,SACA,UACA,iBACA,cACA;AACA,SAAK,UAAU;AACf,SAAK,WAAW;AAChB,SAAK,kBAAkB,mBAAmB;AAC1C,SAAK,eAAe,gBAAgB;AAAA,EACtC;AAAA,EAEA,QAAc;AACZ,UAAM,WAAW,YAAY,KAAK,QAAQ;AAC1C,UAAM,QAAQ,SAAS,IAAI,CAAC,MAAM,GAAG,CAAC,oBAAoB;AAE1D,SAAK,UAAU,MAAM,OAAO;AAAA,MAC1B,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,kBAAkB;AAAA,MAClB,YAAY;AAAA,IACd,CAAC;AAED,SAAK,QAAQ,GAAG,OAAO,CAAC,aAAqB;AAC3C,UAAI,KAAK,WAAW,IAAI,QAAQ,EAAG;AACnC,WAAK,WAAW,IAAI,QAAQ;AAC5B,WAAK,OAAO,UAAU,QAAQ;AAAA,IAChC,CAAC;AAED,SAAK,QAAQ,GAAG,UAAU,CAAC,aAAqB;AAC9C,YAAM,QAAQ,KAAK,OAAO,aAAa,QAAQ;AAC/C,UAAI,MAAM,WAAW,EAAG;AAExB,YAAM,QAAQ,WAAW,KAAK;AAC9B,UAAI,MAAM,SAAS,GAAG;AACpB,aAAK,QAAQ,KAAK;AAAA,MACpB;AAEA,UAAI,KAAK,iBAAiB;AACxB,cAAM,YAAY,eAAe,KAAK;AACtC,YAAI,UAAU,SAAS,GAAG;AACxB,eAAK,gBAAgB,SAAS;AAAA,QAChC;AAAA,MACF;AAEA,UAAI,KAAK,cAAc;AACrB,cAAM,QAAQ,oBAAoB,KAAK;AACvC,YAAI,MAAM,cAAc,KAAK,MAAM,eAAe,GAAG;AACnD,gBAAM,YAAY,MAAM,CAAC;AACzB,cAAI,WAAW;AACb,iBAAK,aAAa,UAAU,WAAW,KAAK;AAAA,UAC9C;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,OAAa;AACX,SAAK,SAAS,MAAM;AACpB,SAAK,UAAU;AACf,SAAK,OAAO,SAAS;AACrB,SAAK,WAAW,MAAM;AAAA,EACxB;AAAA,EAEA,aAAa,UAA8B;AACzC,SAAK,OAAO,MAAM,QAAQ;AAC1B,UAAM,QAAQ,KAAK,OAAO,aAAa,QAAQ;AAC/C,WAAO,WAAW,KAAK;AAAA,EACzB;AACF;;;AVjFA,IAAM,aAAa;AACnB,IAAM,eAAe;AAEd,IAAM,iBAAiB,OAAO,UAAmB,eAAuC;AAC7F,QAAM,SAAkB,CAAC;AACzB,QAAM,WAAW,oBAAI,IAAwB;AAC7C,QAAM,SAAS,aAAa,OAAO,IAAI,eAAe,MAAM;AAE5D,QAAM,cAAc,CAAC,UAAsB;AACzC,eAAW,QAAQ,OAAO;AACxB,YAAM,WAAW,SAAS,IAAI,KAAK,SAAS,KAAK,CAAC;AAClD,eAAS,KAAK,IAAI;AAClB,UAAI,SAAS,SAAS,aAAc,UAAS,OAAO,GAAG,SAAS,SAAS,YAAY;AACrF,eAAS,IAAI,KAAK,WAAW,QAAQ;AAAA,IACvC;AAAA,EACF;AAEA,QAAM,kBAAkB,SACpB,CAAC,WAA4B;AAC3B,eAAW,SAAS,QAAQ;AAC1B,YAAM,YAAY,OAAO,aAAa,KAAK;AAC3C,aAAO,KAAK,GAAG,SAAS;AACxB,UAAI,OAAO,SAAS,WAAY,QAAO,OAAO,GAAG,OAAO,SAAS,UAAU;AAAA,IAC7E;AAAA,EACF,IACA;AAEJ,QAAM,UAAU,IAAI,QAAQ,aAAa,UAAU,eAAe;AAClE,UAAQ,MAAM;AAEd,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,YAAY,SAAS,QAAQ;AAAA,IACrC;AAAA,MACE,cAAc,EAAE,OAAO,CAAC,EAAE;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,kBAAkB,wBAAwB,aAAa;AAAA,IAC5D,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,aAAa,EAAE,MAAM,UAAmB,YAAY,CAAC,EAAE;AAAA,MACzD;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY;AAAA,YACV,UAAU;AAAA,cACR,MAAM;AAAA,cACN,MAAM,CAAC,QAAQ,QAAQ,QAAQ,UAAU;AAAA,cACzC,aAAa;AAAA,YACf;AAAA,YACA,OAAO,EAAE,MAAM,UAAU,aAAa,oCAAoC;AAAA,UAC5E;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY;AAAA,YACV,WAAW,EAAE,MAAM,UAAU,aAAa,4BAA4B;AAAA,UACxE;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY;AAAA,YACV,WAAW,EAAE,MAAM,UAAU,aAAa,aAAa;AAAA,YACvD,OAAO,EAAE,MAAM,UAAU,aAAa,oCAAoC;AAAA,UAC5E;AAAA,UACA,UAAU,CAAC,WAAW;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF,EAAE;AAEF,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,EAAE,MAAM,WAAW,KAAK,IAAI,QAAQ;AAE1C,YAAQ,MAAM;AAAA,MACZ,KAAK,qBAAqB;AACxB,cAAM,WAAW,iBAAiB,QAAQ;AAC1C,cAAM,OAAO,SAAS,IAAI,CAAC,OAAO;AAAA,UAChC,WAAW,EAAE;AAAA,UACb,MAAM,EAAE;AAAA,UACR,OAAO,EAAE;AAAA,UACT,KAAK,EAAE;AAAA,UACP,KAAK,EAAE;AAAA,UACP,OAAO,EAAE;AAAA,UACT,QAAQ,EAAE;AAAA,UACV,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,QAAQ,EAAE,MAAM,cAAc,WAAW,EAAE,MAAM,gBAAgB;AAAA,QACzG,EAAE;AACF,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA,MAC5E;AAAA,MAEA,KAAK,mBAAmB;AACtB,cAAM,WAAY,MAAM,YAA8B;AACtD,cAAM,QAAS,MAAM,SAAoB;AACzC,cAAM,QAAgC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,EAAE;AAC/E,cAAM,WAAW,MAAM,QAAQ,KAAK;AACpC,cAAM,WAAW,OACd,OAAO,CAAC,OAAO,MAAM,EAAE,QAAQ,KAAK,MAAM,QAAQ,EAClD,MAAM,CAAC,KAAK,EACZ,IAAI,CAAC,OAAO;AAAA,UACX,UAAU,EAAE;AAAA,UACZ,MAAM,EAAE;AAAA,UACR,SAAS,EAAE;AAAA,UACX,aAAa,EAAE;AAAA,UACf,WAAW,IAAI,KAAK,EAAE,SAAS,EAAE,YAAY;AAAA,QAC/C,EAAE;AACJ,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA,MAChF;AAAA,MAEA,KAAK,kBAAkB;AACrB,cAAM,WAAW,iBAAiB,QAAQ;AAC1C,cAAM,YAAY,MAAM;AACxB,cAAM,UAAU,YAAY,SAAS,OAAO,CAAC,MAAM,EAAE,cAAc,SAAS,IAAI;AAChF,cAAM,OAAO,QAAQ,IAAI,CAAC,OAAO;AAAA,UAC/B,WAAW,EAAE;AAAA,UACb,MAAM,EAAE;AAAA,UACR,OAAO,EAAE;AAAA,QACX,EAAE;AACF,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA,MAC5E;AAAA,MAEA,KAAK,qBAAqB;AACxB,cAAM,MAAM,MAAM;AAClB,cAAM,QAAS,MAAM,SAAoB;AACzC,cAAM,UAAU,SAAS,IAAI,GAAG,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO;AAAA,UACjE,WAAW,IAAI,KAAK,EAAE,SAAS,EAAE,YAAY;AAAA,UAC7C,MAAM,EAAE;AAAA,UACR,OAAO,EAAE;AAAA,QACX,EAAE;AACF,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA,MAC9E;AAAA,MAEA;AACE,eAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAiB,IAAI,GAAG,CAAC,GAAG,SAAS,KAAK;AAAA,IACvF;AAAA,EACF,CAAC;AAED,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAE9B,UAAQ,GAAG,UAAU,MAAM;AACzB,YAAQ,KAAK;AACb,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACD,UAAQ,GAAG,WAAW,MAAM;AAC1B,YAAQ,KAAK;AACb,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;","names":["readdirSync","statSync","join","homedir","join","join","readdirSync","join","statSync","openSync","readSync","closeSync","statSync"]}
|