@mapick/cost-firewall 0.1.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 (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -0
  3. package/dist/breaker.d.ts +30 -0
  4. package/dist/breaker.d.ts.map +1 -0
  5. package/dist/breaker.js +131 -0
  6. package/dist/breaker.js.map +1 -0
  7. package/dist/cli/index.d.ts +18 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +244 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/config-warn.d.ts +11 -0
  12. package/dist/config-warn.d.ts.map +1 -0
  13. package/dist/config-warn.js +26 -0
  14. package/dist/config-warn.js.map +1 -0
  15. package/dist/config.d.ts +7 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +32 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/dashboard/html.d.ts +2 -0
  20. package/dist/dashboard/html.d.ts.map +1 -0
  21. package/dist/dashboard/html.js +898 -0
  22. package/dist/dashboard/html.js.map +1 -0
  23. package/dist/dashboard/index.d.ts +8 -0
  24. package/dist/dashboard/index.d.ts.map +1 -0
  25. package/dist/dashboard/index.js +163 -0
  26. package/dist/dashboard/index.js.map +1 -0
  27. package/dist/dashboard/sse.d.ts +9 -0
  28. package/dist/dashboard/sse.d.ts.map +1 -0
  29. package/dist/dashboard/sse.js +22 -0
  30. package/dist/dashboard/sse.js.map +1 -0
  31. package/dist/hooks/agent-end.d.ts +18 -0
  32. package/dist/hooks/agent-end.d.ts.map +1 -0
  33. package/dist/hooks/agent-end.js +25 -0
  34. package/dist/hooks/agent-end.js.map +1 -0
  35. package/dist/hooks/before-agent-reply.d.ts +29 -0
  36. package/dist/hooks/before-agent-reply.d.ts.map +1 -0
  37. package/dist/hooks/before-agent-reply.js +42 -0
  38. package/dist/hooks/before-agent-reply.js.map +1 -0
  39. package/dist/hooks/index.d.ts +7 -0
  40. package/dist/hooks/index.d.ts.map +1 -0
  41. package/dist/hooks/index.js +17 -0
  42. package/dist/hooks/index.js.map +1 -0
  43. package/dist/hooks/model-call.d.ts +39 -0
  44. package/dist/hooks/model-call.d.ts.map +1 -0
  45. package/dist/hooks/model-call.js +120 -0
  46. package/dist/hooks/model-call.js.map +1 -0
  47. package/dist/index.d.ts +13 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +49 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/pricing.d.ts +12 -0
  52. package/dist/pricing.d.ts.map +1 -0
  53. package/dist/pricing.js +43 -0
  54. package/dist/pricing.js.map +1 -0
  55. package/dist/provider/auth.d.ts +14 -0
  56. package/dist/provider/auth.d.ts.map +1 -0
  57. package/dist/provider/auth.js +53 -0
  58. package/dist/provider/auth.js.map +1 -0
  59. package/dist/provider/index.d.ts +12 -0
  60. package/dist/provider/index.d.ts.map +1 -0
  61. package/dist/provider/index.js +134 -0
  62. package/dist/provider/index.js.map +1 -0
  63. package/dist/provider/route.d.ts +10 -0
  64. package/dist/provider/route.d.ts.map +1 -0
  65. package/dist/provider/route.js +19 -0
  66. package/dist/provider/route.js.map +1 -0
  67. package/dist/provider/stream.d.ts +10 -0
  68. package/dist/provider/stream.d.ts.map +1 -0
  69. package/dist/provider/stream.js +120 -0
  70. package/dist/provider/stream.js.map +1 -0
  71. package/dist/provider/synthetic.d.ts +13 -0
  72. package/dist/provider/synthetic.d.ts.map +1 -0
  73. package/dist/provider/synthetic.js +59 -0
  74. package/dist/provider/synthetic.js.map +1 -0
  75. package/dist/provider/upstream/anthropic.d.ts +13 -0
  76. package/dist/provider/upstream/anthropic.d.ts.map +1 -0
  77. package/dist/provider/upstream/anthropic.js +62 -0
  78. package/dist/provider/upstream/anthropic.js.map +1 -0
  79. package/dist/provider/upstream/openai.d.ts +17 -0
  80. package/dist/provider/upstream/openai.d.ts.map +1 -0
  81. package/dist/provider/upstream/openai.js +75 -0
  82. package/dist/provider/upstream/openai.js.map +1 -0
  83. package/dist/source.d.ts +35 -0
  84. package/dist/source.d.ts.map +1 -0
  85. package/dist/source.js +41 -0
  86. package/dist/source.js.map +1 -0
  87. package/dist/state.d.ts +56 -0
  88. package/dist/state.d.ts.map +1 -0
  89. package/dist/state.js +178 -0
  90. package/dist/state.js.map +1 -0
  91. package/dist/store.d.ts +23 -0
  92. package/dist/store.d.ts.map +1 -0
  93. package/dist/store.js +68 -0
  94. package/dist/store.js.map +1 -0
  95. package/dist/tools/index.d.ts +13 -0
  96. package/dist/tools/index.d.ts.map +1 -0
  97. package/dist/tools/index.js +63 -0
  98. package/dist/tools/index.js.map +1 -0
  99. package/dist/types.d.ts +98 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +7 -0
  102. package/dist/types.js.map +1 -0
  103. package/openclaw.plugin.json +44 -0
  104. package/package.json +49 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/provider/upstream/anthropic.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,wBAAuB,eAAe,CACpC,OAAO,EAAE,sBAAsB,GAC9B,cAAc,CAAC,GAAG,CAAC,CAgErB"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Anthropic Messages API upstream transport
3
+ */
4
+ export async function* streamAnthropic(options) {
5
+ const { apiKey, model, messages, max_tokens = 4096, stream = true, ...rest } = options;
6
+ const url = "https://api.anthropic.com/v1/messages";
7
+ // 30s timeout
8
+ const controller = new AbortController();
9
+ const timeoutId = setTimeout(() => controller.abort(), 30_000);
10
+ try {
11
+ const response = await fetch(url, {
12
+ method: "POST",
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ "x-api-key": apiKey,
16
+ "anthropic-version": "2023-06-01",
17
+ },
18
+ body: JSON.stringify({
19
+ model,
20
+ messages,
21
+ max_tokens,
22
+ stream,
23
+ ...rest,
24
+ }),
25
+ signal: controller.signal,
26
+ });
27
+ if (!response.ok) {
28
+ const errorText = await response.text();
29
+ throw new Error(`Anthropic upstream error ${response.status}: ${errorText}`);
30
+ }
31
+ if (!response.body) {
32
+ throw new Error("Anthropic upstream returned empty body");
33
+ }
34
+ const reader = response.body.getReader();
35
+ const decoder = new TextDecoder();
36
+ let buffer = "";
37
+ while (true) {
38
+ const { done, value } = await reader.read();
39
+ if (done)
40
+ break;
41
+ buffer += decoder.decode(value, { stream: true });
42
+ const lines = buffer.split("\n");
43
+ buffer = lines.pop() ?? "";
44
+ for (const line of lines) {
45
+ const trimmed = line.trim();
46
+ if (!trimmed || !trimmed.startsWith("data: "))
47
+ continue;
48
+ const data = trimmed.slice("data: ".length);
49
+ try {
50
+ yield JSON.parse(data);
51
+ }
52
+ catch {
53
+ // Skip invalid JSON
54
+ }
55
+ }
56
+ }
57
+ }
58
+ finally {
59
+ clearTimeout(timeoutId);
60
+ }
61
+ }
62
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/provider/upstream/anthropic.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,eAAe,CACpC,OAA+B;IAE/B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAEvF,MAAM,GAAG,GAAG,uCAAuC,CAAC;IAEpD,cAAc;IACd,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;IAE/D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,WAAW,EAAE,MAAM;gBACnB,mBAAmB,EAAE,YAAY;aAClC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK;gBACL,QAAQ;gBACR,UAAU;gBACV,MAAM;gBACN,GAAG,IAAI;aACR,CAAC;YACF,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC,CAAC;QAC/E,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI;gBAAE,MAAM;YAEhB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YAE3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC5B,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC;oBAAE,SAAS;gBAExD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAE5C,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACzB,CAAC;gBAAC,MAAM,CAAC;oBACP,oBAAoB;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * OpenAI-compatible upstream transport
3
+ *
4
+ * Supports /v1/chat/completions
5
+ * Compatible with OpenRouter, DeepSeek, Qwen and other OpenAI-compatible providers
6
+ */
7
+ export interface OpenAiStreamOptions {
8
+ baseUrl: string;
9
+ apiKey: string;
10
+ model: string;
11
+ messages: any[];
12
+ stream?: boolean;
13
+ [key: string]: any;
14
+ }
15
+ export declare function streamOpenAi(options: OpenAiStreamOptions): AsyncGenerator<any>;
16
+ export declare function getOpenAiBaseUrl(upstream: string): string;
17
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/provider/upstream/openai.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,wBAAuB,YAAY,CACjC,OAAO,EAAE,mBAAmB,GAC3B,cAAc,CAAC,GAAG,CAAC,CAgErB;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASzD"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * OpenAI-compatible upstream transport
3
+ *
4
+ * Supports /v1/chat/completions
5
+ * Compatible with OpenRouter, DeepSeek, Qwen and other OpenAI-compatible providers
6
+ */
7
+ export async function* streamOpenAi(options) {
8
+ const { baseUrl, apiKey, model, messages, stream = true, ...rest } = options;
9
+ const url = `${baseUrl}/v1/chat/completions`;
10
+ // 30s timeout
11
+ const controller = new AbortController();
12
+ const timeoutId = setTimeout(() => controller.abort(), 30_000);
13
+ try {
14
+ const response = await fetch(url, {
15
+ method: "POST",
16
+ headers: {
17
+ "Content-Type": "application/json",
18
+ Authorization: `Bearer ${apiKey}`,
19
+ },
20
+ body: JSON.stringify({
21
+ model,
22
+ messages,
23
+ stream,
24
+ ...rest,
25
+ }),
26
+ signal: controller.signal,
27
+ });
28
+ if (!response.ok) {
29
+ const errorText = await response.text();
30
+ throw new Error(`OpenAI upstream error ${response.status}: ${errorText}`);
31
+ }
32
+ if (!response.body) {
33
+ throw new Error("OpenAI upstream returned empty body");
34
+ }
35
+ const reader = response.body.getReader();
36
+ const decoder = new TextDecoder();
37
+ let buffer = "";
38
+ while (true) {
39
+ const { done, value } = await reader.read();
40
+ if (done)
41
+ break;
42
+ buffer += decoder.decode(value, { stream: true });
43
+ const lines = buffer.split("\n");
44
+ buffer = lines.pop() ?? "";
45
+ for (const line of lines) {
46
+ const trimmed = line.trim();
47
+ if (!trimmed || !trimmed.startsWith("data: "))
48
+ continue;
49
+ const data = trimmed.slice("data: ".length);
50
+ if (data === "[DONE]")
51
+ return;
52
+ try {
53
+ yield JSON.parse(data);
54
+ }
55
+ catch {
56
+ // Skip invalid JSON
57
+ }
58
+ }
59
+ }
60
+ }
61
+ finally {
62
+ clearTimeout(timeoutId);
63
+ // Reader is released in the normal flow, no need to releaseLock here
64
+ }
65
+ }
66
+ export function getOpenAiBaseUrl(upstream) {
67
+ const baseUrls = {
68
+ openai: "https://api.openai.com",
69
+ openrouter: "https://openrouter.ai/api",
70
+ deepseek: "https://api.deepseek.com",
71
+ qwen: "https://dashscope.aliyuncs.com/compatible-mode",
72
+ };
73
+ return baseUrls[upstream] ?? `https://api.${upstream}.com`;
74
+ }
75
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/provider/upstream/openai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Source key resolution
3
+ *
4
+ * Priority:
5
+ * 1. agentId + sessionId
6
+ * 2. sessionKey
7
+ * 3. workspaceDir + provider + model
8
+ * 4. provider + model
9
+ */
10
+ export interface HookContext {
11
+ agentId?: string;
12
+ sessionId?: string;
13
+ sessionKey?: string;
14
+ }
15
+ export interface ModelCallEvent {
16
+ runId?: string;
17
+ callId?: string;
18
+ sessionKey?: string;
19
+ sessionId?: string;
20
+ provider: string;
21
+ model: string;
22
+ }
23
+ export interface ProviderContext {
24
+ workspaceDir?: string;
25
+ provider?: string;
26
+ modelId?: string;
27
+ }
28
+ export interface RouteInfo {
29
+ upstream: string;
30
+ model: string;
31
+ }
32
+ export declare function sourceFromHookContext(event: Partial<HookContext>, ctx: Partial<HookContext>): string;
33
+ export declare function sourceFromModelCall(event: ModelCallEvent, ctx: Partial<HookContext>): string;
34
+ export declare function sourceFromProviderContext(ctx: ProviderContext, route: RouteInfo): string;
35
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,EAC3B,GAAG,EAAE,OAAO,CAAC,WAAW,CAAC,GACxB,MAAM,CAcR;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,OAAO,CAAC,WAAW,CAAC,GACxB,MAAM,CAWR;AAED,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,SAAS,GACf,MAAM,CAQR"}
package/dist/source.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Source key resolution
3
+ *
4
+ * Priority:
5
+ * 1. agentId + sessionId
6
+ * 2. sessionKey
7
+ * 3. workspaceDir + provider + model
8
+ * 4. provider + model
9
+ */
10
+ export function sourceFromHookContext(event, ctx) {
11
+ const agentId = event.agentId ?? ctx.agentId;
12
+ const sessionId = event.sessionId ?? ctx.sessionId;
13
+ if (agentId && sessionId) {
14
+ return `${agentId}/${sessionId}`;
15
+ }
16
+ const sessionKey = event.sessionKey ?? ctx.sessionKey;
17
+ if (sessionKey) {
18
+ return sessionKey;
19
+ }
20
+ return "unknown";
21
+ }
22
+ export function sourceFromModelCall(event, ctx) {
23
+ if (event.sessionId) {
24
+ const agentId = ctx.agentId ?? "unknown";
25
+ return `${agentId}/${event.sessionId}`;
26
+ }
27
+ if (event.sessionKey) {
28
+ return event.sessionKey;
29
+ }
30
+ return `${event.provider}/${event.model}`;
31
+ }
32
+ export function sourceFromProviderContext(ctx, route) {
33
+ if (ctx.workspaceDir) {
34
+ // Use last-level directory name instead of full path to avoid leaking user file paths
35
+ const parts = ctx.workspaceDir.replace(/\\/g, "/").split("/").filter(Boolean);
36
+ const basename = parts[parts.length - 1] || "ws";
37
+ return `${basename}/${route.upstream}/${route.model}`;
38
+ }
39
+ return `${route.upstream}/${route.model}`;
40
+ }
41
+ //# sourceMappingURL=source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4BH,MAAM,UAAU,qBAAqB,CACnC,KAA2B,EAC3B,GAAyB;IAEzB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS,CAAC;IAEnD,IAAI,OAAO,IAAI,SAAS,EAAE,CAAC;QACzB,OAAO,GAAG,OAAO,IAAI,SAAS,EAAE,CAAC;IACnC,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,CAAC;IACtD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,KAAqB,EACrB,GAAyB;IAEzB,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,SAAS,CAAC;QACzC,OAAO,GAAG,OAAO,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;IACzC,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC,UAAU,CAAC;IAC1B,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,GAAoB,EACpB,KAAgB;IAEhB,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;QACrB,sFAAsF;QACtF,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9E,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;QACjD,OAAO,GAAG,QAAQ,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;IACxD,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Global state management (in-memory)
3
+ */
4
+ import type { RunState, CallState, GlobalStats, SourceKey, FirewallConfig, PrecheckResult } from "./types.js";
5
+ import { Breaker } from "./breaker.js";
6
+ export declare class FirewallState {
7
+ readonly config: FirewallConfig;
8
+ readonly breaker: Breaker;
9
+ readonly globalStats: GlobalStats;
10
+ private runs;
11
+ private sourceStats;
12
+ private today;
13
+ private cleanupTimer;
14
+ private static readonly MAX_RUNS;
15
+ private static readonly CLEANUP_INTERVAL_MS;
16
+ private static readonly RUN_EXPIRY_MS;
17
+ private checkDayReset;
18
+ constructor(config?: Partial<FirewallConfig>);
19
+ private startCleanupTimer;
20
+ /**
21
+ * Purge runs older than RUN_EXPIRY_MS and enforce maxSize limit
22
+ */
23
+ private purgeOldRuns;
24
+ /**
25
+ * Stop cleanup timer (for graceful shutdown)
26
+ */
27
+ stopCleanupTimer(): void;
28
+ getRun(runId: string): RunState | undefined;
29
+ getOrCreateRun(runId: string, source: SourceKey, sessionId?: string, sessionKey?: string): RunState;
30
+ addCallToRun(runId: string, callId: string, call: CallState): void;
31
+ updateRunCost(runId: string, cost: number): void;
32
+ updateSourceStats(source: SourceKey, tokens: number): void;
33
+ /**
34
+ * Mark a run for cleanup (actual deletion happens via scheduled purgeOldRuns)
35
+ * This method is kept for compatibility with existing callers
36
+ */
37
+ cleanupRun(runId: string): void;
38
+ setEmergencyStop(enabled: boolean): void;
39
+ setMode(mode: "observe" | "protect"): void;
40
+ isLimitExceeded(): boolean;
41
+ /**
42
+ * Unified precheck for hook and provider layers
43
+ * Returns { allow: true } if all checks pass, otherwise { allow: false, reason, layer }
44
+ */
45
+ precheck(source: SourceKey): PrecheckResult;
46
+ getTodayTokens(): number;
47
+ /** Active run summary */
48
+ getActiveRuns(): {
49
+ runId: string;
50
+ source: string;
51
+ calls: number;
52
+ tokens: number;
53
+ status: string;
54
+ }[];
55
+ }
56
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,WAAW,EACX,SAAS,EACT,cAAc,EACd,cAAc,EACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,qBAAa,aAAa;IACxB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,WAAW,CAAiD;IACpE,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,YAAY,CAA+C;IAEnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAO;IACvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAU;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAc;IAEnD,OAAO,CAAC,aAAa;gBAUT,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM;IAahD,OAAO,CAAC,iBAAiB;IAUzB;;OAEG;IACH,OAAO,CAAC,YAAY;IAqBpB;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAOxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAI3C,cAAc,CACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,QAAQ;IAkBX,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAOlE,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAOhD,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAS1D;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ/B,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIxC,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI;IAI1C,eAAe,IAAI,OAAO;IAK1B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,cAAc;IAsB3C,cAAc,IAAI,MAAM;IAIxB,yBAAyB;IACzB,aAAa,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE;CAcpG"}
package/dist/state.js ADDED
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Global state management (in-memory)
3
+ */
4
+ import { Breaker } from "./breaker.js";
5
+ import { resolveConfig } from "./config.js";
6
+ export class FirewallState {
7
+ config;
8
+ breaker;
9
+ globalStats;
10
+ runs = new Map();
11
+ sourceStats = new Map();
12
+ today = new Date().toDateString();
13
+ cleanupTimer = null;
14
+ static MAX_RUNS = 100;
15
+ static CLEANUP_INTERVAL_MS = 10_000;
16
+ static RUN_EXPIRY_MS = 5 * 60_000; // 5 minutes
17
+ checkDayReset() {
18
+ const now = new Date().toDateString();
19
+ if (now !== this.today) {
20
+ this.today = now;
21
+ this.globalStats.todayTokens = 0;
22
+ this.globalStats.todayBlocked = 0;
23
+ this.sourceStats.clear();
24
+ }
25
+ }
26
+ constructor(config = {}) {
27
+ this.config = resolveConfig(config);
28
+ this.breaker = new Breaker(this.config);
29
+ this.globalStats = {
30
+ emergencyStop: false,
31
+ mode: "observe",
32
+ todayTokens: 0,
33
+ todayBlocked: 0,
34
+ todaySavedEstimate: 0,
35
+ };
36
+ this.startCleanupTimer();
37
+ }
38
+ startCleanupTimer() {
39
+ this.cleanupTimer = setInterval(() => {
40
+ this.purgeOldRuns();
41
+ }, FirewallState.CLEANUP_INTERVAL_MS);
42
+ if (this.cleanupTimer.unref) {
43
+ this.cleanupTimer.unref();
44
+ }
45
+ }
46
+ /**
47
+ * Purge runs older than RUN_EXPIRY_MS and enforce maxSize limit
48
+ */
49
+ purgeOldRuns() {
50
+ const now = Date.now();
51
+ // Delete runs older than 5 minutes
52
+ for (const [runId, run] of this.runs) {
53
+ if (now - run.startedAt > FirewallState.RUN_EXPIRY_MS) {
54
+ this.runs.delete(runId);
55
+ }
56
+ }
57
+ // Enforce maxSize limit: delete oldest runs if exceeds limit
58
+ if (this.runs.size > FirewallState.MAX_RUNS) {
59
+ const sortedRuns = [...this.runs.entries()]
60
+ .sort((a, b) => a[1].startedAt - b[1].startedAt);
61
+ const toDelete = sortedRuns.slice(0, this.runs.size - FirewallState.MAX_RUNS);
62
+ for (const [runId] of toDelete) {
63
+ this.runs.delete(runId);
64
+ }
65
+ }
66
+ }
67
+ /**
68
+ * Stop cleanup timer (for graceful shutdown)
69
+ */
70
+ stopCleanupTimer() {
71
+ if (this.cleanupTimer) {
72
+ clearInterval(this.cleanupTimer);
73
+ this.cleanupTimer = null;
74
+ }
75
+ }
76
+ getRun(runId) {
77
+ return this.runs.get(runId);
78
+ }
79
+ getOrCreateRun(runId, source, sessionId, sessionKey) {
80
+ if (!this.runs.has(runId)) {
81
+ this.runs.set(runId, {
82
+ runId,
83
+ sessionId,
84
+ sessionKey,
85
+ source,
86
+ startedAt: Date.now(),
87
+ calls: new Map(),
88
+ llmCallTimestamps: [],
89
+ cumulativeCost: 0,
90
+ promptHashCounts: new Map(),
91
+ status: "healthy",
92
+ });
93
+ }
94
+ return this.runs.get(runId);
95
+ }
96
+ addCallToRun(runId, callId, call) {
97
+ const run = this.runs.get(runId);
98
+ if (run) {
99
+ run.calls.set(callId, call);
100
+ }
101
+ }
102
+ updateRunCost(runId, cost) {
103
+ const run = this.runs.get(runId);
104
+ if (run) {
105
+ run.cumulativeCost += cost;
106
+ }
107
+ }
108
+ updateSourceStats(source, tokens) {
109
+ this.checkDayReset();
110
+ if (!this.sourceStats.has(source)) {
111
+ this.sourceStats.set(source, { todayTokens: 0 });
112
+ }
113
+ this.sourceStats.get(source).todayTokens += tokens;
114
+ this.globalStats.todayTokens += tokens;
115
+ }
116
+ /**
117
+ * Mark a run for cleanup (actual deletion happens via scheduled purgeOldRuns)
118
+ * This method is kept for compatibility with existing callers
119
+ */
120
+ cleanupRun(runId) {
121
+ // No immediate deletion - scheduled cleanup will handle it
122
+ // Optionally force delete if runs exceeds maxSize
123
+ if (this.runs.size > FirewallState.MAX_RUNS) {
124
+ this.runs.delete(runId);
125
+ }
126
+ }
127
+ setEmergencyStop(enabled) {
128
+ this.globalStats.emergencyStop = enabled;
129
+ }
130
+ setMode(mode) {
131
+ this.globalStats.mode = mode;
132
+ }
133
+ isLimitExceeded() {
134
+ if (this.config.dailyTokenLimit == null)
135
+ return false;
136
+ return this.globalStats.todayTokens >= this.config.dailyTokenLimit;
137
+ }
138
+ /**
139
+ * Unified precheck for hook and provider layers
140
+ * Returns { allow: true } if all checks pass, otherwise { allow: false, reason, layer }
141
+ */
142
+ precheck(source) {
143
+ // Emergency stop is a global override - always blocks regardless of mode
144
+ if (this.globalStats.emergencyStop) {
145
+ return { allow: false, reason: "emergency_stop", layer: "hook" };
146
+ }
147
+ // Observe mode bypasses all other checks
148
+ if (this.globalStats.mode === "observe") {
149
+ return { allow: true, layer: "hook" };
150
+ }
151
+ if (this.isLimitExceeded()) {
152
+ return { allow: false, reason: "daily_token_limit", layer: "hook" };
153
+ }
154
+ if (this.breaker.isCoolingDown(source)) {
155
+ return { allow: false, reason: this.breaker.getBlockedReason(source) ?? "source_cooldown", layer: "provider" };
156
+ }
157
+ return { allow: true, layer: "hook" };
158
+ }
159
+ getTodayTokens() {
160
+ return this.globalStats.todayTokens;
161
+ }
162
+ /** Active run summary */
163
+ getActiveRuns() {
164
+ const result = [];
165
+ for (const [id, run] of this.runs) {
166
+ result.push({
167
+ runId: id,
168
+ source: run.source,
169
+ calls: run.calls.size,
170
+ tokens: run.cumulativeCost,
171
+ status: run.status,
172
+ reason: run.reason,
173
+ });
174
+ }
175
+ return result;
176
+ }
177
+ }
178
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * JSONL event storage (async, non-blocking hook)
3
+ */
4
+ import type { FirewallEvent } from "./types.js";
5
+ export type { FirewallEvent };
6
+ export declare class EventStore {
7
+ private buffer;
8
+ private flushTimer;
9
+ private dirReady;
10
+ constructor();
11
+ private ensureDir;
12
+ append(event: Omit<FirewallEvent, "timestamp">): void;
13
+ private startFlushTimer;
14
+ /**
15
+ * Flush buffer to disk (synchronous, copy-and-replace pattern)
16
+ * This prevents concurrent flushes from double-writing or interleaving events
17
+ */
18
+ flush(): void;
19
+ close(): Promise<void>;
20
+ getStateDir(): string;
21
+ getEventsFilePath(): string;
22
+ }
23
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,YAAY,EAAE,aAAa,EAAE,CAAC;AAQ9B,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,QAAQ,CAAS;;IAMzB,OAAO,CAAC,SAAS;IAMjB,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG,IAAI;IAQrD,OAAO,CAAC,eAAe;IAUvB;;;OAGG;IACH,KAAK,IAAI,IAAI;IAYP,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,WAAW,IAAI,MAAM;IAIrB,iBAAiB,IAAI,MAAM;CAG5B"}
package/dist/store.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * JSONL event storage (async, non-blocking hook)
3
+ */
4
+ import { writeFileSync, mkdirSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { homedir } from "node:os";
7
+ const FLUSH_INTERVAL_MS = 1000;
8
+ const STATE_DIR = process.env.OPENCLAW_STATE_DIR
9
+ ? join(process.env.OPENCLAW_STATE_DIR, "plugins", "mapick-firewall")
10
+ : join(homedir(), ".openclaw", "plugins", "mapick-firewall");
11
+ const EVENTS_FILE = join(STATE_DIR, "events.jsonl");
12
+ export class EventStore {
13
+ buffer = [];
14
+ flushTimer = null;
15
+ dirReady = false;
16
+ constructor() {
17
+ this.startFlushTimer();
18
+ }
19
+ ensureDir() {
20
+ if (this.dirReady)
21
+ return;
22
+ mkdirSync(STATE_DIR, { recursive: true });
23
+ this.dirReady = true;
24
+ }
25
+ append(event) {
26
+ const fullEvent = {
27
+ ...event,
28
+ timestamp: Date.now(),
29
+ };
30
+ this.buffer.push(fullEvent);
31
+ }
32
+ startFlushTimer() {
33
+ this.flushTimer = setInterval(() => {
34
+ this.flush();
35
+ }, FLUSH_INTERVAL_MS);
36
+ if (this.flushTimer.unref) {
37
+ this.flushTimer.unref();
38
+ }
39
+ }
40
+ /**
41
+ * Flush buffer to disk (synchronous, copy-and-replace pattern)
42
+ * This prevents concurrent flushes from double-writing or interleaving events
43
+ */
44
+ flush() {
45
+ if (this.buffer.length === 0)
46
+ return;
47
+ // Copy-and-replace: grab current buffer and clear it atomically
48
+ const batch = this.buffer;
49
+ this.buffer = [];
50
+ this.ensureDir();
51
+ const lines = batch.map((e) => JSON.stringify(e)).join("\n") + "\n";
52
+ writeFileSync(EVENTS_FILE, lines, "utf-8");
53
+ }
54
+ async close() {
55
+ if (this.flushTimer) {
56
+ clearInterval(this.flushTimer);
57
+ this.flushTimer = null;
58
+ }
59
+ this.flush();
60
+ }
61
+ getStateDir() {
62
+ return STATE_DIR;
63
+ }
64
+ getEventsFilePath() {
65
+ return EVENTS_FILE;
66
+ }
67
+ }
68
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKlC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB;IAC9C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,SAAS,EAAE,iBAAiB,CAAC;IACpE,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;AAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;AAEpD,MAAM,OAAO,UAAU;IACb,MAAM,GAAoB,EAAE,CAAC;IAC7B,UAAU,GAA0C,IAAI,CAAC;IACzD,QAAQ,GAAG,KAAK,CAAC;IAEzB;QACE,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAEO,SAAS;QACf,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,MAAM,CAAC,KAAuC;QAC5C,MAAM,SAAS,GAAkB;YAC/B,GAAG,KAAK;YACR,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC,EAAE,iBAAiB,CAAC,CAAC;QAEtB,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAErC,gEAAgE;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QAEjB,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACpE,aAAa,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,WAAW;QACT,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,iBAAiB;QACf,OAAO,WAAW,CAAC;IACrB,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Agent Tools registration — users control Cost Firewall via conversation
3
+ *
4
+ * Usage:
5
+ * /firewall status → view status
6
+ * /firewall stop → emergency breaker
7
+ * /firewall resume → resume
8
+ * /firewall log → view recent events
9
+ */
10
+ import type { FirewallState } from "../state.js";
11
+ import type { EventStore } from "../store.js";
12
+ export declare function registerTools(api: any, state: FirewallState, store: EventStore): void;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CA+CrF"}