averecion-lite 1.3.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 +161 -0
- package/dashboard/dash.css +1085 -0
- package/dashboard/dash.js +898 -0
- package/dashboard/index.html +312 -0
- package/dashboard/landing.html +360 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +409 -0
- package/dist/hooks.d.ts +25 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +68 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/injectionGuard.d.ts +9 -0
- package/dist/injectionGuard.d.ts.map +1 -0
- package/dist/injectionGuard.js +16 -0
- package/dist/log-watcher.d.ts +26 -0
- package/dist/log-watcher.d.ts.map +1 -0
- package/dist/log-watcher.js +397 -0
- package/dist/metrics.d.ts +53 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +58 -0
- package/dist/policy.d.ts +11 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +60 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +226 -0
- package/dist/src/capability-manifest.d.ts +16 -0
- package/dist/src/capability-manifest.d.ts.map +1 -0
- package/dist/src/capability-manifest.js +228 -0
- package/dist/src/http-proxy.d.ts +4 -0
- package/dist/src/http-proxy.d.ts.map +1 -0
- package/dist/src/http-proxy.js +266 -0
- package/dist/src/risk-engine.d.ts +43 -0
- package/dist/src/risk-engine.d.ts.map +1 -0
- package/dist/src/risk-engine.js +258 -0
- package/dist/src/shell-wrapper.d.ts +3 -0
- package/dist/src/shell-wrapper.d.ts.map +1 -0
- package/dist/src/shell-wrapper.js +264 -0
- package/dist/storage.d.ts +28 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +144 -0
- package/examples/INTEGRATION.md +162 -0
- package/examples/claude-desktop-agent.json +32 -0
- package/examples/clawdbot-agent.json +44 -0
- package/examples/custom-agent.json +20 -0
- package/lite-policy.json +5 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-watcher.d.ts","sourceRoot":"","sources":["../log-watcher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AA6CtC,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,YAAY,CAA+B;;IAMnD,KAAK,IAAI,IAAI;IAUb,IAAI,IAAI,IAAI;IAWZ,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,SAAS;IAqCjB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,YAAY;IA+BpB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,YAAY;IAmHpB,OAAO,CAAC,eAAe;IAuCvB,OAAO,CAAC,eAAe;IA6BvB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,gBAAgB;CAWzB;AAID,wBAAgB,eAAe,IAAI,UAAU,CAM5C;AAED,wBAAgB,cAAc,IAAI,IAAI,CAKrC;AAED,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAEjD"}
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LogWatcher = void 0;
|
|
37
|
+
exports.startLogWatcher = startLogWatcher;
|
|
38
|
+
exports.stopLogWatcher = stopLogWatcher;
|
|
39
|
+
exports.getLogWatcher = getLogWatcher;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const events_1 = require("events");
|
|
43
|
+
const storage_1 = require("./storage");
|
|
44
|
+
const CLAWDBOT_LOG_DIR = "/tmp/clawdbot";
|
|
45
|
+
const DANGEROUS_PATTERNS = [
|
|
46
|
+
{ pattern: /rm\s+-rf?\s+\//, reason: "Dangerous rm command (root delete)" },
|
|
47
|
+
{ pattern: /rm\s+-rf?\s+~/, reason: "Dangerous rm command (home delete)" },
|
|
48
|
+
{ pattern: /:\(\)\s*{\s*:\|:&\s*};:/, reason: "Fork bomb detected" },
|
|
49
|
+
{ pattern: /mkfs\./, reason: "Filesystem format command" },
|
|
50
|
+
{ pattern: /dd\s+if=.*of=\/dev/, reason: "Dangerous dd to device" },
|
|
51
|
+
{ pattern: />\s*\/dev\/sd[a-z]/, reason: "Direct write to block device" },
|
|
52
|
+
{ pattern: /curl.*\|\s*(ba)?sh/, reason: "Curl pipe to shell" },
|
|
53
|
+
{ pattern: /wget.*\|\s*(ba)?sh/, reason: "Wget pipe to shell" },
|
|
54
|
+
{ pattern: /chmod\s+777/, reason: "Insecure permissions (777)" },
|
|
55
|
+
{ pattern: /sudo\s+su/, reason: "Privilege escalation" },
|
|
56
|
+
];
|
|
57
|
+
const INJECTION_PATTERNS = [
|
|
58
|
+
{ pattern: /ignore\s+(all\s+)?(previous|prior|above)\s+(instructions?|prompts?)/i, reason: "Prompt injection: ignore instructions" },
|
|
59
|
+
{ pattern: /disregard\s+(all\s+)?(previous|prior|above)/i, reason: "Prompt injection: disregard" },
|
|
60
|
+
{ pattern: /you\s+are\s+now\s+(a|an)\s+/i, reason: "Prompt injection: role override" },
|
|
61
|
+
{ pattern: /jailbreak/i, reason: "Prompt injection: jailbreak attempt" },
|
|
62
|
+
{ pattern: /DAN\s*mode/i, reason: "Prompt injection: DAN mode" },
|
|
63
|
+
{ pattern: /pretend\s+(you'?re?|to\s+be)\s+/i, reason: "Prompt injection: pretend" },
|
|
64
|
+
{ pattern: /bypass\s+(your\s+)?(restrictions?|rules?|guidelines?)/i, reason: "Prompt injection: bypass" },
|
|
65
|
+
];
|
|
66
|
+
class LogWatcher extends events_1.EventEmitter {
|
|
67
|
+
watchedFile = null;
|
|
68
|
+
watcher = null;
|
|
69
|
+
filePosition = 0;
|
|
70
|
+
activeRuns = new Map();
|
|
71
|
+
pollInterval = null;
|
|
72
|
+
constructor() {
|
|
73
|
+
super();
|
|
74
|
+
}
|
|
75
|
+
start() {
|
|
76
|
+
const logFile = this.getCurrentLogFile();
|
|
77
|
+
if (!logFile) {
|
|
78
|
+
console.log("[LogWatcher] ClawdBot log directory not found, will poll for it...");
|
|
79
|
+
this.pollInterval = setInterval(() => this.checkForLogFile(), 5000);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.watchFile(logFile);
|
|
83
|
+
}
|
|
84
|
+
stop() {
|
|
85
|
+
if (this.watcher) {
|
|
86
|
+
this.watcher.close();
|
|
87
|
+
this.watcher = null;
|
|
88
|
+
}
|
|
89
|
+
if (this.pollInterval) {
|
|
90
|
+
clearInterval(this.pollInterval);
|
|
91
|
+
this.pollInterval = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
getCurrentLogFile() {
|
|
95
|
+
if (!fs.existsSync(CLAWDBOT_LOG_DIR))
|
|
96
|
+
return null;
|
|
97
|
+
const today = new Date().toISOString().split("T")[0];
|
|
98
|
+
const logFile = path.join(CLAWDBOT_LOG_DIR, `clawdbot-${today}.log`);
|
|
99
|
+
return fs.existsSync(logFile) ? logFile : null;
|
|
100
|
+
}
|
|
101
|
+
checkForLogFile() {
|
|
102
|
+
const logFile = this.getCurrentLogFile();
|
|
103
|
+
if (logFile) {
|
|
104
|
+
if (this.pollInterval) {
|
|
105
|
+
clearInterval(this.pollInterval);
|
|
106
|
+
this.pollInterval = null;
|
|
107
|
+
}
|
|
108
|
+
this.watchFile(logFile);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
watchFile(logFile) {
|
|
112
|
+
if (this.watchedFile === logFile)
|
|
113
|
+
return;
|
|
114
|
+
if (this.watcher) {
|
|
115
|
+
this.watcher.close();
|
|
116
|
+
}
|
|
117
|
+
this.watchedFile = logFile;
|
|
118
|
+
this.filePosition = fs.existsSync(logFile) ? fs.statSync(logFile).size : 0;
|
|
119
|
+
console.log(`[LogWatcher] Watching ${logFile} from position ${this.filePosition}`);
|
|
120
|
+
try {
|
|
121
|
+
this.watcher = fs.watch(logFile, (eventType) => {
|
|
122
|
+
if (eventType === "change") {
|
|
123
|
+
this.readNewLines();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
this.watcher.on("error", (err) => {
|
|
127
|
+
console.error("[LogWatcher] Watch error:", err);
|
|
128
|
+
this.scheduleRewatch();
|
|
129
|
+
});
|
|
130
|
+
setInterval(() => {
|
|
131
|
+
const currentLogFile = this.getCurrentLogFile();
|
|
132
|
+
if (currentLogFile && currentLogFile !== this.watchedFile) {
|
|
133
|
+
console.log("[LogWatcher] Log file rotated, switching to new file");
|
|
134
|
+
this.watchFile(currentLogFile);
|
|
135
|
+
}
|
|
136
|
+
}, 60000);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
console.error("[LogWatcher] Failed to watch file:", err);
|
|
140
|
+
this.scheduleRewatch();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
scheduleRewatch() {
|
|
144
|
+
setTimeout(() => {
|
|
145
|
+
const logFile = this.getCurrentLogFile();
|
|
146
|
+
if (logFile)
|
|
147
|
+
this.watchFile(logFile);
|
|
148
|
+
}, 5000);
|
|
149
|
+
}
|
|
150
|
+
readNewLines() {
|
|
151
|
+
if (!this.watchedFile)
|
|
152
|
+
return;
|
|
153
|
+
try {
|
|
154
|
+
const stat = fs.statSync(this.watchedFile);
|
|
155
|
+
if (stat.size < this.filePosition) {
|
|
156
|
+
console.log("[LogWatcher] Log file truncated or rotated, resetting position");
|
|
157
|
+
this.filePosition = 0;
|
|
158
|
+
}
|
|
159
|
+
if (stat.size <= this.filePosition)
|
|
160
|
+
return;
|
|
161
|
+
const fd = fs.openSync(this.watchedFile, "r");
|
|
162
|
+
const buffer = Buffer.alloc(stat.size - this.filePosition);
|
|
163
|
+
fs.readSync(fd, buffer, 0, buffer.length, this.filePosition);
|
|
164
|
+
fs.closeSync(fd);
|
|
165
|
+
this.filePosition = stat.size;
|
|
166
|
+
const lines = buffer.toString("utf-8").split("\n");
|
|
167
|
+
for (const line of lines) {
|
|
168
|
+
if (line.trim()) {
|
|
169
|
+
this.processLine(line);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
console.error("[LogWatcher] Error reading file:", err);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
processLine(line) {
|
|
178
|
+
try {
|
|
179
|
+
const parsed = this.parseLogLine(line);
|
|
180
|
+
if (!parsed)
|
|
181
|
+
return;
|
|
182
|
+
if (parsed.toolEvent) {
|
|
183
|
+
this.handleToolEvent(parsed.toolEvent);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch { }
|
|
187
|
+
}
|
|
188
|
+
parseLogLine(line) {
|
|
189
|
+
try {
|
|
190
|
+
const json = JSON.parse(line);
|
|
191
|
+
const meta = json._meta || {};
|
|
192
|
+
const timestamp = meta.date ? new Date(meta.date) : new Date();
|
|
193
|
+
const logLevel = meta.logLevelName || "INFO";
|
|
194
|
+
let subsystem = "";
|
|
195
|
+
let message = "";
|
|
196
|
+
if (typeof json["0"] === "string") {
|
|
197
|
+
try {
|
|
198
|
+
const parsed = JSON.parse(json["0"]);
|
|
199
|
+
subsystem = parsed.subsystem || "";
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
subsystem = json["0"];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (typeof json["1"] === "string") {
|
|
206
|
+
message = json["1"];
|
|
207
|
+
}
|
|
208
|
+
let toolEvent;
|
|
209
|
+
if (subsystem.includes("agent/embedded") && message) {
|
|
210
|
+
const startMatch = message.match(/embedded run tool start: runId=(\S+) tool=(\S+) toolCallId=(\S+)/);
|
|
211
|
+
if (startMatch) {
|
|
212
|
+
toolEvent = {
|
|
213
|
+
runId: startMatch[1],
|
|
214
|
+
tool: startMatch[2],
|
|
215
|
+
toolCallId: startMatch[3],
|
|
216
|
+
phase: "start",
|
|
217
|
+
timestamp,
|
|
218
|
+
args: this.extractToolArgs(json),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const endMatch = message.match(/embedded run tool end: runId=(\S+) tool=(\S+) toolCallId=(\S+)/);
|
|
222
|
+
if (endMatch) {
|
|
223
|
+
toolEvent = {
|
|
224
|
+
runId: endMatch[1],
|
|
225
|
+
tool: endMatch[2],
|
|
226
|
+
toolCallId: endMatch[3],
|
|
227
|
+
phase: "end",
|
|
228
|
+
timestamp,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
// Fallback: detect agent run start/done events (newer ClawdBot versions)
|
|
232
|
+
if (!toolEvent) {
|
|
233
|
+
const runStartMatch = message.match(/embedded run start: runId=(\S+)/);
|
|
234
|
+
if (runStartMatch) {
|
|
235
|
+
toolEvent = {
|
|
236
|
+
runId: runStartMatch[1],
|
|
237
|
+
tool: "agent-run",
|
|
238
|
+
toolCallId: `run-${runStartMatch[1]}`,
|
|
239
|
+
phase: "start",
|
|
240
|
+
timestamp,
|
|
241
|
+
args: this.extractToolArgs(json),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const runDoneMatch = message.match(/embedded run done: runId=(\S+)/);
|
|
245
|
+
if (runDoneMatch) {
|
|
246
|
+
toolEvent = {
|
|
247
|
+
runId: runDoneMatch[1],
|
|
248
|
+
tool: "agent-run",
|
|
249
|
+
toolCallId: `run-${runDoneMatch[1]}`,
|
|
250
|
+
phase: "end",
|
|
251
|
+
timestamp,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Check web-auto-reply for response content (contains actual command output)
|
|
257
|
+
if (!toolEvent && message && typeof json["1"] === "object") {
|
|
258
|
+
const data = json["1"];
|
|
259
|
+
if (data.text && typeof data.text === "string") {
|
|
260
|
+
const text = data.text;
|
|
261
|
+
// Check for shell output patterns (code blocks with command output)
|
|
262
|
+
if (text.includes("```") && (text.includes("stanchat") || text.includes("drwx") || text.includes("total "))) {
|
|
263
|
+
toolEvent = {
|
|
264
|
+
runId: `reply-${Date.now()}`,
|
|
265
|
+
tool: "exec",
|
|
266
|
+
toolCallId: `exec-${Date.now()}`,
|
|
267
|
+
phase: "start",
|
|
268
|
+
timestamp,
|
|
269
|
+
args: { output: text, command: "shell command" },
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (!toolEvent && message) {
|
|
275
|
+
const altStartMatch = message.match(/tool_call.*?name["\s:]+([^"\s,]+).*?id["\s:]+([^"\s,]+)/i);
|
|
276
|
+
if (altStartMatch) {
|
|
277
|
+
toolEvent = {
|
|
278
|
+
runId: `alt-${Date.now()}`,
|
|
279
|
+
tool: altStartMatch[1],
|
|
280
|
+
toolCallId: altStartMatch[2],
|
|
281
|
+
phase: "start",
|
|
282
|
+
timestamp,
|
|
283
|
+
args: this.extractToolArgs(json),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return { subsystem, message, timestamp, logLevel, toolEvent };
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
extractToolArgs(json) {
|
|
294
|
+
const args = {};
|
|
295
|
+
for (const key of Object.keys(json)) {
|
|
296
|
+
if (key === "_meta")
|
|
297
|
+
continue;
|
|
298
|
+
const val = json[key];
|
|
299
|
+
if (typeof val === "string") {
|
|
300
|
+
try {
|
|
301
|
+
const parsed = JSON.parse(val);
|
|
302
|
+
if (parsed && typeof parsed === "object") {
|
|
303
|
+
if (parsed.args || parsed.arguments || parsed.input || parsed.command) {
|
|
304
|
+
Object.assign(args, parsed.args || parsed.arguments || parsed.input || { command: parsed.command });
|
|
305
|
+
}
|
|
306
|
+
if (parsed.tool_input) {
|
|
307
|
+
Object.assign(args, typeof parsed.tool_input === "string" ? { raw: parsed.tool_input } : parsed.tool_input);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
catch { }
|
|
312
|
+
if (val.includes("rm ") || val.includes("curl ") || val.includes("wget ") ||
|
|
313
|
+
val.includes("chmod ") || val.includes("sudo ") || val.includes("dd ")) {
|
|
314
|
+
args.command = val;
|
|
315
|
+
}
|
|
316
|
+
if (/ignore\s+(previous|prior)/i.test(val) || /jailbreak/i.test(val) ||
|
|
317
|
+
/you\s+are\s+now/i.test(val) || /bypass/i.test(val)) {
|
|
318
|
+
args.suspiciousText = val;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (val && typeof val === "object") {
|
|
322
|
+
const obj = val;
|
|
323
|
+
if (obj.command)
|
|
324
|
+
args.command = obj.command;
|
|
325
|
+
if (obj.input)
|
|
326
|
+
args.input = obj.input;
|
|
327
|
+
if (obj.args)
|
|
328
|
+
Object.assign(args, obj.args);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return args;
|
|
332
|
+
}
|
|
333
|
+
handleToolEvent(event) {
|
|
334
|
+
if (event.phase === "start") {
|
|
335
|
+
this.activeRuns.set(event.toolCallId, event);
|
|
336
|
+
const dangerAnalysis = this.analyzeDanger(event);
|
|
337
|
+
const injectionAnalysis = this.analyzeInjection(event);
|
|
338
|
+
const actionEvent = {
|
|
339
|
+
ts: event.timestamp.toISOString(),
|
|
340
|
+
tool: event.tool,
|
|
341
|
+
decision: dangerAnalysis.dangerous || injectionAnalysis.detected ? "blocked" : "approved",
|
|
342
|
+
reason: dangerAnalysis.reason || injectionAnalysis.reason || `Tool executed: ${event.tool}`,
|
|
343
|
+
egress: [],
|
|
344
|
+
};
|
|
345
|
+
(0, storage_1.appendEvent)(actionEvent);
|
|
346
|
+
this.emit("toolEvent", { ...event, analysis: { danger: dangerAnalysis, injection: injectionAnalysis }, actionEvent });
|
|
347
|
+
console.log(`🛡️ [LogWatcher] Tool: ${event.tool} | ${dangerAnalysis.dangerous ? "⚠️ DANGER" : injectionAnalysis.detected ? "🔴 INJECTION" : "✓ OK"}`);
|
|
348
|
+
}
|
|
349
|
+
else if (event.phase === "end") {
|
|
350
|
+
const startEvent = this.activeRuns.get(event.toolCallId);
|
|
351
|
+
if (startEvent) {
|
|
352
|
+
const duration = event.timestamp.getTime() - startEvent.timestamp.getTime();
|
|
353
|
+
this.emit("toolComplete", { ...event, duration });
|
|
354
|
+
this.activeRuns.delete(event.toolCallId);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
analyzeDanger(event) {
|
|
359
|
+
if (event.tool !== "exec") {
|
|
360
|
+
return { dangerous: false, reason: "" };
|
|
361
|
+
}
|
|
362
|
+
const argsStr = JSON.stringify(event.args || {}).toLowerCase();
|
|
363
|
+
for (const { pattern, reason } of DANGEROUS_PATTERNS) {
|
|
364
|
+
if (pattern.test(argsStr)) {
|
|
365
|
+
return { dangerous: true, reason };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return { dangerous: false, reason: "" };
|
|
369
|
+
}
|
|
370
|
+
analyzeInjection(event) {
|
|
371
|
+
const argsStr = JSON.stringify(event.args || {});
|
|
372
|
+
for (const { pattern, reason } of INJECTION_PATTERNS) {
|
|
373
|
+
if (pattern.test(argsStr)) {
|
|
374
|
+
return { detected: true, reason };
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return { detected: false, reason: "" };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
exports.LogWatcher = LogWatcher;
|
|
381
|
+
let watcher = null;
|
|
382
|
+
function startLogWatcher() {
|
|
383
|
+
if (!watcher) {
|
|
384
|
+
watcher = new LogWatcher();
|
|
385
|
+
watcher.start();
|
|
386
|
+
}
|
|
387
|
+
return watcher;
|
|
388
|
+
}
|
|
389
|
+
function stopLogWatcher() {
|
|
390
|
+
if (watcher) {
|
|
391
|
+
watcher.stop();
|
|
392
|
+
watcher = null;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function getLogWatcher() {
|
|
396
|
+
return watcher;
|
|
397
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ActionEvent } from "./storage";
|
|
2
|
+
export interface LiteMetrics {
|
|
3
|
+
window: string;
|
|
4
|
+
kpis: {
|
|
5
|
+
approved: number;
|
|
6
|
+
blocked: number;
|
|
7
|
+
manualApproved: number;
|
|
8
|
+
highRiskIntercepts: number;
|
|
9
|
+
promptInjectionDetected: number;
|
|
10
|
+
};
|
|
11
|
+
egressTop: {
|
|
12
|
+
host: string;
|
|
13
|
+
calls: number;
|
|
14
|
+
}[];
|
|
15
|
+
skills: {
|
|
16
|
+
trusted: number;
|
|
17
|
+
unknownBlocked: number;
|
|
18
|
+
outdated: number;
|
|
19
|
+
};
|
|
20
|
+
instance: {
|
|
21
|
+
reverseProxyHardened: boolean;
|
|
22
|
+
dashboardLocalOnly: boolean;
|
|
23
|
+
secretsEnvOnly: boolean;
|
|
24
|
+
};
|
|
25
|
+
timeline: {
|
|
26
|
+
hours: string[];
|
|
27
|
+
approved: number[];
|
|
28
|
+
blocked: number[];
|
|
29
|
+
};
|
|
30
|
+
cost: {
|
|
31
|
+
inputTokens: number[];
|
|
32
|
+
outputTokens: number[];
|
|
33
|
+
estimatedUSD: number[];
|
|
34
|
+
};
|
|
35
|
+
lastActions: ActionEvent[];
|
|
36
|
+
config: {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
showMode: boolean;
|
|
39
|
+
showModeActionsRemaining: number;
|
|
40
|
+
protectionLevel: string;
|
|
41
|
+
} | null;
|
|
42
|
+
pendingApprovals: {
|
|
43
|
+
id: string;
|
|
44
|
+
tool: string;
|
|
45
|
+
reason: string;
|
|
46
|
+
createdAt: string;
|
|
47
|
+
}[];
|
|
48
|
+
}
|
|
49
|
+
declare const session: Record<string, number>;
|
|
50
|
+
export declare function incrementMetric(k: keyof typeof session): void;
|
|
51
|
+
export declare function getMetrics(hoursBack?: number): LiteMetrics;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,WAAW,EAAkC,MAAM,WAAW,CAAC;AAElG,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAC;QAAC,uBAAuB,EAAE,MAAM,CAAA;KAAE,CAAC;IACjI,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,EAAE;QAAE,oBAAoB,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IAClG,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACrE,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAChF,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,wBAAwB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClH,gBAAgB,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrF;AAED,QAAA,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAqG,CAAC;AAC1I,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,OAAO,OAAO,QAAmB;AAE1E,wBAAgB,UAAU,CAAC,SAAS,SAAK,GAAG,WAAW,CA0CtD"}
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.incrementMetric = incrementMetric;
|
|
4
|
+
exports.getMetrics = getMetrics;
|
|
5
|
+
const storage_1 = require("./storage");
|
|
6
|
+
const session = { approved: 0, blocked: 0, manualApproved: 0, highRiskIntercepts: 0, promptInjectionDetected: 0 };
|
|
7
|
+
function incrementMetric(k) { session[k]++; }
|
|
8
|
+
function getMetrics(hoursBack = 24) {
|
|
9
|
+
const events = (0, storage_1.getEvents)(hoursBack);
|
|
10
|
+
const kpis = { approved: 0, blocked: 0, manualApproved: 0, highRiskIntercepts: 0, promptInjectionDetected: 0 };
|
|
11
|
+
const egressCounts = {};
|
|
12
|
+
const trusted = new Set(), unknownBlocked = new Set();
|
|
13
|
+
for (const e of events) {
|
|
14
|
+
if (e.decision === "approved")
|
|
15
|
+
kpis.approved++;
|
|
16
|
+
else if (e.decision === "blocked")
|
|
17
|
+
kpis.blocked++;
|
|
18
|
+
else if (e.decision === "manual") {
|
|
19
|
+
kpis.manualApproved++;
|
|
20
|
+
kpis.approved++;
|
|
21
|
+
}
|
|
22
|
+
if (e.reason.includes("highRisk"))
|
|
23
|
+
kpis.highRiskIntercepts++;
|
|
24
|
+
if (e.reason.includes("promptInjection"))
|
|
25
|
+
kpis.promptInjectionDetected++;
|
|
26
|
+
if (e.decision !== "blocked")
|
|
27
|
+
trusted.add(e.tool);
|
|
28
|
+
if (e.decision === "blocked" && e.reason === "unknownSkill")
|
|
29
|
+
unknownBlocked.add(e.tool);
|
|
30
|
+
for (const h of e.egress || [])
|
|
31
|
+
egressCounts[h] = (egressCounts[h] || 0) + 1;
|
|
32
|
+
}
|
|
33
|
+
const hours = [], approved = [], blocked = [];
|
|
34
|
+
const inputTokens = [], outputTokens = [], estimatedUSD = [];
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
for (let i = 23; i >= 0; i--) {
|
|
37
|
+
const start = new Date(now - i * 3600000);
|
|
38
|
+
start.setMinutes(0, 0, 0);
|
|
39
|
+
const end = new Date(start.getTime() + 3600000);
|
|
40
|
+
hours.push(start.toISOString().slice(11, 16));
|
|
41
|
+
const hourEvents = events.filter(e => { const t = new Date(e.ts); return t >= start && t < end; });
|
|
42
|
+
approved.push(hourEvents.filter(e => e.decision !== "blocked").length);
|
|
43
|
+
blocked.push(hourEvents.filter(e => e.decision === "blocked").length);
|
|
44
|
+
inputTokens.push(hourEvents.reduce((s, e) => s + (e.inputTokens || 0), 0));
|
|
45
|
+
outputTokens.push(hourEvents.reduce((s, e) => s + (e.outputTokens || 0), 0));
|
|
46
|
+
estimatedUSD.push(hourEvents.reduce((s, e) => s + (e.estimatedUSD || 0), 0));
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
window: `${hoursBack}h`, kpis,
|
|
50
|
+
egressTop: Object.entries(egressCounts).map(([host, calls]) => ({ host, calls })).sort((a, b) => b.calls - a.calls).slice(0, 5),
|
|
51
|
+
skills: { trusted: trusted.size, unknownBlocked: unknownBlocked.size, outdated: 0 },
|
|
52
|
+
instance: { reverseProxyHardened: true, dashboardLocalOnly: true, secretsEnvOnly: !!process.env.LITE_ADAPTER_SECRET },
|
|
53
|
+
timeline: { hours, approved, blocked }, cost: { inputTokens, outputTokens, estimatedUSD },
|
|
54
|
+
lastActions: (0, storage_1.getLastEvents)(10),
|
|
55
|
+
config: (0, storage_1.getConfig)(),
|
|
56
|
+
pendingApprovals: (0, storage_1.getPendingApprovals)()
|
|
57
|
+
};
|
|
58
|
+
}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface LitePolicy {
|
|
2
|
+
allowedSkills: string[];
|
|
3
|
+
highRiskActions: string[];
|
|
4
|
+
blockUnknownSkills: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function loadPolicy(policyPath?: string): LitePolicy;
|
|
7
|
+
export declare function classifyAction(skill: string): {
|
|
8
|
+
decision: "approved" | "blocked" | "requires_confirmation";
|
|
9
|
+
reason: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../policy.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAID,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,CAS1D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,uBAAuB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAM5H"}
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadPolicy = loadPolicy;
|
|
37
|
+
exports.classifyAction = classifyAction;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
let policy = null;
|
|
41
|
+
function loadPolicy(policyPath) {
|
|
42
|
+
if (policy)
|
|
43
|
+
return policy;
|
|
44
|
+
const filePath = policyPath || process.env.LITE_POLICY_PATH || path.join(__dirname, "lite-policy.json");
|
|
45
|
+
try {
|
|
46
|
+
policy = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
policy = { allowedSkills: ["email.send", "calendar.create", "web.get", "file.read"], highRiskActions: ["shell.exec", "file.write", "network.post", "delete.*", "wallet.tx"], blockUnknownSkills: true };
|
|
50
|
+
}
|
|
51
|
+
return policy;
|
|
52
|
+
}
|
|
53
|
+
function classifyAction(skill) {
|
|
54
|
+
const p = loadPolicy();
|
|
55
|
+
if (p.allowedSkills.includes(skill))
|
|
56
|
+
return { decision: "approved", reason: "allowlist" };
|
|
57
|
+
if (p.highRiskActions.some(pat => pat.includes("*") ? new RegExp("^" + pat.replace(/\*/g, ".*") + "$").test(skill) : pat === skill))
|
|
58
|
+
return { decision: "requires_confirmation", reason: "highRisk" };
|
|
59
|
+
return p.blockUnknownSkills ? { decision: "blocked", reason: "unknownSkill" } : { decision: "approved", reason: "defaultAllow" };
|
|
60
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../server.ts"],"names":[],"mappings":"AAsCA,wBAAsB,WAAW,CAAC,IAAI,SAAO,EAAE,IAAI,SAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAwI9E;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBhD"}
|