@clawnitor/plugin 1.0.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.
Files changed (62) hide show
  1. package/README.md +60 -0
  2. package/dist/config.d.ts +10 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +16 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/event-builder.d.ts +15 -0
  7. package/dist/event-builder.d.ts.map +1 -0
  8. package/dist/event-builder.js +34 -0
  9. package/dist/event-builder.js.map +1 -0
  10. package/dist/hooks/lifecycle.d.ts +18 -0
  11. package/dist/hooks/lifecycle.d.ts.map +1 -0
  12. package/dist/hooks/lifecycle.js +79 -0
  13. package/dist/hooks/lifecycle.js.map +1 -0
  14. package/dist/hooks/llm.d.ts +11 -0
  15. package/dist/hooks/llm.d.ts.map +1 -0
  16. package/dist/hooks/llm.js +35 -0
  17. package/dist/hooks/llm.js.map +1 -0
  18. package/dist/hooks/message.d.ts +14 -0
  19. package/dist/hooks/message.d.ts.map +1 -0
  20. package/dist/hooks/message.js +52 -0
  21. package/dist/hooks/message.js.map +1 -0
  22. package/dist/hooks/tool-call.d.ts +20 -0
  23. package/dist/hooks/tool-call.d.ts.map +1 -0
  24. package/dist/hooks/tool-call.js +89 -0
  25. package/dist/hooks/tool-call.js.map +1 -0
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +105 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/kill-switch/kill-state.d.ts +16 -0
  31. package/dist/kill-switch/kill-state.d.ts.map +1 -0
  32. package/dist/kill-switch/kill-state.js +25 -0
  33. package/dist/kill-switch/kill-state.js.map +1 -0
  34. package/dist/kill-switch/local-failsafe.d.ts +19 -0
  35. package/dist/kill-switch/local-failsafe.d.ts.map +1 -0
  36. package/dist/kill-switch/local-failsafe.js +64 -0
  37. package/dist/kill-switch/local-failsafe.js.map +1 -0
  38. package/dist/redaction.d.ts +3 -0
  39. package/dist/redaction.d.ts.map +1 -0
  40. package/dist/redaction.js +46 -0
  41. package/dist/redaction.js.map +1 -0
  42. package/dist/rule-cache.d.ts +42 -0
  43. package/dist/rule-cache.d.ts.map +1 -0
  44. package/dist/rule-cache.js +138 -0
  45. package/dist/rule-cache.js.map +1 -0
  46. package/dist/severity.d.ts +8 -0
  47. package/dist/severity.d.ts.map +1 -0
  48. package/dist/severity.js +38 -0
  49. package/dist/severity.js.map +1 -0
  50. package/dist/transport/https-sender.d.ts +18 -0
  51. package/dist/transport/https-sender.d.ts.map +1 -0
  52. package/dist/transport/https-sender.js +62 -0
  53. package/dist/transport/https-sender.js.map +1 -0
  54. package/dist/transport/sqlite-cache.d.ts +12 -0
  55. package/dist/transport/sqlite-cache.d.ts.map +1 -0
  56. package/dist/transport/sqlite-cache.js +74 -0
  57. package/dist/transport/sqlite-cache.js.map +1 -0
  58. package/dist/transport/websocket-client.d.ts +19 -0
  59. package/dist/transport/websocket-client.d.ts.map +1 -0
  60. package/dist/transport/websocket-client.js +79 -0
  61. package/dist/transport/websocket-client.js.map +1 -0
  62. package/package.json +51 -0
