@intutic/mcp-governance-proxy 2.1.0 → 2.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 +34 -18
- package/dist/agentTools.d.ts +68 -0
- package/dist/agentTools.d.ts.map +1 -0
- package/dist/agentTools.js +150 -0
- package/dist/agentTools.js.map +1 -0
- package/dist/anomaly/detectors.js +1 -1
- package/dist/anomaly/detectors.js.map +1 -1
- package/dist/anomaly/index.d.ts +2 -1
- package/dist/anomaly/index.d.ts.map +1 -1
- package/dist/anomaly/index.js.map +1 -1
- package/dist/approvalHold.d.ts +66 -0
- package/dist/approvalHold.d.ts.map +1 -0
- package/dist/approvalHold.js +143 -0
- package/dist/approvalHold.js.map +1 -0
- package/dist/budget.d.ts +133 -0
- package/dist/budget.d.ts.map +1 -0
- package/dist/budget.js +191 -0
- package/dist/budget.js.map +1 -0
- package/dist/config.d.ts +7 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/daemon/healthMonitor.d.ts +2 -0
- package/dist/daemon/healthMonitor.d.ts.map +1 -1
- package/dist/daemon/healthMonitor.js +60 -27
- package/dist/daemon/healthMonitor.js.map +1 -1
- package/dist/daemon/index.d.ts +0 -2
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +0 -2
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/instrumentation.d.ts +2 -2
- package/dist/daemon/instrumentation.js +2 -2
- package/dist/daemon/policyCache.d.ts +38 -3
- package/dist/daemon/policyCache.d.ts.map +1 -1
- package/dist/daemon/policyCache.js +64 -22
- package/dist/daemon/policyCache.js.map +1 -1
- package/dist/daemon/socketServer.d.ts +0 -1
- package/dist/daemon/socketServer.d.ts.map +1 -1
- package/dist/daemon/socketServer.js +0 -1
- package/dist/daemon/socketServer.js.map +1 -1
- package/dist/daemon/statusReporter.d.ts.map +1 -1
- package/dist/daemon/statusReporter.js +0 -1
- package/dist/daemon/statusReporter.js.map +1 -1
- package/dist/daemon/telemetryBatcher.d.ts.map +1 -1
- package/dist/daemon/telemetryBatcher.js +0 -1
- package/dist/daemon/telemetryBatcher.js.map +1 -1
- package/dist/daemonClient.d.ts.map +1 -1
- package/dist/daemonClient.js +0 -1
- package/dist/daemonClient.js.map +1 -1
- package/dist/dlp.d.ts +32 -3
- package/dist/dlp.d.ts.map +1 -1
- package/dist/dlp.js +116 -18
- package/dist/dlp.js.map +1 -1
- package/dist/dlpPii.d.ts +66 -0
- package/dist/dlpPii.d.ts.map +1 -0
- package/dist/dlpPii.js +235 -0
- package/dist/dlpPii.js.map +1 -0
- package/dist/emitter.d.ts +68 -10
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +29 -43
- package/dist/emitter.js.map +1 -1
- package/dist/guardedValkey.d.ts +40 -0
- package/dist/guardedValkey.d.ts.map +1 -0
- package/dist/guardedValkey.js +84 -0
- package/dist/guardedValkey.js.map +1 -0
- package/dist/httpJson.d.ts +19 -0
- package/dist/httpJson.d.ts.map +1 -0
- package/dist/httpJson.js +62 -0
- package/dist/httpJson.js.map +1 -0
- package/dist/identity.d.ts +27 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +36 -0
- package/dist/identity.js.map +1 -0
- package/dist/injection.js +1 -1
- package/dist/interceptor.d.ts +135 -8
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +284 -45
- package/dist/interceptor.js.map +1 -1
- package/dist/policy.d.ts +105 -2
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +188 -40
- package/dist/policy.js.map +1 -1
- package/dist/proxy.d.ts +35 -11
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +183 -66
- package/dist/proxy.js.map +1 -1
- package/dist/refusals.d.ts +26 -0
- package/dist/refusals.d.ts.map +1 -0
- package/dist/refusals.js +43 -0
- package/dist/refusals.js.map +1 -0
- package/dist/registryObserver.d.ts +37 -0
- package/dist/registryObserver.d.ts.map +1 -0
- package/dist/registryObserver.js +70 -0
- package/dist/registryObserver.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/dist/sessionScope.d.ts +1 -1
- package/dist/sessionScope.js +1 -1
- package/dist/sessionStore.d.ts +10 -23
- package/dist/sessionStore.d.ts.map +1 -1
- package/dist/sessionStore.js +17 -76
- package/dist/sessionStore.js.map +1 -1
- package/dist/version.d.ts +12 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/dist/wasm/context.d.ts +10 -0
- package/dist/wasm/context.d.ts.map +1 -1
- package/dist/wasm/context.js +27 -0
- package/dist/wasm/context.js.map +1 -1
- package/dist/wasm/hostImports.d.ts +3 -3
- package/dist/wasm/hostImports.d.ts.map +1 -1
- package/dist/wasm/hostImports.js +8 -7
- package/dist/wasm/hostImports.js.map +1 -1
- package/dist/wasm/loader.d.ts +8 -1
- package/dist/wasm/loader.d.ts.map +1 -1
- package/dist/wasm/loader.js +2 -0
- package/dist/wasm/loader.js.map +1 -1
- package/dist/wasm/referencedFiles.d.ts +29 -4
- package/dist/wasm/referencedFiles.d.ts.map +1 -1
- package/dist/wasm/referencedFiles.js +48 -21
- package/dist/wasm/referencedFiles.js.map +1 -1
- package/dist/wasm/runner.d.ts +106 -22
- package/dist/wasm/runner.d.ts.map +1 -1
- package/dist/wasm/runner.js +194 -74
- package/dist/wasm/runner.js.map +1 -1
- package/dist/wasm/watch.js +1 -1
- package/dist/wasm/worker.d.ts +10 -6
- package/dist/wasm/worker.d.ts.map +1 -1
- package/dist/wasm/worker.js +93 -9
- package/dist/wasm/worker.js.map +1 -1
- package/package.json +5 -4
- package/dist/toolPoison.d.ts +0 -32
- package/dist/toolPoison.d.ts.map +0 -1
- package/dist/toolPoison.js +0 -81
- package/dist/toolPoison.js.map +0 -1
- package/dist/wasm/fuel.d.ts +0 -35
- package/dist/wasm/fuel.d.ts.map +0 -1
- package/dist/wasm/fuel.js +0 -299
- package/dist/wasm/fuel.js.map +0 -1
- package/dist/wasm/memoryCap.d.ts +0 -21
- package/dist/wasm/memoryCap.d.ts.map +0 -1
- package/dist/wasm/memoryCap.js +0 -54
- package/dist/wasm/memoryCap.js.map +0 -1
- package/dist/wasm/wasmBinary.d.ts +0 -25
- package/dist/wasm/wasmBinary.d.ts.map +0 -1
- package/dist/wasm/wasmBinary.js +0 -100
- package/dist/wasm/wasmBinary.js.map +0 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* approvalHold.ts — `require_approval` rules as real holds, not blocks.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the harness hook gates' hold tier through the same control-plane
|
|
5
|
+
* decisions API:
|
|
6
|
+
*
|
|
7
|
+
* 1. A call that matches a `require_approval` rule is looked up in the
|
|
8
|
+
* workspace's approved bypasses (`GET /api/v1/decisions/approved-bypasses`).
|
|
9
|
+
* An exact, unexpired match — same rule, same server and tool, same
|
|
10
|
+
* arguments — lets the call through.
|
|
11
|
+
* 2. Otherwise the call is refused and a hold is recorded
|
|
12
|
+
* (`POST /api/v1/decisions`), which puts it in the review queue, notifies
|
|
13
|
+
* the workspace (`decision.pending`, Slack included), and gives the agent a
|
|
14
|
+
* hold id to quote.
|
|
15
|
+
* 3. An Owner, Admin or EM approves it (`intutic decision approve <holdId>`, the review
|
|
16
|
+
* API, or the Slack card — all one code path in the control plane). The
|
|
17
|
+
* approval lets the identical retry pass only while the workspace's
|
|
18
|
+
* review-hold bypass (`reviewHoldBypassEnabled`) is on: approving then
|
|
19
|
+
* writes the bypass step 1 finds, valid for `reviewHoldBypassTtlMinutes`.
|
|
20
|
+
* Otherwise the approval records the decision only, exactly as it does for
|
|
21
|
+
* a hook-gate hold.
|
|
22
|
+
*
|
|
23
|
+
* "Identical" is the bypass key: the rule id, `mcp__<server>__<tool>`
|
|
24
|
+
* lower-cased, and a SHA-256 of the arguments serialised with sorted keys, so
|
|
25
|
+
* the same arguments in a different key order still match and any other
|
|
26
|
+
* argument value does not.
|
|
27
|
+
*
|
|
28
|
+
* A hold needs the control plane both ways — to find an approval and to
|
|
29
|
+
* record a request for one — so an unreachable control plane means the call
|
|
30
|
+
* stays held, whatever the fail-open setting: a rule that says a person must
|
|
31
|
+
* approve is not satisfied by nobody being reachable to ask.
|
|
32
|
+
*
|
|
33
|
+
* @module
|
|
34
|
+
*/
|
|
35
|
+
import * as node_crypto from 'node:crypto';
|
|
36
|
+
import { createStderrLogger as createLogger } from './stderrLog.js';
|
|
37
|
+
import { getJson, postJson } from './httpJson.js';
|
|
38
|
+
const log = createLogger('mcp-proxy-hold');
|
|
39
|
+
/** The hold-record version the control plane accepts (`POST /api/v1/decisions`). */
|
|
40
|
+
const HOLD_RECORD_VERSION = 1;
|
|
41
|
+
/** JSON with object keys sorted at every level, so key order never changes a hash. */
|
|
42
|
+
export function canonicalJson(value) {
|
|
43
|
+
if (Array.isArray(value))
|
|
44
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
45
|
+
if (value !== null && typeof value === 'object') {
|
|
46
|
+
const entries = Object.entries(value)
|
|
47
|
+
.filter(([, v]) => v !== undefined)
|
|
48
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
49
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
|
|
50
|
+
}
|
|
51
|
+
return JSON.stringify(value ?? null);
|
|
52
|
+
}
|
|
53
|
+
export function holdKey(serverName, toolName, toolInput) {
|
|
54
|
+
return {
|
|
55
|
+
toolNameNormalized: `mcp__${serverName}__${toolName}`.trim().toLowerCase(),
|
|
56
|
+
targetHash: node_crypto.createHash('sha256').update(canonicalJson(toolInput ?? {})).digest('hex'),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function newHoldId() {
|
|
60
|
+
return `hold_${Date.now().toString(36)}_${node_crypto.randomBytes(4).toString('hex')}`;
|
|
61
|
+
}
|
|
62
|
+
export class ApprovalHolds {
|
|
63
|
+
controlPlaneUrl;
|
|
64
|
+
apiKey;
|
|
65
|
+
workspaceId;
|
|
66
|
+
serverName;
|
|
67
|
+
identity;
|
|
68
|
+
constructor(controlPlaneUrl, apiKey, workspaceId, serverName,
|
|
69
|
+
/** Rides on the hold record so the reviewer sees who asked (identity.ts). */
|
|
70
|
+
identity = undefined) {
|
|
71
|
+
this.controlPlaneUrl = controlPlaneUrl;
|
|
72
|
+
this.apiKey = apiKey;
|
|
73
|
+
this.workspaceId = workspaceId;
|
|
74
|
+
this.serverName = serverName;
|
|
75
|
+
this.identity = identity;
|
|
76
|
+
}
|
|
77
|
+
/** Lets the call through on an approved bypass, or records a hold and returns its id. Never throws. */
|
|
78
|
+
async request(rule, toolName, toolInput) {
|
|
79
|
+
const { toolNameNormalized, targetHash } = holdKey(this.serverName, toolName, toolInput);
|
|
80
|
+
const bypass = await this.findBypass(rule.id, toolNameNormalized, targetHash);
|
|
81
|
+
if (bypass)
|
|
82
|
+
return bypass;
|
|
83
|
+
const holdId = newHoldId();
|
|
84
|
+
try {
|
|
85
|
+
await postJson(`${this.controlPlaneUrl}/api/v1/decisions`, this.apiKey, {
|
|
86
|
+
holds: [
|
|
87
|
+
{
|
|
88
|
+
v: HOLD_RECORD_VERSION,
|
|
89
|
+
holdId,
|
|
90
|
+
reason: rule.id,
|
|
91
|
+
tool: toolNameNormalized,
|
|
92
|
+
sessionId: '',
|
|
93
|
+
at: new Date().toISOString(),
|
|
94
|
+
toolNameNormalized,
|
|
95
|
+
targetHash,
|
|
96
|
+
context: {
|
|
97
|
+
source: 'mcp_proxy',
|
|
98
|
+
server: this.serverName,
|
|
99
|
+
tool: toolName,
|
|
100
|
+
rule: rule.reason,
|
|
101
|
+
...(this.identity ? { principal: this.identity } : {}),
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
return { kind: 'held', holdId, recorded: true };
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
log.warn({ action: 'hold_record_failed', ruleId: rule.id, err: err.message }, 'Could not record the hold');
|
|
110
|
+
return { kind: 'held', holdId, recorded: false };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async findBypass(ruleId, toolNameNormalized, targetHash) {
|
|
114
|
+
let body;
|
|
115
|
+
try {
|
|
116
|
+
body = await getJson(`${this.controlPlaneUrl}/api/v1/decisions/approved-bypasses`, this.apiKey);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
log.warn({ action: 'bypass_lookup_failed', err: err.message }, 'Could not look up approved bypasses — the call stays held');
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const list = body?.bypasses;
|
|
123
|
+
if (!Array.isArray(list))
|
|
124
|
+
return null;
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
for (const raw of list) {
|
|
127
|
+
if (raw.workspaceId !== this.workspaceId || raw.sopRuleId !== ruleId)
|
|
128
|
+
continue;
|
|
129
|
+
if (raw.toolNameNormalized !== toolNameNormalized || raw.targetHash !== targetHash)
|
|
130
|
+
continue;
|
|
131
|
+
const expires = typeof raw.expiresAt === 'string' ? Date.parse(raw.expiresAt) : NaN;
|
|
132
|
+
if (Number.isNaN(expires) || now >= expires)
|
|
133
|
+
continue;
|
|
134
|
+
return {
|
|
135
|
+
kind: 'bypassed',
|
|
136
|
+
holdId: typeof raw.holdId === 'string' ? raw.holdId : '',
|
|
137
|
+
decidedBy: typeof raw.decidedBy === 'string' ? raw.decidedBy : '',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=approvalHold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalHold.js","sourceRoot":"","sources":["../src/approvalHold.ts"],"names":[],"mappings":"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"}
|
package/dist/budget.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* budget.ts — MCP call budgets, enforced before a call leaves the machine.
|
|
3
|
+
*
|
|
4
|
+
* The workspace's `mcpBudgets` setting (shared-types `mcpBudgets.ts`) limits
|
|
5
|
+
* how many tool calls go to a server, a tool, or come from a member, per hour
|
|
6
|
+
* or per day. Every proxy pointed at the same Valkey counts against the same
|
|
7
|
+
* counters, so a member's daily allowance is one allowance however many
|
|
8
|
+
* harness sessions and servers they run; proxies on different Valkeys count
|
|
9
|
+
* separately.
|
|
10
|
+
*
|
|
11
|
+
* One round trip per call that any budget covers, none for a call no budget
|
|
12
|
+
* covers. A Lua script checks every counter the call draws on and, only if
|
|
13
|
+
* none is used up, increments them all — atomically, so two proxies racing
|
|
14
|
+
* for the last call cannot both get it, and a refused call never consumes
|
|
15
|
+
* allowance. The same script claims the once-per-period alert flags (SET NX
|
|
16
|
+
* with the counter's lifetime, the dedup the LLM budget alerts use), so the
|
|
17
|
+
* warning and the exceeded notification go out once per budget per period
|
|
18
|
+
* whichever proxy crosses the line.
|
|
19
|
+
*
|
|
20
|
+
* Periods are fixed UTC windows (`budgetWindow`); the window start is part of
|
|
21
|
+
* every key, so a new hour or day starts from zero and old counters expire on
|
|
22
|
+
* their own. Each proxy reads its own clock: proxies whose clocks disagree by
|
|
23
|
+
* a few seconds disagree about the window for those seconds.
|
|
24
|
+
*
|
|
25
|
+
* When Valkey cannot answer — no URL configured, not connected, slower than
|
|
26
|
+
* the guard's timeout, or an error — the check is `unavailable`, and the
|
|
27
|
+
* interceptor applies the proxy's fail setting: fail-open lets the call
|
|
28
|
+
* through uncounted, fail-closed refuses it.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
import { type McpBudget, type McpBudgetPolicy } from '@intutic/shared-types';
|
|
33
|
+
import type { GuardedValkey } from './guardedValkey.js';
|
|
34
|
+
/**
|
|
35
|
+
* Counter keys. The workspace id is a hash tag (`{…}`), so every key one call
|
|
36
|
+
* touches lives in the same slot and the script stays valid on a cluster.
|
|
37
|
+
*/
|
|
38
|
+
export declare function budgetCounterKey(workspaceId: string, budgetId: string, windowStartMs: number, subject: string): string;
|
|
39
|
+
export interface BudgetCounter {
|
|
40
|
+
key: string;
|
|
41
|
+
limit: number;
|
|
42
|
+
warnAt: number;
|
|
43
|
+
ttlSecs: number;
|
|
44
|
+
}
|
|
45
|
+
export type CountOutcome = {
|
|
46
|
+
allowed: true;
|
|
47
|
+
used: number[];
|
|
48
|
+
warnClaimed: boolean[];
|
|
49
|
+
} | {
|
|
50
|
+
allowed: false;
|
|
51
|
+
index: number;
|
|
52
|
+
used: number;
|
|
53
|
+
exhaustedClaimed: boolean;
|
|
54
|
+
};
|
|
55
|
+
export interface BudgetStore {
|
|
56
|
+
/** Checks and counts one call against every counter; `undefined` when Valkey cannot answer. */
|
|
57
|
+
count(counters: readonly BudgetCounter[]): Promise<CountOutcome | undefined>;
|
|
58
|
+
}
|
|
59
|
+
export declare class ValkeyBudgetStore implements BudgetStore {
|
|
60
|
+
private readonly valkey;
|
|
61
|
+
constructor(valkey: GuardedValkey);
|
|
62
|
+
count(counters: readonly BudgetCounter[]): Promise<CountOutcome | undefined>;
|
|
63
|
+
/**
|
|
64
|
+
* The current counts of the given counters, without counting anything:
|
|
65
|
+
* what the `intutic` MCP server reports as used. A counter never written
|
|
66
|
+
* this period reads 0. `undefined` when Valkey cannot answer.
|
|
67
|
+
*/
|
|
68
|
+
used(keys: readonly string[]): Promise<number[] | undefined>;
|
|
69
|
+
}
|
|
70
|
+
/** A budget at a moment in its period: which allowance, how much of it is used, and when it resets. */
|
|
71
|
+
export interface BudgetStanding {
|
|
72
|
+
budget: McpBudget;
|
|
73
|
+
/** The member (or fallback caller) a per-member budget counted; empty for any other budget. */
|
|
74
|
+
subject: string;
|
|
75
|
+
used: number;
|
|
76
|
+
resetAt: Date;
|
|
77
|
+
}
|
|
78
|
+
export type BudgetVerdict =
|
|
79
|
+
/** No budget covers this call. */
|
|
80
|
+
{
|
|
81
|
+
kind: 'unlimited';
|
|
82
|
+
}
|
|
83
|
+
/** Counted. `warnings`: budgets this call took to their warning threshold first this period. */
|
|
84
|
+
| {
|
|
85
|
+
kind: 'allowed';
|
|
86
|
+
warnings: BudgetStanding[];
|
|
87
|
+
}
|
|
88
|
+
/** Refused: a budget is used up. `notify` is true for the first refusal of the period. */
|
|
89
|
+
| {
|
|
90
|
+
kind: 'exceeded';
|
|
91
|
+
standing: BudgetStanding;
|
|
92
|
+
notify: boolean;
|
|
93
|
+
}
|
|
94
|
+
/** Budgets cover this call, but Valkey could not answer. */
|
|
95
|
+
| {
|
|
96
|
+
kind: 'unavailable';
|
|
97
|
+
budgets: McpBudget[];
|
|
98
|
+
};
|
|
99
|
+
export declare class McpBudgetEnforcer {
|
|
100
|
+
/** `undefined` when the proxy has no Valkey configured: every covered call is then `unavailable`. */
|
|
101
|
+
private readonly store;
|
|
102
|
+
private readonly workspaceId;
|
|
103
|
+
private readonly serverName;
|
|
104
|
+
/** Who a per-member budget counts when the control plane has not named a member (identity.ts). */
|
|
105
|
+
private readonly fallbackCaller;
|
|
106
|
+
private readonly now;
|
|
107
|
+
constructor(
|
|
108
|
+
/** `undefined` when the proxy has no Valkey configured: every covered call is then `unavailable`. */
|
|
109
|
+
store: BudgetStore | undefined, workspaceId: string, serverName: string,
|
|
110
|
+
/** Who a per-member budget counts when the control plane has not named a member (identity.ts). */
|
|
111
|
+
fallbackCaller: string, now?: () => number);
|
|
112
|
+
check(policy: McpBudgetPolicy, toolName: string, memberId: string | null): Promise<BudgetVerdict>;
|
|
113
|
+
}
|
|
114
|
+
/** The refusal an agent reads: which budget, how much of it is used, and when it resets. */
|
|
115
|
+
export declare function exceededReason(s: BudgetStanding, nowMs?: number): string;
|
|
116
|
+
export declare function warningReason(s: BudgetStanding, warnAtPct: number): string;
|
|
117
|
+
export declare function unavailableReason(budgets: readonly McpBudget[]): string;
|
|
118
|
+
/** What a budget event tells the control plane, for its notification and finding. */
|
|
119
|
+
export interface BudgetEventDetail {
|
|
120
|
+
budgetId: string;
|
|
121
|
+
scope: McpBudget['scope'];
|
|
122
|
+
server?: string;
|
|
123
|
+
tool?: string;
|
|
124
|
+
memberId?: string;
|
|
125
|
+
/** Whose allowance ran out, for a per-member budget: the member, or the fallback caller. */
|
|
126
|
+
subject?: string;
|
|
127
|
+
period: McpBudget['period'];
|
|
128
|
+
limit: number;
|
|
129
|
+
used: number;
|
|
130
|
+
resetAt: string;
|
|
131
|
+
}
|
|
132
|
+
export declare function budgetEventDetail(s: BudgetStanding): BudgetEventDetail;
|
|
133
|
+
//# sourceMappingURL=budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"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"}
|
package/dist/budget.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* budget.ts — MCP call budgets, enforced before a call leaves the machine.
|
|
3
|
+
*
|
|
4
|
+
* The workspace's `mcpBudgets` setting (shared-types `mcpBudgets.ts`) limits
|
|
5
|
+
* how many tool calls go to a server, a tool, or come from a member, per hour
|
|
6
|
+
* or per day. Every proxy pointed at the same Valkey counts against the same
|
|
7
|
+
* counters, so a member's daily allowance is one allowance however many
|
|
8
|
+
* harness sessions and servers they run; proxies on different Valkeys count
|
|
9
|
+
* separately.
|
|
10
|
+
*
|
|
11
|
+
* One round trip per call that any budget covers, none for a call no budget
|
|
12
|
+
* covers. A Lua script checks every counter the call draws on and, only if
|
|
13
|
+
* none is used up, increments them all — atomically, so two proxies racing
|
|
14
|
+
* for the last call cannot both get it, and a refused call never consumes
|
|
15
|
+
* allowance. The same script claims the once-per-period alert flags (SET NX
|
|
16
|
+
* with the counter's lifetime, the dedup the LLM budget alerts use), so the
|
|
17
|
+
* warning and the exceeded notification go out once per budget per period
|
|
18
|
+
* whichever proxy crosses the line.
|
|
19
|
+
*
|
|
20
|
+
* Periods are fixed UTC windows (`budgetWindow`); the window start is part of
|
|
21
|
+
* every key, so a new hour or day starts from zero and old counters expire on
|
|
22
|
+
* their own. Each proxy reads its own clock: proxies whose clocks disagree by
|
|
23
|
+
* a few seconds disagree about the window for those seconds.
|
|
24
|
+
*
|
|
25
|
+
* When Valkey cannot answer — no URL configured, not connected, slower than
|
|
26
|
+
* the guard's timeout, or an error — the check is `unavailable`, and the
|
|
27
|
+
* interceptor applies the proxy's fail setting: fail-open lets the call
|
|
28
|
+
* through uncounted, fail-closed refuses it.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
import { budgetWarnAt, budgetWindow, budgetsForCall, describeMcpBudget, } from '@intutic/shared-types';
|
|
33
|
+
/**
|
|
34
|
+
* Counter keys. The workspace id is a hash tag (`{…}`), so every key one call
|
|
35
|
+
* touches lives in the same slot and the script stays valid on a cluster.
|
|
36
|
+
*/
|
|
37
|
+
export function budgetCounterKey(workspaceId, budgetId, windowStartMs, subject) {
|
|
38
|
+
return `v2:mcpbudget:{${workspaceId}}:${budgetId}:${windowStartMs}:${encodeURIComponent(subject)}`;
|
|
39
|
+
}
|
|
40
|
+
/** Extra seconds a counter outlives its window, so a slow clock never reads a fresh zero mid-window. */
|
|
41
|
+
const COUNTER_TTL_SLACK_SECS = 60;
|
|
42
|
+
/**
|
|
43
|
+
* KEYS, per counter i (1-based, three each): the count, the warned flag, the
|
|
44
|
+
* exhausted flag. ARGV, per counter: limit, warn-at count, TTL in seconds.
|
|
45
|
+
* Returns `{0, i, used, exhaustedClaimed}` when counter i is used up (nothing
|
|
46
|
+
* incremented), else `{1, used_1, warnClaimed_1, used_2, warnClaimed_2, …}`.
|
|
47
|
+
*/
|
|
48
|
+
const CHECK_AND_COUNT = `
|
|
49
|
+
local n = #KEYS / 3
|
|
50
|
+
for i = 1, n do
|
|
51
|
+
local used = tonumber(redis.call('GET', KEYS[3 * i - 2]) or '0')
|
|
52
|
+
if used >= tonumber(ARGV[3 * i - 2]) then
|
|
53
|
+
local claimed = redis.call('SET', KEYS[3 * i], '1', 'NX', 'EX', ARGV[3 * i]) and 1 or 0
|
|
54
|
+
return {0, i, used, claimed}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
local out = {1}
|
|
58
|
+
for i = 1, n do
|
|
59
|
+
local used = redis.call('INCR', KEYS[3 * i - 2])
|
|
60
|
+
if used == 1 then redis.call('EXPIRE', KEYS[3 * i - 2], ARGV[3 * i]) end
|
|
61
|
+
local claimed = 0
|
|
62
|
+
if used >= tonumber(ARGV[3 * i - 1]) then
|
|
63
|
+
if redis.call('SET', KEYS[3 * i - 1], '1', 'NX', 'EX', ARGV[3 * i]) then claimed = 1 end
|
|
64
|
+
end
|
|
65
|
+
out[#out + 1] = used
|
|
66
|
+
out[#out + 1] = claimed
|
|
67
|
+
end
|
|
68
|
+
return out
|
|
69
|
+
`;
|
|
70
|
+
export class ValkeyBudgetStore {
|
|
71
|
+
valkey;
|
|
72
|
+
constructor(valkey) {
|
|
73
|
+
this.valkey = valkey;
|
|
74
|
+
}
|
|
75
|
+
async count(counters) {
|
|
76
|
+
const keys = counters.flatMap((c) => [`${c.key}:count`, `${c.key}:warned`, `${c.key}:exhausted`]);
|
|
77
|
+
const args = counters.flatMap((c) => [String(c.limit), String(c.warnAt), String(c.ttlSecs)]);
|
|
78
|
+
const reply = await this.valkey.run((client) => client.eval(CHECK_AND_COUNT, keys.length, ...keys, ...args), { awaitConnect: true });
|
|
79
|
+
if (!Array.isArray(reply) || reply.length === 0)
|
|
80
|
+
return undefined;
|
|
81
|
+
const n = reply.map(Number);
|
|
82
|
+
if (n[0] === 0)
|
|
83
|
+
return { allowed: false, index: n[1] - 1, used: n[2], exhaustedClaimed: n[3] === 1 };
|
|
84
|
+
const used = [];
|
|
85
|
+
const warnClaimed = [];
|
|
86
|
+
for (let i = 1; i < n.length; i += 2) {
|
|
87
|
+
used.push(n[i]);
|
|
88
|
+
warnClaimed.push(n[i + 1] === 1);
|
|
89
|
+
}
|
|
90
|
+
return { allowed: true, used, warnClaimed };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The current counts of the given counters, without counting anything:
|
|
94
|
+
* what the `intutic` MCP server reports as used. A counter never written
|
|
95
|
+
* this period reads 0. `undefined` when Valkey cannot answer.
|
|
96
|
+
*/
|
|
97
|
+
async used(keys) {
|
|
98
|
+
const reply = await this.valkey.run((client) => client.mget(...keys.map((k) => `${k}:count`)), { awaitConnect: true });
|
|
99
|
+
return reply?.map((v) => Number(v ?? 0));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export class McpBudgetEnforcer {
|
|
103
|
+
store;
|
|
104
|
+
workspaceId;
|
|
105
|
+
serverName;
|
|
106
|
+
fallbackCaller;
|
|
107
|
+
now;
|
|
108
|
+
constructor(
|
|
109
|
+
/** `undefined` when the proxy has no Valkey configured: every covered call is then `unavailable`. */
|
|
110
|
+
store, workspaceId, serverName,
|
|
111
|
+
/** Who a per-member budget counts when the control plane has not named a member (identity.ts). */
|
|
112
|
+
fallbackCaller, now = Date.now) {
|
|
113
|
+
this.store = store;
|
|
114
|
+
this.workspaceId = workspaceId;
|
|
115
|
+
this.serverName = serverName;
|
|
116
|
+
this.fallbackCaller = fallbackCaller;
|
|
117
|
+
this.now = now;
|
|
118
|
+
}
|
|
119
|
+
async check(policy, toolName, memberId) {
|
|
120
|
+
const charges = budgetsForCall(policy, {
|
|
121
|
+
server: this.serverName,
|
|
122
|
+
tool: toolName,
|
|
123
|
+
memberId,
|
|
124
|
+
fallbackCaller: this.fallbackCaller,
|
|
125
|
+
});
|
|
126
|
+
if (charges.length === 0)
|
|
127
|
+
return { kind: 'unlimited' };
|
|
128
|
+
if (!this.store)
|
|
129
|
+
return { kind: 'unavailable', budgets: charges.map((c) => c.budget) };
|
|
130
|
+
const nowMs = this.now();
|
|
131
|
+
const windows = charges.map((c) => budgetWindow(c.budget.period, nowMs));
|
|
132
|
+
const counters = charges.map((c, i) => ({
|
|
133
|
+
key: budgetCounterKey(this.workspaceId, c.budget.id, windows[i].startMs, c.subject),
|
|
134
|
+
limit: c.budget.limit,
|
|
135
|
+
warnAt: budgetWarnAt(c.budget.limit, policy.warnAtPct),
|
|
136
|
+
ttlSecs: Math.ceil((windows[i].resetAtMs - nowMs) / 1000) + COUNTER_TTL_SLACK_SECS,
|
|
137
|
+
}));
|
|
138
|
+
const outcome = await this.store.count(counters);
|
|
139
|
+
if (!outcome)
|
|
140
|
+
return { kind: 'unavailable', budgets: charges.map((c) => c.budget) };
|
|
141
|
+
const standing = (i, used) => ({
|
|
142
|
+
budget: charges[i].budget,
|
|
143
|
+
subject: charges[i].subject,
|
|
144
|
+
used,
|
|
145
|
+
resetAt: new Date(windows[i].resetAtMs),
|
|
146
|
+
});
|
|
147
|
+
if (!outcome.allowed) {
|
|
148
|
+
return { kind: 'exceeded', standing: standing(outcome.index, outcome.used), notify: outcome.exhaustedClaimed };
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
kind: 'allowed',
|
|
152
|
+
warnings: outcome.used.flatMap((used, i) => (outcome.warnClaimed[i] ? [standing(i, used)] : [])),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function untilReset(resetAt, nowMs) {
|
|
157
|
+
const minutes = Math.max(1, Math.ceil((resetAt.getTime() - nowMs) / 60_000));
|
|
158
|
+
return minutes < 120 ? `${minutes} min` : `${Math.round(minutes / 60)} h`;
|
|
159
|
+
}
|
|
160
|
+
/** The refusal an agent reads: which budget, how much of it is used, and when it resets. */
|
|
161
|
+
export function exceededReason(s, nowMs = Date.now()) {
|
|
162
|
+
return (`MCP call budget "${s.budget.id}" is used up (${describeMcpBudget(s.budget)}): ${s.used} of ${s.budget.limit} ` +
|
|
163
|
+
`calls made this ${s.budget.period}. It resets at ${s.resetAt.toISOString()} (in ${untilReset(s.resetAt, nowMs)}). ` +
|
|
164
|
+
`An owner or admin can change MCP budgets on the MCP Servers page.`);
|
|
165
|
+
}
|
|
166
|
+
export function warningReason(s, warnAtPct) {
|
|
167
|
+
return (`MCP call budget "${s.budget.id}" reached its ${warnAtPct}% warning (${describeMcpBudget(s.budget)}): ` +
|
|
168
|
+
`${s.used} of ${s.budget.limit} calls made this ${s.budget.period}. It resets at ${s.resetAt.toISOString()}.`);
|
|
169
|
+
}
|
|
170
|
+
export function unavailableReason(budgets) {
|
|
171
|
+
return (`MCP call budget${budgets.length === 1 ? '' : 's'} ${budgets.map((b) => `"${b.id}"`).join(', ')} cover this call, ` +
|
|
172
|
+
`but the call count could not be checked (no Valkey configured for this proxy with INTUTIC_VALKEY_URL, ` +
|
|
173
|
+
`or Valkey unreachable). Tool call blocked by workspace policy (fail-closed mode). Contact your ` +
|
|
174
|
+
`administrator or update mcpProxyFailBehavior to open.`);
|
|
175
|
+
}
|
|
176
|
+
export function budgetEventDetail(s) {
|
|
177
|
+
const { budget } = s;
|
|
178
|
+
return {
|
|
179
|
+
budgetId: budget.id,
|
|
180
|
+
scope: budget.scope,
|
|
181
|
+
...(budget.server !== undefined ? { server: budget.server } : {}),
|
|
182
|
+
...(budget.tool !== undefined ? { tool: budget.tool } : {}),
|
|
183
|
+
...(budget.memberId !== undefined ? { memberId: budget.memberId } : {}),
|
|
184
|
+
...(s.subject ? { subject: s.subject } : {}),
|
|
185
|
+
period: budget.period,
|
|
186
|
+
limit: budget.limit,
|
|
187
|
+
used: s.used,
|
|
188
|
+
resetAt: s.resetAt.toISOString(),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -19,7 +19,12 @@ export interface ProxyConfig {
|
|
|
19
19
|
eventsFilePath: string;
|
|
20
20
|
/** Policy cache TTL in milliseconds (default: 60_000) */
|
|
21
21
|
policyTtlMs: number;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Whether a governance check that cannot complete lets the call through
|
|
24
|
+
* (default: true), from `INTUTIC_MCP_FAIL_OPEN`. The local fallback only:
|
|
25
|
+
* once the control plane delivers the workspace's `mcpProxyFailBehavior`,
|
|
26
|
+
* that wins (interceptor.ts's `failOpen`).
|
|
27
|
+
*/
|
|
23
28
|
failOpen: boolean;
|
|
24
29
|
/**
|
|
25
30
|
* MCP proxy deployment model ('per-session' | 'daemon').
|
|
@@ -125,7 +130,7 @@ export interface ProxyConfig {
|
|
|
125
130
|
*/
|
|
126
131
|
mcpWasmDir: string | undefined;
|
|
127
132
|
/**
|
|
128
|
-
* The Valkey the session window is shared through (Wave 5.3
|
|
133
|
+
* The Valkey the session window is shared through (Wave 5.3):
|
|
129
134
|
* `INTUTIC_VALKEY_URL` or `VALKEY_URL` from the environment, else
|
|
130
135
|
* `INTUTIC_VALKEY_URL` from runtime.env (written by `intutic connect` / the
|
|
131
136
|
* sync daemon when their local Valkey is running). No localhost default —
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAA;IACnB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAA;IACvB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;IACnB
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAA;IACnB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAA;IACvB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;;;;OAOG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IAChC;;;;;;;OAOG;IACH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC;;;;;;;;;;;OAWG;IACH,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAA;IACpC;;;;;;;;;;;;OAYG;IACH,cAAc,EAAE,SAAS,GAAG,MAAM,GAAG,KAAK,CAAA;IAC1C;;;;;;;;;;;OAWG;IACH,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC,CAAA;IACvE;;;;;;;;;;;OAWG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CACjC;AAyHD;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,WAAW,CAAC,CA+G7F"}
|
package/dist/config.js
CHANGED
|
@@ -139,7 +139,7 @@ export async function loadConfig(argv = process.argv.slice(2)) {
|
|
|
139
139
|
// (`runtimeEnv.ts`) and what the hook scripts read. This loader read only
|
|
140
140
|
// `INTUTIC_CONTROL_PLANE_URL`, which nothing writes there, so every
|
|
141
141
|
// per-session proxy on a connected machine fetched policy from
|
|
142
|
-
// localhost:3001 unless the shell happened to export the variable
|
|
142
|
+
// localhost:3001 unless the shell happened to export the variable.
|
|
143
143
|
const controlPlaneUrl = process.env['INTUTIC_CONTROL_PLANE_URL'] ??
|
|
144
144
|
runtimeEnv['INTUTIC_CONTROL_PLANE_URL'] ??
|
|
145
145
|
runtimeEnv['INTUTIC_HOST'] ??
|
|
@@ -154,7 +154,7 @@ export async function loadConfig(argv = process.argv.slice(2)) {
|
|
|
154
154
|
const eventsFilePath = process.env['INTUTIC_EVENTS_FILE'] ??
|
|
155
155
|
runtimeEnv['INTUTIC_EVENTS_FILE'] ??
|
|
156
156
|
DEFAULT_EVENTS_PATH;
|
|
157
|
-
const failOpen = (runtimeEnv['INTUTIC_MCP_FAIL_OPEN'] ?? 'true').toLowerCase() !== 'false';
|
|
157
|
+
const failOpen = (process.env['INTUTIC_MCP_FAIL_OPEN'] ?? runtimeEnv['INTUTIC_MCP_FAIL_OPEN'] ?? 'true').toLowerCase() !== 'false';
|
|
158
158
|
// 'daemon' is honoured downstream: policy.ts routes policy lookups through
|
|
159
159
|
// the daemon's Unix socket when this is set. No warning here — the mode does
|
|
160
160
|
// what it says.
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,8 @@ export interface McpServerHealth {
|
|
|
11
11
|
credentialExpiryAt?: string;
|
|
12
12
|
checkedAt: string;
|
|
13
13
|
}
|
|
14
|
+
/** Exported for tests; `home` defaults to the user's home directory. */
|
|
15
|
+
export declare function discoverServers(home?: string): McpServerConfig[];
|
|
14
16
|
export declare function registerServer(server: McpServerConfig): void;
|
|
15
17
|
export declare function startHealthMonitor(): void;
|
|
16
18
|
export declare function stopHealthMonitor(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"healthMonitor.d.ts","sourceRoot":"","sources":["../../src/daemon/healthMonitor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"healthMonitor.d.ts","sourceRoot":"","sources":["../../src/daemon/healthMonitor.ts"],"names":[],"mappings":"AA8BA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAM,MAAM,CAAA;IAChB,GAAG,EAAO,MAAM,CAAA;IAChB,kBAAkB,CAAC,EAAE,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAW,MAAM,CAAA;IAC3B,MAAM,EAAe,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;IAC3D,YAAY,CAAC,EAAQ,MAAM,CAAA;IAC3B,YAAY,CAAC,EAAQ,MAAM,CAAA;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,SAAS,EAAY,MAAM,CAAA;CAC5B;AAyED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAqB,GAAG,eAAe,EAAE,CA0C9E;AAkDD,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAE5D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAuBzC;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,wBAAgB,iBAAiB,IAAI,eAAe,EAAE,CAErD"}
|