@intutic/mcp-governance-proxy 1.10.0 → 1.10.1
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 +28 -0
- package/dist/anomaly/detectors.d.ts +129 -0
- package/dist/anomaly/detectors.d.ts.map +1 -0
- package/dist/anomaly/detectors.js +368 -0
- package/dist/anomaly/detectors.js.map +1 -0
- package/dist/anomaly/index.d.ts +52 -0
- package/dist/anomaly/index.d.ts.map +1 -0
- package/dist/anomaly/index.js +71 -0
- package/dist/anomaly/index.js.map +1 -0
- package/dist/config.d.ts +107 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +129 -6
- package/dist/config.js.map +1 -1
- package/dist/daemon/healthMonitor.d.ts.map +1 -1
- package/dist/daemon/healthMonitor.js +45 -46
- package/dist/daemon/healthMonitor.js.map +1 -1
- package/dist/daemon/index.d.ts +15 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +78 -9
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/instrumentation.d.ts +20 -0
- package/dist/daemon/instrumentation.d.ts.map +1 -0
- package/dist/daemon/instrumentation.js +61 -0
- package/dist/daemon/instrumentation.js.map +1 -0
- package/dist/daemon/policyCache.d.ts +83 -0
- package/dist/daemon/policyCache.d.ts.map +1 -1
- package/dist/daemon/policyCache.js +280 -11
- package/dist/daemon/policyCache.js.map +1 -1
- package/dist/daemon/statusReporter.d.ts +12 -0
- package/dist/daemon/statusReporter.d.ts.map +1 -0
- package/dist/daemon/statusReporter.js +130 -0
- package/dist/daemon/statusReporter.js.map +1 -0
- package/dist/daemon/telemetryBatcher.d.ts +3 -1
- package/dist/daemon/telemetryBatcher.d.ts.map +1 -1
- package/dist/daemon/telemetryBatcher.js +56 -13
- package/dist/daemon/telemetryBatcher.js.map +1 -1
- package/dist/daemonClient.d.ts +1 -1
- package/dist/daemonClient.d.ts.map +1 -1
- package/dist/daemonClient.js +0 -2
- package/dist/daemonClient.js.map +1 -1
- package/dist/dlp.d.ts +19 -0
- package/dist/dlp.d.ts.map +1 -1
- package/dist/dlp.js +95 -18
- package/dist/dlp.js.map +1 -1
- package/dist/emitter.d.ts +40 -2
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +27 -5
- package/dist/emitter.js.map +1 -1
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/injection.d.ts +46 -0
- package/dist/injection.d.ts.map +1 -0
- package/dist/injection.js +147 -0
- package/dist/injection.js.map +1 -0
- package/dist/interceptor.d.ts +131 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +301 -5
- package/dist/interceptor.js.map +1 -1
- package/dist/policy.d.ts +110 -2
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +206 -5
- package/dist/policy.js.map +1 -1
- package/dist/proxy.d.ts +205 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +572 -65
- package/dist/proxy.js.map +1 -1
- package/dist/remoteBridge.d.ts +51 -0
- package/dist/remoteBridge.d.ts.map +1 -0
- package/dist/remoteBridge.js +182 -0
- package/dist/remoteBridge.js.map +1 -0
- package/dist/session.d.ts +158 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +206 -0
- package/dist/session.js.map +1 -0
- package/dist/sessionScope.d.ts +51 -0
- package/dist/sessionScope.d.ts.map +1 -0
- package/dist/sessionScope.js +94 -0
- package/dist/sessionScope.js.map +1 -0
- package/dist/sessionStore.d.ts +69 -0
- package/dist/sessionStore.d.ts.map +1 -0
- package/dist/sessionStore.js +143 -0
- package/dist/sessionStore.js.map +1 -0
- package/dist/tofu.d.ts +112 -0
- package/dist/tofu.d.ts.map +1 -0
- package/dist/tofu.js +180 -0
- package/dist/tofu.js.map +1 -0
- package/dist/toolPoison.d.ts +32 -0
- package/dist/toolPoison.d.ts.map +1 -0
- package/dist/toolPoison.js +81 -0
- package/dist/toolPoison.js.map +1 -0
- package/dist/valkeyErrors.d.ts +17 -0
- package/dist/valkeyErrors.d.ts.map +1 -0
- package/dist/valkeyErrors.js +24 -0
- package/dist/valkeyErrors.js.map +1 -0
- package/dist/wasm/context.d.ts +57 -0
- package/dist/wasm/context.d.ts.map +1 -0
- package/dist/wasm/context.js +59 -0
- package/dist/wasm/context.js.map +1 -0
- package/dist/wasm/hostImports.d.ts +43 -0
- package/dist/wasm/hostImports.d.ts.map +1 -0
- package/dist/wasm/hostImports.js +158 -0
- package/dist/wasm/hostImports.js.map +1 -0
- package/dist/wasm/loader.d.ts +95 -0
- package/dist/wasm/loader.d.ts.map +1 -0
- package/dist/wasm/loader.js +198 -0
- package/dist/wasm/loader.js.map +1 -0
- package/dist/wasm/referencedFiles.d.ts +115 -0
- package/dist/wasm/referencedFiles.d.ts.map +1 -0
- package/dist/wasm/referencedFiles.js +366 -0
- package/dist/wasm/referencedFiles.js.map +1 -0
- package/dist/wasm/runner.d.ts +91 -0
- package/dist/wasm/runner.d.ts.map +1 -0
- package/dist/wasm/runner.js +308 -0
- package/dist/wasm/runner.js.map +1 -0
- package/dist/wasm/watch.d.ts +5 -0
- package/dist/wasm/watch.d.ts.map +1 -0
- package/dist/wasm/watch.js +54 -0
- package/dist/wasm/watch.js.map +1 -0
- package/dist/wasm/worker.d.ts +39 -0
- package/dist/wasm/worker.d.ts.map +1 -0
- package/dist/wasm/worker.js +245 -0
- package/dist/wasm/worker.js.map +1 -0
- package/package.json +14 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostImports.d.ts","sourceRoot":"","sources":["../../src/wasm/hostImports.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EACL,eAAe,EAOhB,MAAM,sBAAsB,CAAA;AAI7B,mIAAmI;AACnI,eAAO,MAAM,WAAW,KAAU,CAAA;AAElC,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,KAAK,EAAE,eAAe,CAAA;IACtB,6DAA6D;IAC7D,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAgB,kBAAkB,CAAC,KAAK,GAAE,eAAyC,GAAG,eAAe,CAEpG;AAoBD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,WAAW,CAAC,MAAM,GAAG,SAAS,EAC/C,KAAK,GAAE,eAAsC,GAC5C,WAAW,CAAC,aAAa,CA0G3B"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wasm/hostImports.ts — The 4 frozen host imports (`WASM_HOST_IMPORTS` from
|
|
3
|
+
* `@intutic/shared-types`), implemented for this proxy's `worker_threads`
|
|
4
|
+
* sandbox.
|
|
5
|
+
*
|
|
6
|
+
* Built from `packages/proxy/src/wasm/host.rs` (the Rust behavior each
|
|
7
|
+
* import must match) and from `packages/wasm-sdk/__tests__/generatedRules.test.ts`'s
|
|
8
|
+
* `HOST_IMPL` (the reference Node-side implementation the task named as the
|
|
9
|
+
* base to build from). `log_info`/`abort`/`trace` mirror `host.rs`'s
|
|
10
|
+
* tracing-log behavior, routed through this package's own `createStderrLogger`
|
|
11
|
+
* (never `console.log`/stdout — the same stdio-isolation rule every other
|
|
12
|
+
* module in this package follows). `read_referenced_file` resolves against
|
|
13
|
+
* the per-evaluation table the runner pre-reads through `referencedFiles.ts`
|
|
14
|
+
* (TD-441) — the port of `host.rs`'s `read_referenced_file_impl`, code for
|
|
15
|
+
* code: the read budget is charged before argument validation, a refusal is
|
|
16
|
+
* a value the rule can act on, and nothing here traps. With no table (no
|
|
17
|
+
* `INTUTIC_WASM_MANIFEST_ROOT`, or a rule that does not import the function)
|
|
18
|
+
* every call gets `ERR_REFUSED`, which is what the Rust proxy answers with no
|
|
19
|
+
* root configured.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import { createStderrLogger as createLogger } from '../stderrLog.js';
|
|
24
|
+
import { ReferencedFiles, ERR_BAD_ARGS, ERR_BUDGET, ERR_BUFFER_TOO_SMALL, ERR_REFUSED as REFUSED, MAX_GUEST_PATH_BYTES, MAX_READS_PER_EVALUATION, } from './referencedFiles.js';
|
|
25
|
+
const log = createLogger('mcp-proxy-wasm-host');
|
|
26
|
+
/** `referenced_files.rs`'s `ERR_REFUSED` (-2), kept exported here for the callers that imported it before the resolver existed. */
|
|
27
|
+
export const ERR_REFUSED = REFUSED;
|
|
28
|
+
export function newHostImportState(files = ReferencedFiles.empty()) {
|
|
29
|
+
return { files, fileReadsRemaining: MAX_READS_PER_EVALUATION };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Decode a UTF-8 string out of a WASM instance's linear memory. Returns `''`
|
|
33
|
+
* on any bounds/decode failure — mirrors `host.rs`'s own posture of logging
|
|
34
|
+
* a warning and returning without acting, never trapping the guest over a
|
|
35
|
+
* malformed log call.
|
|
36
|
+
*/
|
|
37
|
+
function readUtf8(memory, ptr, len) {
|
|
38
|
+
if (ptr < 0 || len < 0)
|
|
39
|
+
return null;
|
|
40
|
+
const bytes = new Uint8Array(memory.buffer);
|
|
41
|
+
const end = ptr + len;
|
|
42
|
+
if (end > bytes.length)
|
|
43
|
+
return null;
|
|
44
|
+
try {
|
|
45
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(bytes.subarray(ptr, end));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Builds the `env.*` import object for one WASM instance. `getMemory` is a
|
|
53
|
+
* thunk rather than a captured value because the memory export does not
|
|
54
|
+
* exist until AFTER `WebAssembly.instantiate` returns, while the import
|
|
55
|
+
* object has to be supplied BEFORE it — the same ordering `host.rs` avoids
|
|
56
|
+
* entirely by reading `caller.get_export("memory")` inside each call; this
|
|
57
|
+
* is the Node-side equivalent of that lazy lookup.
|
|
58
|
+
*/
|
|
59
|
+
export function createHostImports(getMemory, state = newHostImportState()) {
|
|
60
|
+
return {
|
|
61
|
+
log_info: (ptr, len) => {
|
|
62
|
+
const memory = getMemory();
|
|
63
|
+
if (!memory) {
|
|
64
|
+
log.warn({ action: 'wasm_host_no_memory', call: 'log_info' }, 'WASM rule called log_info with no memory export');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const msg = readUtf8(memory, ptr, len);
|
|
68
|
+
if (msg === null) {
|
|
69
|
+
log.warn({ action: 'wasm_log_info_bad_args' }, 'WASM rule log_info payload out of bounds or not valid UTF-8');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
log.info({ action: 'wasm_rule_log' }, msg);
|
|
73
|
+
},
|
|
74
|
+
// Mirrors host.rs's `abort` import: log only, never throw/trap. A guest
|
|
75
|
+
// unhandled-exception calling `abort` must not crash the evaluation —
|
|
76
|
+
// the caller (worker.ts) still wraps the whole `evaluate` call so any
|
|
77
|
+
// resulting trap fails open, but this import itself is inert.
|
|
78
|
+
abort: (msgPtr, filePtr, line, col) => {
|
|
79
|
+
log.warn({ action: 'wasm_abort', msgPtr, filePtr, line, col }, 'WASM rule called abort (AssemblyScript unhandled exception)');
|
|
80
|
+
},
|
|
81
|
+
// Best-effort decode of AssemblyScript's `String#trace` layout — a
|
|
82
|
+
// length-prefixed UTF-16LE string with the 4-byte length immediately
|
|
83
|
+
// before `ptr`. Falls back to logging the raw pointer/length, same as
|
|
84
|
+
// host.rs's own fallback branch, rather than treating a decode failure
|
|
85
|
+
// as fatal.
|
|
86
|
+
trace: (ptr, n) => {
|
|
87
|
+
const memory = getMemory();
|
|
88
|
+
if (!memory)
|
|
89
|
+
return;
|
|
90
|
+
if (ptr >= 4) {
|
|
91
|
+
const bytes = new Uint8Array(memory.buffer);
|
|
92
|
+
if (ptr <= bytes.length) {
|
|
93
|
+
const lenBytes = bytes.subarray(ptr - 4, ptr);
|
|
94
|
+
const len = new DataView(lenBytes.buffer, lenBytes.byteOffset, 4).getUint32(0, true);
|
|
95
|
+
const end = ptr + len;
|
|
96
|
+
if (end <= bytes.length) {
|
|
97
|
+
try {
|
|
98
|
+
const utf16 = new Uint16Array(bytes.buffer, bytes.byteOffset + ptr, len / 2);
|
|
99
|
+
const msg = String.fromCharCode(...utf16);
|
|
100
|
+
log.info({ action: 'wasm_trace', n }, msg);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// fall through to the raw fallback below
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
log.info({ action: 'wasm_trace_raw', ptr, n }, 'WASM trace (undecoded)');
|
|
110
|
+
},
|
|
111
|
+
// Ported from host.rs's `read_referenced_file_impl`. Returns a length or
|
|
112
|
+
// one of the negative codes; never throws — a host trap would unwind the
|
|
113
|
+
// guest, fail the whole evaluation and land in the runner's fail-open
|
|
114
|
+
// arm, so one malformed call would silently switch off every OTHER
|
|
115
|
+
// check the rule performs.
|
|
116
|
+
read_referenced_file: (pathPtr, pathLen, outPtr, outCap) => {
|
|
117
|
+
// Charged before anything else, including argument validation, so a
|
|
118
|
+
// guest cannot spin on cheap malformed calls any more than on
|
|
119
|
+
// expensive good ones.
|
|
120
|
+
if (state.fileReadsRemaining <= 0) {
|
|
121
|
+
log.warn({ action: 'wasm_file_read_budget_exhausted', budget: MAX_READS_PER_EVALUATION }, 'WASM rule exhausted its referenced-file reads in one evaluation');
|
|
122
|
+
return ERR_BUDGET;
|
|
123
|
+
}
|
|
124
|
+
state.fileReadsRemaining -= 1;
|
|
125
|
+
const memory = getMemory();
|
|
126
|
+
if (!memory) {
|
|
127
|
+
log.warn({ action: 'wasm_host_no_memory', call: 'read_referenced_file' }, 'WASM rule called read_referenced_file with no memory export');
|
|
128
|
+
return ERR_BAD_ARGS;
|
|
129
|
+
}
|
|
130
|
+
if (pathPtr < 0 || pathLen <= 0 || pathLen > MAX_GUEST_PATH_BYTES || outCap < 0)
|
|
131
|
+
return ERR_BAD_ARGS;
|
|
132
|
+
const requested = readUtf8(memory, pathPtr, pathLen);
|
|
133
|
+
if (requested === null)
|
|
134
|
+
return ERR_BAD_ARGS;
|
|
135
|
+
const hit = state.files.lookup(requested);
|
|
136
|
+
if (!hit.ok) {
|
|
137
|
+
// The reason is operator-facing only. The guest gets the code and is
|
|
138
|
+
// told nothing it did not already supply.
|
|
139
|
+
log.debug({ action: 'wasm_referenced_file_refused', path: requested, code: hit.code, reason: state.files.refusalReason(requested) }, "refused a WASM rule's referenced-file read");
|
|
140
|
+
return hit.code;
|
|
141
|
+
}
|
|
142
|
+
const len = hit.bytes.length; // capped at MAX_REFERENCED_FILE_BYTES on the way in
|
|
143
|
+
if (outCap === 0)
|
|
144
|
+
return len;
|
|
145
|
+
if (outCap < len)
|
|
146
|
+
return ERR_BUFFER_TOO_SMALL;
|
|
147
|
+
const view = new Uint8Array(memory.buffer);
|
|
148
|
+
if (outPtr < 0 || outPtr + len > view.length) {
|
|
149
|
+
// Out of bounds: a refusal, not a trap, same as host.rs.
|
|
150
|
+
log.warn({ action: 'wasm_referenced_file_out_ptr_oob', outPtr, len }, 'WASM rule passed an out-of-bounds output buffer');
|
|
151
|
+
return ERR_BAD_ARGS;
|
|
152
|
+
}
|
|
153
|
+
view.set(hit.bytes, outPtr);
|
|
154
|
+
return len;
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=hostImports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostImports.js","sourceRoot":"","sources":["../../src/wasm/hostImports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wasm/loader.ts — `~/.intutic/wasm/` directory loader.
|
|
3
|
+
*
|
|
4
|
+
* Direct TypeScript port of `packages/proxy/src/wasm/local_loader.rs`'s
|
|
5
|
+
* directory-resolution order, `NN_name.wasm` priority parsing, the 10MB
|
|
6
|
+
* per-file size cap, and mtime+size-based change detection — confirmed by
|
|
7
|
+
* reading that file, not guessed. Compilation and import validation
|
|
8
|
+
* themselves happen in the worker (`worker.ts`); this module owns file
|
|
9
|
+
* discovery, diffing, and the fail-open-per-file bookkeeping (a corrupt or
|
|
10
|
+
* oversized file is skipped and logged; if a previous good rule was loaded
|
|
11
|
+
* from that same file, it is retained rather than dropped).
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
/** Ported from `local_loader.rs`'s `MAX_RULE_FILE_BYTES`. */
|
|
16
|
+
export declare const MAX_RULE_FILE_BYTES: number;
|
|
17
|
+
/** Ported from `local_loader.rs`'s `DEFAULT_PRIORITY`. */
|
|
18
|
+
export declare const DEFAULT_PRIORITY = 100;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the local rules directory: `INTUTIC_WASM_DIR` env var, then the
|
|
21
|
+
* caller-supplied config override, then `~/.intutic/wasm` — same precedence
|
|
22
|
+
* order as `local_loader.rs`'s `resolve_local_dir` (confirmed by its own
|
|
23
|
+
* `resolve_dir_precedence` test: env wins over config, config wins over the
|
|
24
|
+
* home default).
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveWasmDir(configOverride?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Parse `NN_name.wasm` into `{ priority, name }`. Ported from
|
|
29
|
+
* `local_loader.rs`'s `parse_priority`, including its edge cases:
|
|
30
|
+
* `10_block-prod-db.wasm` -> `{10, "block-prod-db"}`; `my-rule.wasm` ->
|
|
31
|
+
* `{100, "my-rule"}`; a non-numeric or empty prefix (`block_prod.wasm`,
|
|
32
|
+
* `_hidden.wasm`) also defaults to priority 100 with the whole stem as the name.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parsePriority(fileName: string): {
|
|
35
|
+
priority: number;
|
|
36
|
+
name: string;
|
|
37
|
+
};
|
|
38
|
+
export interface FileSignature {
|
|
39
|
+
mtimeMs: number;
|
|
40
|
+
size: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `(mtime, size)` signature of every `*.wasm` file in `dir`, keyed by full
|
|
44
|
+
* path. A missing directory returns an empty map (first-run UX, ported from
|
|
45
|
+
* `scan_signatures`'s own `NotFound` handling) — any other I/O error
|
|
46
|
+
* propagates so the caller can retain the previously loaded rules instead of
|
|
47
|
+
* mistaking a transient EACCES/EIO for "every rule was deleted."
|
|
48
|
+
*/
|
|
49
|
+
export declare function scanSignatures(dir: string): Promise<Map<string, FileSignature>>;
|
|
50
|
+
export interface LoadedRuleMeta {
|
|
51
|
+
/** `local:<file name>` — same convention `local_loader.rs` uses. */
|
|
52
|
+
ruleId: string;
|
|
53
|
+
fileName: string;
|
|
54
|
+
name: string;
|
|
55
|
+
priority: number;
|
|
56
|
+
readsReferencedFiles: boolean;
|
|
57
|
+
/** Cached raw bytes, so a worker respawn (wasm/runner.ts) can resend every
|
|
58
|
+
* currently-loaded rule without re-reading the directory from disk. */
|
|
59
|
+
bytes: Uint8Array;
|
|
60
|
+
}
|
|
61
|
+
export type CompileOutcome = {
|
|
62
|
+
ok: true;
|
|
63
|
+
readsReferencedFiles: boolean;
|
|
64
|
+
} | {
|
|
65
|
+
ok: false;
|
|
66
|
+
error: string;
|
|
67
|
+
unsupportedImports?: string[];
|
|
68
|
+
};
|
|
69
|
+
/** What `WasmLoader` needs from the worker to load/unload a rule — implemented by `WasmRunner`. */
|
|
70
|
+
export interface CompileBridge {
|
|
71
|
+
compile(ruleId: string, bytes: Uint8Array): Promise<CompileOutcome>;
|
|
72
|
+
remove(ruleId: string): void;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Tracks the local rules directory's current state: which files exist, their
|
|
76
|
+
* change signatures, and the rule metadata successfully compiled from each.
|
|
77
|
+
* Fail-open per file — see this module's doc comment.
|
|
78
|
+
*/
|
|
79
|
+
export declare class WasmLoader {
|
|
80
|
+
private readonly dir;
|
|
81
|
+
private signatures;
|
|
82
|
+
private loaded;
|
|
83
|
+
constructor(dir: string);
|
|
84
|
+
getDir(): string;
|
|
85
|
+
/** Currently loaded rules, priority order (lower runs first), ties in file-scan order. */
|
|
86
|
+
getRules(): LoadedRuleMeta[];
|
|
87
|
+
/**
|
|
88
|
+
* Rescans the directory and (re)compiles anything changed via `bridge`.
|
|
89
|
+
* `force` reloads every current file unconditionally (used by the
|
|
90
|
+
* policy-tick hook after a worker respawn, where the worker's own module
|
|
91
|
+
* cache was lost and must be rebuilt even though nothing on disk moved).
|
|
92
|
+
*/
|
|
93
|
+
rescan(bridge: CompileBridge, force?: boolean): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/wasm/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,QAAmB,CAAA;AAEnD,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAEnC;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAK9D;AAOD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAWlF;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAsBrF;AAWD,MAAM,WAAW,cAAc;IAC7B,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,EAAE,OAAO,CAAA;IAC7B;4EACwE;IACxE,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,oBAAoB,EAAE,OAAO,CAAA;CAAE,GAC3C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAA;AAE/D,mGAAmG;AACnG,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IACnE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED;;;;GAIG;AACH,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,GAAG;IAHhC,OAAO,CAAC,UAAU,CAAmC;IACrD,OAAO,CAAC,MAAM,CAAoC;gBAErB,GAAG,EAAE,MAAM;IAExC,MAAM,IAAI,MAAM;IAIhB,0FAA0F;IAC1F,QAAQ,IAAI,cAAc,EAAE;IAI5B;;;;;OAKG;IACG,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CA+ElE"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wasm/loader.ts — `~/.intutic/wasm/` directory loader.
|
|
3
|
+
*
|
|
4
|
+
* Direct TypeScript port of `packages/proxy/src/wasm/local_loader.rs`'s
|
|
5
|
+
* directory-resolution order, `NN_name.wasm` priority parsing, the 10MB
|
|
6
|
+
* per-file size cap, and mtime+size-based change detection — confirmed by
|
|
7
|
+
* reading that file, not guessed. Compilation and import validation
|
|
8
|
+
* themselves happen in the worker (`worker.ts`); this module owns file
|
|
9
|
+
* discovery, diffing, and the fail-open-per-file bookkeeping (a corrupt or
|
|
10
|
+
* oversized file is skipped and logged; if a previous good rule was loaded
|
|
11
|
+
* from that same file, it is retained rather than dropped).
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import * as node_fs from 'node:fs/promises';
|
|
16
|
+
import * as node_os from 'node:os';
|
|
17
|
+
import * as node_path from 'node:path';
|
|
18
|
+
import { createStderrLogger as createLogger } from '../stderrLog.js';
|
|
19
|
+
const log = createLogger('mcp-proxy-wasm-loader');
|
|
20
|
+
/** Ported from `local_loader.rs`'s `MAX_RULE_FILE_BYTES`. */
|
|
21
|
+
export const MAX_RULE_FILE_BYTES = 10 * 1024 * 1024;
|
|
22
|
+
/** Ported from `local_loader.rs`'s `DEFAULT_PRIORITY`. */
|
|
23
|
+
export const DEFAULT_PRIORITY = 100;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the local rules directory: `INTUTIC_WASM_DIR` env var, then the
|
|
26
|
+
* caller-supplied config override, then `~/.intutic/wasm` — same precedence
|
|
27
|
+
* order as `local_loader.rs`'s `resolve_local_dir` (confirmed by its own
|
|
28
|
+
* `resolve_dir_precedence` test: env wins over config, config wins over the
|
|
29
|
+
* home default).
|
|
30
|
+
*/
|
|
31
|
+
export function resolveWasmDir(configOverride) {
|
|
32
|
+
const envDir = process.env['INTUTIC_WASM_DIR'];
|
|
33
|
+
if (envDir)
|
|
34
|
+
return expandHome(envDir);
|
|
35
|
+
if (configOverride)
|
|
36
|
+
return expandHome(configOverride);
|
|
37
|
+
return node_path.join(node_os.homedir(), '.intutic', 'wasm');
|
|
38
|
+
}
|
|
39
|
+
function expandHome(raw) {
|
|
40
|
+
if (raw.startsWith('~/'))
|
|
41
|
+
return node_path.join(node_os.homedir(), raw.slice(2));
|
|
42
|
+
return raw;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse `NN_name.wasm` into `{ priority, name }`. Ported from
|
|
46
|
+
* `local_loader.rs`'s `parse_priority`, including its edge cases:
|
|
47
|
+
* `10_block-prod-db.wasm` -> `{10, "block-prod-db"}`; `my-rule.wasm` ->
|
|
48
|
+
* `{100, "my-rule"}`; a non-numeric or empty prefix (`block_prod.wasm`,
|
|
49
|
+
* `_hidden.wasm`) also defaults to priority 100 with the whole stem as the name.
|
|
50
|
+
*/
|
|
51
|
+
export function parsePriority(fileName) {
|
|
52
|
+
const stem = fileName.endsWith('.wasm') ? fileName.slice(0, -'.wasm'.length) : fileName;
|
|
53
|
+
const idx = stem.indexOf('_');
|
|
54
|
+
if (idx > 0 && idx < stem.length - 1) {
|
|
55
|
+
const prefix = stem.slice(0, idx);
|
|
56
|
+
const rest = stem.slice(idx + 1);
|
|
57
|
+
if (/^\d+$/.test(prefix)) {
|
|
58
|
+
return { priority: Number.parseInt(prefix, 10), name: rest };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { priority: DEFAULT_PRIORITY, name: stem };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `(mtime, size)` signature of every `*.wasm` file in `dir`, keyed by full
|
|
65
|
+
* path. A missing directory returns an empty map (first-run UX, ported from
|
|
66
|
+
* `scan_signatures`'s own `NotFound` handling) — any other I/O error
|
|
67
|
+
* propagates so the caller can retain the previously loaded rules instead of
|
|
68
|
+
* mistaking a transient EACCES/EIO for "every rule was deleted."
|
|
69
|
+
*/
|
|
70
|
+
export async function scanSignatures(dir) {
|
|
71
|
+
const signatures = new Map();
|
|
72
|
+
let entries;
|
|
73
|
+
try {
|
|
74
|
+
entries = await node_fs.readdir(dir, { withFileTypes: true });
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
if (err.code === 'ENOENT')
|
|
78
|
+
return signatures;
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
if (!entry.isFile() || !entry.name.endsWith('.wasm'))
|
|
83
|
+
continue;
|
|
84
|
+
const full = node_path.join(dir, entry.name);
|
|
85
|
+
try {
|
|
86
|
+
const stat = await node_fs.stat(full);
|
|
87
|
+
signatures.set(full, { mtimeMs: stat.mtimeMs, size: stat.size });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Vanished between readdir and stat (race with a concurrent
|
|
91
|
+
// write/delete) — skip it this pass; the next rescan sees whatever
|
|
92
|
+
// state settles.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return signatures;
|
|
96
|
+
}
|
|
97
|
+
function signaturesEqual(a, b) {
|
|
98
|
+
if (a.size !== b.size)
|
|
99
|
+
return false;
|
|
100
|
+
for (const [key, sig] of a) {
|
|
101
|
+
const other = b.get(key);
|
|
102
|
+
if (!other || other.mtimeMs !== sig.mtimeMs || other.size !== sig.size)
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Tracks the local rules directory's current state: which files exist, their
|
|
109
|
+
* change signatures, and the rule metadata successfully compiled from each.
|
|
110
|
+
* Fail-open per file — see this module's doc comment.
|
|
111
|
+
*/
|
|
112
|
+
export class WasmLoader {
|
|
113
|
+
dir;
|
|
114
|
+
signatures = new Map();
|
|
115
|
+
loaded = new Map(); // keyed by file path
|
|
116
|
+
constructor(dir) {
|
|
117
|
+
this.dir = dir;
|
|
118
|
+
}
|
|
119
|
+
getDir() {
|
|
120
|
+
return this.dir;
|
|
121
|
+
}
|
|
122
|
+
/** Currently loaded rules, priority order (lower runs first), ties in file-scan order. */
|
|
123
|
+
getRules() {
|
|
124
|
+
return [...this.loaded.values()].sort((a, b) => a.priority - b.priority);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Rescans the directory and (re)compiles anything changed via `bridge`.
|
|
128
|
+
* `force` reloads every current file unconditionally (used by the
|
|
129
|
+
* policy-tick hook after a worker respawn, where the worker's own module
|
|
130
|
+
* cache was lost and must be rebuilt even though nothing on disk moved).
|
|
131
|
+
*/
|
|
132
|
+
async rescan(bridge, force = false) {
|
|
133
|
+
let newSignatures;
|
|
134
|
+
try {
|
|
135
|
+
newSignatures = await scanSignatures(this.dir);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
log.warn({ action: 'wasm_scan_error', dir: this.dir, err: err.message }, 'local WASM rule directory scan failed — keeping previously loaded rules');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!force && signaturesEqual(newSignatures, this.signatures))
|
|
142
|
+
return;
|
|
143
|
+
// Removed files.
|
|
144
|
+
for (const filePath of [...this.loaded.keys()]) {
|
|
145
|
+
if (!newSignatures.has(filePath)) {
|
|
146
|
+
const meta = this.loaded.get(filePath);
|
|
147
|
+
if (meta)
|
|
148
|
+
bridge.remove(meta.ruleId);
|
|
149
|
+
this.loaded.delete(filePath);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// New or changed files.
|
|
153
|
+
for (const [filePath, sig] of newSignatures) {
|
|
154
|
+
const previousSig = this.signatures.get(filePath);
|
|
155
|
+
const changed = force || !previousSig || previousSig.mtimeMs !== sig.mtimeMs || previousSig.size !== sig.size;
|
|
156
|
+
if (!changed)
|
|
157
|
+
continue;
|
|
158
|
+
const fileName = node_path.basename(filePath);
|
|
159
|
+
const ruleId = `local:${fileName}`;
|
|
160
|
+
if (sig.size > MAX_RULE_FILE_BYTES) {
|
|
161
|
+
log.warn({ action: 'wasm_rule_too_large', file: filePath, sizeBytes: sig.size, capBytes: MAX_RULE_FILE_BYTES }, 'local WASM rule exceeds size cap — skipped');
|
|
162
|
+
continue; // fail-open-per-file: whatever was in `this.loaded` for this path (if anything) stays
|
|
163
|
+
}
|
|
164
|
+
let bytes;
|
|
165
|
+
try {
|
|
166
|
+
bytes = await node_fs.readFile(filePath);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
log.warn({ action: 'wasm_rule_read_error', file: filePath, err: err.message }, 'local WASM rule failed to read — retaining previous version if any');
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const result = await bridge.compile(ruleId, bytes);
|
|
173
|
+
if (result.ok) {
|
|
174
|
+
const { priority, name } = parsePriority(fileName);
|
|
175
|
+
this.loaded.set(filePath, {
|
|
176
|
+
ruleId,
|
|
177
|
+
fileName,
|
|
178
|
+
name,
|
|
179
|
+
priority,
|
|
180
|
+
readsReferencedFiles: result.readsReferencedFiles,
|
|
181
|
+
bytes,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
log.warn({
|
|
186
|
+
action: 'wasm_rule_load_error',
|
|
187
|
+
file: filePath,
|
|
188
|
+
error: result.error,
|
|
189
|
+
unsupportedImports: result.unsupportedImports,
|
|
190
|
+
}, 'local WASM rule failed to load — retaining previous version if any');
|
|
191
|
+
// Fail-open-per-file: do NOT touch `this.loaded.get(filePath)` — a
|
|
192
|
+
// previously good module (if any) for this exact path stays active.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
this.signatures = newSignatures;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/wasm/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wasm/referencedFiles.ts — The MCP-side resolver behind the
|
|
3
|
+
* `read_referenced_file` host import (TD-441). A 1:1 port of
|
|
4
|
+
* `packages/proxy/src/wasm/referenced_files.rs`: same constants, same error
|
|
5
|
+
* codes, same candidate-token rules, same six guards, same realpath
|
|
6
|
+
* confinement. Where the two differ it is stated at the site.
|
|
7
|
+
*
|
|
8
|
+
* ## The design, in one paragraph
|
|
9
|
+
*
|
|
10
|
+
* A rule never names a file. The proxy derives the candidate paths from the
|
|
11
|
+
* CURRENT tool call's arguments (structured path keys and the shell words of
|
|
12
|
+
* a command), keeps the manifest-shaped ones, resolves each under the
|
|
13
|
+
* operator-configured root with every symlink followed, and reads the ones
|
|
14
|
+
* that survive — all BEFORE the sandbox exists. The guest then passes a path
|
|
15
|
+
* as a lookup key into that fixed table. There is no code path from a guest
|
|
16
|
+
* string to `open(2)`.
|
|
17
|
+
*
|
|
18
|
+
* ## Guards (numbered as in the Rust module)
|
|
19
|
+
*
|
|
20
|
+
* 1. Only paths the tool call literally named (no glob expansion).
|
|
21
|
+
* 2. Only manifest extensions.
|
|
22
|
+
* 3. No `..` component; no platform prefix.
|
|
23
|
+
* 4. Realpath must stay under the realpath'd root (symlink escape covered).
|
|
24
|
+
* 5. Regular files only.
|
|
25
|
+
* 6. Size-capped, checked on the stat AND on the read.
|
|
26
|
+
*
|
|
27
|
+
* ## Divergence from the Rust module
|
|
28
|
+
*
|
|
29
|
+
* Reads are `fs/promises` (async) rather than a blocking pool — the runner
|
|
30
|
+
* awaits them once per evaluation, before any rule runs, so the per-rule
|
|
31
|
+
* deadline still covers only guest execution. The residual TOCTOU between
|
|
32
|
+
* `realpath` and `open` is the same one `referenced_files.rs` documents;
|
|
33
|
+
* closing it needs `openat2(RESOLVE_BENEATH)`, which Node does not expose.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
/** Largest file the host will hand to a rule. `referenced_files.rs`: 256 KiB. */
|
|
38
|
+
export declare const MAX_REFERENCED_FILE_BYTES: number;
|
|
39
|
+
/** Candidate paths considered per evaluation. */
|
|
40
|
+
export declare const MAX_REFERENCED_FILES = 8;
|
|
41
|
+
/** Longest path a guest may ask for. */
|
|
42
|
+
export declare const MAX_GUEST_PATH_BYTES = 4096;
|
|
43
|
+
export declare const ERR_BAD_ARGS = -1;
|
|
44
|
+
export declare const ERR_REFUSED = -2;
|
|
45
|
+
export declare const ERR_NOT_FOUND = -3;
|
|
46
|
+
export declare const ERR_TOO_LARGE = -4;
|
|
47
|
+
export declare const ERR_BUFFER_TOO_SMALL = -5;
|
|
48
|
+
export declare const ERR_BUDGET = -6;
|
|
49
|
+
/** Host calls one evaluation may make, charged before argument validation. */
|
|
50
|
+
export declare const MAX_READS_PER_EVALUATION = 64;
|
|
51
|
+
/** The operator-configured root. Unset means the capability is off. */
|
|
52
|
+
export declare const MANIFEST_ROOT_ENV = "INTUTIC_WASM_MANIFEST_ROOT";
|
|
53
|
+
/** One candidate's fate. `content` carries bytes; everything else a code. */
|
|
54
|
+
export type Outcome = {
|
|
55
|
+
kind: 'content';
|
|
56
|
+
bytes: Uint8Array;
|
|
57
|
+
} | {
|
|
58
|
+
kind: 'refused';
|
|
59
|
+
why: string;
|
|
60
|
+
} | {
|
|
61
|
+
kind: 'not_found';
|
|
62
|
+
} | {
|
|
63
|
+
kind: 'too_large';
|
|
64
|
+
size: number;
|
|
65
|
+
};
|
|
66
|
+
/** The structured-clone-safe wire form the runner posts to the worker. */
|
|
67
|
+
export type ReferencedFilesTable = Array<[string, Outcome]>;
|
|
68
|
+
export interface ToolCallLike {
|
|
69
|
+
name: string;
|
|
70
|
+
arguments: unknown;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The pre-read table one evaluation sees. Immutable after construction; the
|
|
74
|
+
* worker rebuilds it from `toTable()` on its side of `postMessage`.
|
|
75
|
+
*/
|
|
76
|
+
export declare class ReferencedFiles {
|
|
77
|
+
private readonly entries;
|
|
78
|
+
private constructor();
|
|
79
|
+
static empty(): ReferencedFiles;
|
|
80
|
+
static fromTable(table: ReferencedFilesTable | undefined): ReferencedFiles;
|
|
81
|
+
toTable(): ReferencedFilesTable;
|
|
82
|
+
isEmpty(): boolean;
|
|
83
|
+
readableCount(): number;
|
|
84
|
+
/** The bytes for `requested`, or the error code the guest gets. */
|
|
85
|
+
lookup(requested: string): {
|
|
86
|
+
ok: true;
|
|
87
|
+
bytes: Uint8Array;
|
|
88
|
+
} | {
|
|
89
|
+
ok: false;
|
|
90
|
+
code: number;
|
|
91
|
+
};
|
|
92
|
+
/** Operator-facing reason. Never handed to the guest. */
|
|
93
|
+
refusalReason(requested: string): string;
|
|
94
|
+
/** Debug rendering: states, never contents. */
|
|
95
|
+
describe(): string;
|
|
96
|
+
}
|
|
97
|
+
/** The configured root, `~/` expanded. `undefined` keeps the capability off. */
|
|
98
|
+
export declare function resolveRoot(env?: NodeJS.ProcessEnv): string | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Guard 1 and 2: every manifest-shaped path the tool calls literally name,
|
|
101
|
+
* deduped, capped at `MAX_REFERENCED_FILES`, in first-seen order.
|
|
102
|
+
*/
|
|
103
|
+
export declare function candidateTokens(toolCalls: readonly ToolCallLike[]): string[];
|
|
104
|
+
/**
|
|
105
|
+
* Split a command into shell words. Quotes group; whitespace and the
|
|
106
|
+
* metacharacters that end a word separate. Deliberately does NOT expand
|
|
107
|
+
* globs, variables or command substitution — an unexpanded `*.yaml` simply
|
|
108
|
+
* fails to resolve and the guest is told so.
|
|
109
|
+
*/
|
|
110
|
+
export declare function shellTokens(command: string): string[];
|
|
111
|
+
/** Resolve and read every candidate under `root`. Async, so the runner awaits it once per evaluation. */
|
|
112
|
+
export declare function readTokens(tokens: readonly string[], root: string): Promise<ReferencedFiles>;
|
|
113
|
+
/** Convenience for callers with tool calls in hand. */
|
|
114
|
+
export declare function prefetch(toolCalls: readonly ToolCallLike[], root: string): Promise<ReferencedFiles>;
|
|
115
|
+
//# sourceMappingURL=referencedFiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"referencedFiles.d.ts","sourceRoot":"","sources":["../../src/wasm/referencedFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAUH,iFAAiF;AACjF,eAAO,MAAM,yBAAyB,QAAa,CAAA;AACnD,iDAAiD;AACjD,eAAO,MAAM,oBAAoB,IAAI,CAAA;AACrC,wCAAwC;AACxC,eAAO,MAAM,oBAAoB,OAAO,CAAA;AAMxC,eAAO,MAAM,YAAY,KAAK,CAAA;AAC9B,eAAO,MAAM,WAAW,KAAK,CAAA;AAC7B,eAAO,MAAM,aAAa,KAAK,CAAA;AAC/B,eAAO,MAAM,aAAa,KAAK,CAAA;AAC/B,eAAO,MAAM,oBAAoB,KAAK,CAAA;AACtC,eAAO,MAAM,UAAU,KAAK,CAAA;AAE5B,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAE1C,uEAAuE;AACvE,eAAO,MAAM,iBAAiB,+BAA+B,CAAA;AAO7D,6EAA6E;AAC7E,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC,0EAA0E;AAC1E,MAAM,MAAM,oBAAoB,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAE3D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;CACnB;AAED;;;GAGG;AACH,qBAAa,eAAe;IACN,OAAO,CAAC,QAAQ,CAAC,OAAO;IAA5C,OAAO;IAEP,MAAM,CAAC,KAAK,IAAI,eAAe;IAI/B,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,oBAAoB,GAAG,SAAS,GAAG,eAAe;IAI1E,OAAO,IAAI,oBAAoB;IAI/B,OAAO,IAAI,OAAO;IAIlB,aAAa,IAAI,MAAM;IAIvB,mEAAmE;IACnE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAgBxF,yDAAyD;IACzD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAgBxC,+CAA+C;IAC/C,QAAQ,IAAI,MAAM;CAgBnB;AAED,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAKpF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,EAAE,CAoB5E;AAsBD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BrD;AAED,yGAAyG;AACzG,wBAAsB,UAAU,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA2ClG;AAeD,uDAAuD;AACvD,wBAAgB,QAAQ,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAEnG"}
|