@@ -0,0 +1,16 @@
1
+ export interface KillStateData {
2
+ killed: boolean;
3
+ reason?: string;
4
+ killedAt?: Date;
5
+ }
6
+ declare class KillState {
7
+ private state;
8
+ isKilled(): boolean;
9
+ getReason(): string | undefined;
10
+ setKilled(reason: string): void;
11
+ clearKilled(): void;
12
+ getState(): Readonly<KillStateData>;
13
+ }
14
+ export declare const killState: KillState;
15
+ export {};
16
+ //# sourceMappingURL=kill-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill-state.d.ts","sourceRoot":"","sources":["../../src/kill-switch/kill-state.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,IAAI,CAAC;CACjB;AAED,cAAM,SAAS;IACb,OAAO,CAAC,KAAK,CAAoC;IAEjD,QAAQ,IAAI,OAAO;IAInB,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B,SAAS,CAAC,MAAM,EAAE,MAAM;IAQxB,WAAW;IAIX,QAAQ,IAAI,QAAQ,CAAC,aAAa,CAAC;CAGpC;AAGD,eAAO,MAAM,SAAS,WAAkB,CAAC"}
@@ -0,0 +1,25 @@
1
+ class KillState {
2
+ state = { killed: false };
3
+ isKilled() {
4
+ return this.state.killed;
5
+ }
6
+ getReason() {
7
+ return this.state.reason;
8
+ }
9
+ setKilled(reason) {
10
+ this.state = {
11
+ killed: true,
12
+ reason,
13
+ killedAt: new Date(),
14
+ };
15
+ }
16
+ clearKilled() {
17
+ this.state = { killed: false };
18
+ }
19
+ getState() {
20
+ return { ...this.state };
21
+ }
22
+ }
23
+ // Singleton
24
+ export const killState = new KillState();
25
+ //# sourceMappingURL=kill-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill-state.js","sourceRoot":"","sources":["../../src/kill-switch/kill-state.ts"],"names":[],"mappings":"AAMA,MAAM,SAAS;IACL,KAAK,GAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAEjD,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,SAAS,CAAC,MAAc;QACtB,IAAI,CAAC,KAAK,GAAG;YACX,MAAM,EAAE,IAAI;YACZ,MAAM;YACN,QAAQ,EAAE,IAAI,IAAI,EAAE;SACrB,CAAC;IACJ,CAAC;IAED,WAAW;QACT,IAAI,CAAC,KAAK,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACjC,CAAC;IAED,QAAQ;QACN,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;CACF;AAED,YAAY;AACZ,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { PluginConfig } from "../config.js";
2
+ interface FailsafeResult {
3
+ block: boolean;
4
+ reason: string;
5
+ }
6
+ export declare class LocalFailsafe {
7
+ private sessionSpend;
8
+ private rateCounter;
9
+ private config;
10
+ constructor(config: PluginConfig);
11
+ addSpend(costUsd: number): void;
12
+ recordToolCall(): void;
13
+ resetSession(): void;
14
+ check(toolName: string): FailsafeResult;
15
+ getSessionSpend(): number;
16
+ getCallsPerMinute(): number;
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=local-failsafe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-failsafe.d.ts","sourceRoot":"","sources":["../../src/kill-switch/local-failsafe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,UAAU,cAAc;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAkBD,qBAAa,aAAa;IACxB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,EAAE,YAAY;IAIhC,QAAQ,CAAC,OAAO,EAAE,MAAM;IAIxB,cAAc;IAId,YAAY;IAIZ,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc;IAiCvC,eAAe,IAAI,MAAM;IAIzB,iBAAiB,IAAI,MAAM;CAG5B"}
@@ -0,0 +1,64 @@
1
+ // Sliding window rate counter
2
+ class RateCounter {
3
+ timestamps = [];
4
+ record() {
5
+ this.timestamps.push(Date.now());
6
+ }
7
+ getCallsPerMinute() {
8
+ const now = Date.now();
9
+ const oneMinuteAgo = now - 60_000;
10
+ this.timestamps = this.timestamps.filter((t) => t > oneMinuteAgo);
11
+ return this.timestamps.length;
12
+ }
13
+ }
14
+ export class LocalFailsafe {
15
+ sessionSpend = 0;
16
+ rateCounter = new RateCounter();
17
+ config;
18
+ constructor(config) {
19
+ this.config = config;
20
+ }
21
+ addSpend(costUsd) {
22
+ this.sessionSpend += costUsd;
23
+ }
24
+ recordToolCall() {
25
+ this.rateCounter.record();
26
+ }
27
+ resetSession() {
28
+ this.sessionSpend = 0;
29
+ }
30
+ check(toolName) {
31
+ // 1. Spend circuit breaker
32
+ if (this.sessionSpend >= this.config.spendLimit) {
33
+ return {
34
+ block: true,
35
+ reason: `Clawnitor: Spend limit reached ($${this.sessionSpend.toFixed(2)} >= $${this.config.spendLimit}). Agent paused.`,
36
+ };
37
+ }
38
+ // 2. Rate limiter
39
+ const callsPerMin = this.rateCounter.getCallsPerMinute();
40
+ if (callsPerMin >= this.config.rateLimit) {
41
+ return {
42
+ block: true,
43
+ reason: `Clawnitor: Rate limit reached (${callsPerMin} calls/min >= ${this.config.rateLimit}). Agent paused.`,
44
+ };
45
+ }
46
+ // 3. Tool blocklist
47
+ const normalizedTool = toolName.toLowerCase();
48
+ const blocked = this.config.toolBlocklist.some((t) => t.toLowerCase() === normalizedTool);
49
+ if (blocked) {
50
+ return {
51
+ block: true,
52
+ reason: `Clawnitor: Tool "${toolName}" is blocklisted. Agent paused.`,
53
+ };
54
+ }
55
+ return { block: false, reason: "" };
56
+ }
57
+ getSessionSpend() {
58
+ return this.sessionSpend;
59
+ }
60
+ getCallsPerMinute() {
61
+ return this.rateCounter.getCallsPerMinute();
62
+ }
63
+ }
64
+ //# sourceMappingURL=local-failsafe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-failsafe.js","sourceRoot":"","sources":["../../src/kill-switch/local-failsafe.ts"],"names":[],"mappings":"AAOA,8BAA8B;AAC9B,MAAM,WAAW;IACP,UAAU,GAAa,EAAE,CAAC;IAElC,MAAM;QACJ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,iBAAiB;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC;QAClC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;IAChC,CAAC;CACF;AAED,MAAM,OAAO,aAAa;IAChB,YAAY,GAAG,CAAC,CAAC;IACjB,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;IAChC,MAAM,CAAe;IAE7B,YAAY,MAAoB;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,QAAQ,CAAC,OAAe;QACtB,IAAI,CAAC,YAAY,IAAI,OAAO,CAAC;IAC/B,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED,YAAY;QACV,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,QAAgB;QACpB,2BAA2B;QAC3B,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAChD,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,oCAAoC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,UAAU,kBAAkB;aACzH,CAAC;QACJ,CAAC;QAED,kBAAkB;QAClB,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;QACzD,IAAI,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACzC,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,kCAAkC,WAAW,iBAAiB,IAAI,CAAC,MAAM,CAAC,SAAS,kBAAkB;aAC9G,CAAC;QACJ,CAAC;QAED,oBAAoB;QACpB,MAAM,cAAc,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAC5C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,cAAc,CAC1C,CAAC;QACF,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO;gBACL,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,oBAAoB,QAAQ,iCAAiC;aACtE,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACtC,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,iBAAiB;QACf,OAAO,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;IAC9C,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ export declare const DEFAULT_PATTERNS: string[];
2
+ export declare function redact(text: string, patterns: string[]): string;
3
+ //# sourceMappingURL=redaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,UA+B5B,CAAC;AAEF,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAW/D"}
@@ -0,0 +1,46 @@
1
+ export const DEFAULT_PATTERNS = [
2
+ // OpenAI
3
+ "sk-[a-zA-Z0-9]{20,}",
4
+ "sk-proj-[a-zA-Z0-9]{20,}",
5
+ // Anthropic
6
+ "sk-ant-[a-zA-Z0-9\\-]{20,}",
7
+ // Stripe
8
+ "sk_live_[a-zA-Z0-9]{20,}",
9
+ "sk_test_[a-zA-Z0-9]{20,}",
10
+ "pk_live_[a-zA-Z0-9]{20,}",
11
+ "pk_test_[a-zA-Z0-9]{20,}",
12
+ // Bearer tokens / JWTs
13
+ "Bearer\\s+[a-zA-Z0-9._\\-]{20,}",
14
+ "eyJ[a-zA-Z0-9_\\-]{10,}\\.[a-zA-Z0-9_\\-]{10,}\\.[a-zA-Z0-9_\\-]{10,}",
15
+ // AWS
16
+ "AKIA[0-9A-Z]{16}",
17
+ // GitHub
18
+ "ghp_[a-zA-Z0-9]{36}",
19
+ "gho_[a-zA-Z0-9]{36}",
20
+ "github_pat_[a-zA-Z0-9_]{20,}",
21
+ // Slack
22
+ "xoxb-[a-zA-Z0-9\\-]+",
23
+ "xoxp-[a-zA-Z0-9\\-]+",
24
+ // Private keys
25
+ "-----BEGIN\\s+(RSA\\s+)?PRIVATE\\s+KEY-----",
26
+ // Passwords in URLs/config
27
+ "password\\s*[=:]\\s*\\S+",
28
+ // Database connection strings with passwords
29
+ "(?:postgres|mysql|mongodb)://[^:]+:[^@]+@",
30
+ // Generic secret assignments
31
+ "(?:secret|token|api_key|apikey)\\s*[=:]\\s*\\S{8,}",
32
+ ];
33
+ export function redact(text, patterns) {
34
+ let result = text;
35
+ for (const pattern of patterns) {
36
+ try {
37
+ const regex = new RegExp(pattern, "gi");
38
+ result = result.replace(regex, "[REDACTED]");
39
+ }
40
+ catch {
41
+ // Skip invalid regex patterns
42
+ }
43
+ }
44
+ return result;
45
+ }
46
+ //# sourceMappingURL=redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,SAAS;IACT,qBAAqB;IACrB,0BAA0B;IAC1B,YAAY;IACZ,4BAA4B;IAC5B,SAAS;IACT,0BAA0B;IAC1B,0BAA0B;IAC1B,0BAA0B;IAC1B,0BAA0B;IAC1B,uBAAuB;IACvB,iCAAiC;IACjC,uEAAuE;IACvE,MAAM;IACN,kBAAkB;IAClB,SAAS;IACT,qBAAqB;IACrB,qBAAqB;IACrB,8BAA8B;IAC9B,QAAQ;IACR,sBAAsB;IACtB,sBAAsB;IACtB,eAAe;IACf,6CAA6C;IAC7C,2BAA2B;IAC3B,0BAA0B;IAC1B,6CAA6C;IAC7C,2CAA2C;IAC3C,6BAA6B;IAC7B,oDAAoD;CACrD,CAAC;AAEF,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,QAAkB;IACrD,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACxC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { PluginConfig } from "./config.js";
2
+ export interface CachedRule {
3
+ id: string;
4
+ name: string;
5
+ rule_type: string;
6
+ config: any;
7
+ enabled: boolean;
8
+ }
9
+ interface RuleCheckResult {
10
+ blocked: boolean;
11
+ ruleName?: string;
12
+ reason?: string;
13
+ }
14
+ interface EventRecord {
15
+ timestamp: number;
16
+ toolName?: string;
17
+ eventType?: string;
18
+ costUsd?: number;
19
+ action?: string;
20
+ target?: string;
21
+ rawSnippet?: string;
22
+ }
23
+ export declare class RuleCache {
24
+ private rules;
25
+ private config;
26
+ private fetchTimer;
27
+ private recentEvents;
28
+ private readonly MAX_RECENT_EVENTS;
29
+ constructor(config: PluginConfig);
30
+ start(): void;
31
+ stop(): void;
32
+ private fetchRules;
33
+ recordEvent(event: EventRecord): void;
34
+ checkBeforeToolCall(toolName: string, params?: Record<string, unknown>): RuleCheckResult;
35
+ private evaluateRule;
36
+ private evaluateKeyword;
37
+ private evaluateRate;
38
+ private evaluateThreshold;
39
+ getRuleCount(): number;
40
+ }
41
+ export {};
42
+ //# sourceMappingURL=rule-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-cache.d.ts","sourceRoot":"","sources":["../src/rule-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAO;gBAE7B,MAAM,EAAE,YAAY;IAIhC,KAAK;IAML,IAAI;YAOU,UAAU;IAiBxB,WAAW,CAAC,KAAK,EAAE,WAAW;IAS9B,mBAAmB,CACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,eAAe;IAelB,OAAO,CAAC,YAAY;IAsBpB,OAAO,CAAC,eAAe;IAgCvB,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,iBAAiB;IA8BzB,YAAY,IAAI,MAAM;CAGvB"}
@@ -0,0 +1,138 @@
1
+ export class RuleCache {
2
+ rules = [];
3
+ config;
4
+ fetchTimer = null;
5
+ recentEvents = [];
6
+ MAX_RECENT_EVENTS = 500;
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+ start() {
11
+ // Fetch immediately, then every 60s
12
+ this.fetchRules();
13
+ this.fetchTimer = setInterval(() => this.fetchRules(), 60_000);
14
+ }
15
+ stop() {
16
+ if (this.fetchTimer) {
17
+ clearInterval(this.fetchTimer);
18
+ this.fetchTimer = null;
19
+ }
20
+ }
21
+ async fetchRules() {
22
+ try {
23
+ const res = await fetch(`${this.config.backendUrl}/api/rules`, {
24
+ headers: {
25
+ Authorization: `Bearer ${this.config.apiKey}`,
26
+ },
27
+ signal: AbortSignal.timeout(5000),
28
+ });
29
+ if (res.ok) {
30
+ const data = await res.json();
31
+ this.rules = (data.rules || []).filter((r) => r.enabled);
32
+ }
33
+ }
34
+ catch {
35
+ // Keep existing cached rules on fetch failure
36
+ }
37
+ }
38
+ recordEvent(event) {
39
+ this.recentEvents.push(event);
40
+ // Trim to max size
41
+ if (this.recentEvents.length > this.MAX_RECENT_EVENTS) {
42
+ this.recentEvents = this.recentEvents.slice(-this.MAX_RECENT_EVENTS);
43
+ }
44
+ }
45
+ // Evaluate all cached pattern rules against the current tool call + recent history
46
+ checkBeforeToolCall(toolName, params) {
47
+ const paramsStr = params ? JSON.stringify(params).slice(0, 500) : "";
48
+ for (const rule of this.rules) {
49
+ if (rule.rule_type === "nl")
50
+ continue; // Can't evaluate locally
51
+ const result = this.evaluateRule(rule, toolName, paramsStr);
52
+ if (result.blocked) {
53
+ return result;
54
+ }
55
+ }
56
+ return { blocked: false };
57
+ }
58
+ evaluateRule(rule, toolName, paramsStr) {
59
+ const config = rule.config;
60
+ if (rule.rule_type === "keyword") {
61
+ return this.evaluateKeyword(rule, toolName, paramsStr);
62
+ }
63
+ if (rule.rule_type === "rate") {
64
+ return this.evaluateRate(rule);
65
+ }
66
+ if (rule.rule_type === "threshold") {
67
+ return this.evaluateThreshold(rule);
68
+ }
69
+ return { blocked: false };
70
+ }
71
+ evaluateKeyword(rule, toolName, paramsStr) {
72
+ const config = rule.config;
73
+ const keywords = config.keywords || [];
74
+ const caseSensitive = config.caseSensitive || false;
75
+ const matchMode = config.matchMode || "any";
76
+ const searchable = [toolName, paramsStr].join(" ");
77
+ const text = caseSensitive ? searchable : searchable.toLowerCase();
78
+ const matched = keywords.filter((kw) => {
79
+ const k = caseSensitive ? kw : kw.toLowerCase();
80
+ return text.includes(k);
81
+ });
82
+ const triggered = matchMode === "any" ? matched.length > 0 : matched.length === keywords.length;
83
+ if (triggered) {
84
+ return {
85
+ blocked: true,
86
+ ruleName: rule.name,
87
+ reason: `Clawnitor: Rule "${rule.name}" blocked — keyword match: ${matched.join(", ")}`,
88
+ };
89
+ }
90
+ return { blocked: false };
91
+ }
92
+ evaluateRate(rule) {
93
+ const config = rule.config;
94
+ const windowMs = (config.windowMinutes || 10) * 60 * 1000;
95
+ const maxCount = config.maxCount || 100;
96
+ const now = Date.now();
97
+ let filtered = this.recentEvents.filter((e) => e.timestamp > now - windowMs);
98
+ if (config.eventType) {
99
+ filtered = filtered.filter((e) => e.eventType === config.eventType);
100
+ }
101
+ if (config.toolName) {
102
+ filtered = filtered.filter((e) => e.toolName === config.toolName);
103
+ }
104
+ if (filtered.length >= maxCount) {
105
+ return {
106
+ blocked: true,
107
+ ruleName: rule.name,
108
+ reason: `Clawnitor: Rule "${rule.name}" blocked — ${filtered.length} events in ${config.windowMinutes}min (max: ${maxCount})`,
109
+ };
110
+ }
111
+ return { blocked: false };
112
+ }
113
+ evaluateThreshold(rule) {
114
+ const config = rule.config;
115
+ const windowMs = (config.windowMinutes || 60) * 60 * 1000;
116
+ const now = Date.now();
117
+ const recentInWindow = this.recentEvents.filter((e) => e.timestamp > now - windowMs);
118
+ let sum = 0;
119
+ for (const event of recentInWindow) {
120
+ if (config.field === "cost_usd" && event.costUsd) {
121
+ sum += event.costUsd;
122
+ }
123
+ }
124
+ const triggered = config.operator === "gt" ? sum > config.value : sum < config.value;
125
+ if (triggered) {
126
+ return {
127
+ blocked: true,
128
+ ruleName: rule.name,
129
+ reason: `Clawnitor: Rule "${rule.name}" blocked — ${config.field} = ${sum.toFixed(4)} (${config.operator} ${config.value})`,
130
+ };
131
+ }
132
+ return { blocked: false };
133
+ }
134
+ getRuleCount() {
135
+ return this.rules.length;
136
+ }
137
+ }
138
+ //# sourceMappingURL=rule-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-cache.js","sourceRoot":"","sources":["../src/rule-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { EventType, SeverityHint } from "@clawnitor/shared";
2
+ export interface RateTracker {
3
+ getRate(): number;
4
+ record(): void;
5
+ }
6
+ export declare function createRateTracker(): RateTracker;
7
+ export declare function assignSeverity(eventType: EventType, metadata: Record<string, unknown>, rateTracker?: RateTracker): SeverityHint;
8
+ //# sourceMappingURL=severity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"severity.d.ts","sourceRoot":"","sources":["../src/severity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,WAAW,WAAW;IAC1B,OAAO,IAAI,MAAM,CAAC;IAClB,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAkB/C;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,CAAC,EAAE,WAAW,GACxB,YAAY,CAqBd"}
@@ -0,0 +1,38 @@
1
+ export function createRateTracker() {
2
+ const window = []; // timestamps
3
+ const WINDOW_MS = 5 * 60 * 1000; // 5 minutes
4
+ return {
5
+ getRate() {
6
+ const now = Date.now();
7
+ // Remove entries outside window
8
+ while (window.length > 0 && window[0] < now - WINDOW_MS) {
9
+ window.shift();
10
+ }
11
+ // Convert to per-minute rate
12
+ return window.length / 5;
13
+ },
14
+ record() {
15
+ window.push(Date.now());
16
+ },
17
+ };
18
+ }
19
+ export function assignSeverity(eventType, metadata, rateTracker) {
20
+ // Critical: error in metadata
21
+ if (metadata.error) {
22
+ return "elevated";
23
+ }
24
+ // Critical: high spend
25
+ if (typeof metadata.cost_usd === "number" && metadata.cost_usd > 1) {
26
+ return "elevated";
27
+ }
28
+ // Rate-based: flag elevated if tool call rate exceeds 30/min
29
+ // (normal agents do 5-15/min, 30+ suggests a loop or runaway)
30
+ if (rateTracker && eventType === "tool_use") {
31
+ const rate = rateTracker.getRate();
32
+ if (rate > 30) {
33
+ return "elevated";
34
+ }
35
+ }
36
+ return "normal";
37
+ }
38
+ //# sourceMappingURL=severity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"severity.js","sourceRoot":"","sources":["../src/severity.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,iBAAiB;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC,CAAC,aAAa;IAC1C,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;IAE7C,OAAO;QACL,OAAO;YACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,gCAAgC;YAChC,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,SAAS,EAAE,CAAC;gBACxD,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;YACD,6BAA6B;YAC7B,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM;YACJ,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,SAAoB,EACpB,QAAiC,EACjC,WAAyB;IAEzB,8BAA8B;IAC9B,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,uBAAuB;IACvB,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QACnE,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,6DAA6D;IAC7D,8DAA8D;IAC9D,IAAI,WAAW,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC;YACd,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { ClawnitorEvent } from "@clawnitor/shared";
2
+ import type { PluginConfig } from "../config.js";
3
+ export declare class HttpsSender {
4
+ private buffer;
5
+ private timer;
6
+ private config;
7
+ private onKillState?;
8
+ private onFlushFail?;
9
+ constructor(config: PluginConfig, opts?: {
10
+ onKillState?: (killed: boolean, reason?: string) => void;
11
+ onFlushFail?: (events: ClawnitorEvent[]) => void;
12
+ });
13
+ start(): void;
14
+ stop(): void;
15
+ enqueue(event: ClawnitorEvent): void;
16
+ private flush;
17
+ }
18
+ //# sourceMappingURL=https-sender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https-sender.d.ts","sourceRoot":"","sources":["../../src/transport/https-sender.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,mBAAmB,CAAC;AAE9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,WAAW,CAAC,CAA6C;IACjE,OAAO,CAAC,WAAW,CAAC,CAAqC;gBAGvD,MAAM,EAAE,YAAY,EACpB,IAAI,CAAC,EAAE;QACL,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;QACzD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;KAClD;IAOH,KAAK;IAIL,IAAI;IASJ,OAAO,CAAC,KAAK,EAAE,cAAc;YAOf,KAAK;CA+BpB"}
@@ -0,0 +1,62 @@
1
+ import { EVENT_BATCH_SIZE, EVENT_BATCH_INTERVAL_MS } from "@clawnitor/shared";
2
+ export class HttpsSender {
3
+ buffer = [];
4
+ timer = null;
5
+ config;
6
+ onKillState;
7
+ onFlushFail;
8
+ constructor(config, opts) {
9
+ this.config = config;
10
+ this.onKillState = opts?.onKillState;
11
+ this.onFlushFail = opts?.onFlushFail;
12
+ }
13
+ start() {
14
+ this.timer = setInterval(() => this.flush(), EVENT_BATCH_INTERVAL_MS);
15
+ }
16
+ stop() {
17
+ if (this.timer) {
18
+ clearInterval(this.timer);
19
+ this.timer = null;
20
+ }
21
+ // Final flush
22
+ this.flush();
23
+ }
24
+ enqueue(event) {
25
+ this.buffer.push(event);
26
+ if (this.buffer.length >= EVENT_BATCH_SIZE) {
27
+ this.flush();
28
+ }
29
+ }
30
+ async flush() {
31
+ if (this.buffer.length === 0)
32
+ return;
33
+ const batch = this.buffer.splice(0, EVENT_BATCH_SIZE);
34
+ try {
35
+ const response = await fetch(`${this.config.backendUrl}/api/events`, {
36
+ method: "POST",
37
+ headers: {
38
+ "Content-Type": "application/json",
39
+ Authorization: `Bearer ${this.config.apiKey}`,
40
+ },
41
+ body: JSON.stringify({ events: batch }),
42
+ signal: AbortSignal.timeout(10_000),
43
+ });
44
+ if (response.ok) {
45
+ const data = (await response.json());
46
+ if (this.onKillState) {
47
+ this.onKillState(data.kill_state.killed, data.kill_state.reason);
48
+ }
49
+ }
50
+ else if (response.status >= 500) {
51
+ // Server error — cache for retry
52
+ this.onFlushFail?.(batch);
53
+ }
54
+ // 4xx errors are dropped (bad data, auth issues)
55
+ }
56
+ catch {
57
+ // Network error — cache for retry
58
+ this.onFlushFail?.(batch);
59
+ }
60
+ }
61
+ }
62
+ //# sourceMappingURL=https-sender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https-sender.js","sourceRoot":"","sources":["../../src/transport/https-sender.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAG9E,MAAM,OAAO,WAAW;IACd,MAAM,GAAqB,EAAE,CAAC;IAC9B,KAAK,GAA0C,IAAI,CAAC;IACpD,MAAM,CAAe;IACrB,WAAW,CAA8C;IACzD,WAAW,CAAsC;IAEzD,YACE,MAAoB,EACpB,IAGC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,IAAI,EAAE,WAAW,CAAC;QACrC,IAAI,CAAC,WAAW,GAAG,IAAI,EAAE,WAAW,CAAC;IACvC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,uBAAuB,CAAC,CAAC;IACxE,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,CAAC;QACD,cAAc;QACd,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,OAAO,CAAC,KAAqB;QAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;YAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,KAAK;QACjB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAErC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAEtD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,aAAa,EAAE;gBACnE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;iBAC9C;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;gBACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;aACpC,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyB,CAAC;gBAC7D,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;oBACrB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC;iBAAM,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;gBAClC,iCAAiC;gBACjC,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;YACD,iDAAiD;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;YAClC,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ import type { ClawnitorEvent } from "@clawnitor/shared";
2
+ export declare class SqliteCache {
3
+ private db;
4
+ constructor(dbPath?: string);
5
+ cache(events: ClawnitorEvent[]): void;
6
+ flush(limit?: number): ClawnitorEvent[];
7
+ count(): number;
8
+ sizeBytes(): number;
9
+ prune(): void;
10
+ close(): void;
11
+ }
12
+ //# sourceMappingURL=sqlite-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-cache.d.ts","sourceRoot":"","sources":["../../src/transport/sqlite-cache.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKxD,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,CAAC,EAAE,MAAM;IAa3B,KAAK,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI;IAarC,KAAK,CAAC,KAAK,GAAE,MAAW,GAAG,cAAc,EAAE;IAkB3C,KAAK,IAAI,MAAM;IAOf,SAAS,IAAI,MAAM;IASnB,KAAK,IAAI,IAAI;IAkBb,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,74 @@
1
+ import Database from "better-sqlite3";
2
+ import { CACHE_SIZE_LIMIT, CACHE_AGE_LIMIT } from "@clawnitor/shared";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ export class SqliteCache {
6
+ db;
7
+ constructor(dbPath) {
8
+ const path = dbPath || join(tmpdir(), "clawnitor-cache.db");
9
+ this.db = new Database(path);
10
+ this.db.pragma("journal_mode = WAL");
11
+ this.db.exec(`
12
+ CREATE TABLE IF NOT EXISTS cached_events (
13
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
14
+ payload TEXT NOT NULL,
15
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
16
+ )
17
+ `);
18
+ }
19
+ cache(events) {
20
+ const insert = this.db.prepare("INSERT INTO cached_events (payload) VALUES (?)");
21
+ const tx = this.db.transaction((evts) => {
22
+ for (const event of evts) {
23
+ insert.run(JSON.stringify(event));
24
+ }
25
+ });
26
+ tx(events);
27
+ this.prune();
28
+ }
29
+ flush(limit = 50) {
30
+ const rows = this.db
31
+ .prepare("SELECT id, payload FROM cached_events ORDER BY id ASC LIMIT ?")
32
+ .all(limit);
33
+ if (rows.length === 0)
34
+ return [];
35
+ const events = rows.map((r) => JSON.parse(r.payload));
36
+ const ids = rows.map((r) => r.id);
37
+ const placeholders = ids.map(() => "?").join(",");
38
+ this.db
39
+ .prepare(`DELETE FROM cached_events WHERE id IN (${placeholders})`)
40
+ .run(...ids);
41
+ return events;
42
+ }
43
+ count() {
44
+ const row = this.db
45
+ .prepare("SELECT COUNT(*) as count FROM cached_events")
46
+ .get();
47
+ return row.count;
48
+ }
49
+ sizeBytes() {
50
+ const row = this.db
51
+ .prepare("SELECT COALESCE(SUM(LENGTH(payload)), 0) as size FROM cached_events")
52
+ .get();
53
+ return row.size;
54
+ }
55
+ prune() {
56
+ // Age-based eviction
57
+ const maxAge = Math.floor((Date.now() - CACHE_AGE_LIMIT) / 1000);
58
+ this.db
59
+ .prepare("DELETE FROM cached_events WHERE created_at < ?")
60
+ .run(maxAge);
61
+ // Size-based eviction
62
+ while (this.sizeBytes() > CACHE_SIZE_LIMIT) {
63
+ const deleted = this.db
64
+ .prepare("DELETE FROM cached_events WHERE id IN (SELECT id FROM cached_events ORDER BY id ASC LIMIT 100)")
65
+ .run();
66
+ if (deleted.changes === 0)
67
+ break;
68
+ }
69
+ }
70
+ close() {
71
+ this.db.close();
72
+ }
73
+ }
74
+ //# sourceMappingURL=sqlite-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-cache.js","sourceRoot":"","sources":["../../src/transport/sqlite-cache.ts"],"names":[],"mappings":"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"}