@expressots/studio-agent 4.0.0-preview.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.
Files changed (86) hide show
  1. package/README.md +143 -0
  2. package/dist/agent.d.ts +127 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +1031 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/discovery/index.d.ts +2 -0
  7. package/dist/discovery/index.d.ts.map +1 -0
  8. package/dist/discovery/index.js +2 -0
  9. package/dist/discovery/index.js.map +1 -0
  10. package/dist/discovery/route-scanner.d.ts +35 -0
  11. package/dist/discovery/route-scanner.d.ts.map +1 -0
  12. package/dist/discovery/route-scanner.js +385 -0
  13. package/dist/discovery/route-scanner.js.map +1 -0
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +15 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/instrumentation/index.d.ts +2 -0
  19. package/dist/instrumentation/index.d.ts.map +1 -0
  20. package/dist/instrumentation/index.js +2 -0
  21. package/dist/instrumentation/index.js.map +1 -0
  22. package/dist/instrumentation/tracer.d.ts +40 -0
  23. package/dist/instrumentation/tracer.d.ts.map +1 -0
  24. package/dist/instrumentation/tracer.js +190 -0
  25. package/dist/instrumentation/tracer.js.map +1 -0
  26. package/dist/introspection/container-introspector.d.ts +81 -0
  27. package/dist/introspection/container-introspector.d.ts.map +1 -0
  28. package/dist/introspection/container-introspector.js +251 -0
  29. package/dist/introspection/container-introspector.js.map +1 -0
  30. package/dist/logging/log-capture.d.ts +58 -0
  31. package/dist/logging/log-capture.d.ts.map +1 -0
  32. package/dist/logging/log-capture.js +184 -0
  33. package/dist/logging/log-capture.js.map +1 -0
  34. package/dist/recording/index.d.ts +2 -0
  35. package/dist/recording/index.d.ts.map +1 -0
  36. package/dist/recording/index.js +2 -0
  37. package/dist/recording/index.js.map +1 -0
  38. package/dist/recording/request-recorder.d.ts +43 -0
  39. package/dist/recording/request-recorder.d.ts.map +1 -0
  40. package/dist/recording/request-recorder.js +373 -0
  41. package/dist/recording/request-recorder.js.map +1 -0
  42. package/dist/security/fix-resolver.d.ts +40 -0
  43. package/dist/security/fix-resolver.d.ts.map +1 -0
  44. package/dist/security/fix-resolver.js +283 -0
  45. package/dist/security/fix-resolver.js.map +1 -0
  46. package/dist/security/fix-runner.d.ts +60 -0
  47. package/dist/security/fix-runner.d.ts.map +1 -0
  48. package/dist/security/fix-runner.js +188 -0
  49. package/dist/security/fix-runner.js.map +1 -0
  50. package/dist/security/index.d.ts +140 -0
  51. package/dist/security/index.d.ts.map +1 -0
  52. package/dist/security/index.js +460 -0
  53. package/dist/security/index.js.map +1 -0
  54. package/dist/security/lockfile-graph.d.ts +69 -0
  55. package/dist/security/lockfile-graph.d.ts.map +1 -0
  56. package/dist/security/lockfile-graph.js +245 -0
  57. package/dist/security/lockfile-graph.js.map +1 -0
  58. package/dist/security/npm-audit.d.ts +67 -0
  59. package/dist/security/npm-audit.d.ts.map +1 -0
  60. package/dist/security/npm-audit.js +320 -0
  61. package/dist/security/npm-audit.js.map +1 -0
  62. package/dist/security/osv-cache.d.ts +51 -0
  63. package/dist/security/osv-cache.d.ts.map +1 -0
  64. package/dist/security/osv-cache.js +99 -0
  65. package/dist/security/osv-cache.js.map +1 -0
  66. package/dist/security/osv-client.d.ts +47 -0
  67. package/dist/security/osv-client.d.ts.map +1 -0
  68. package/dist/security/osv-client.js +247 -0
  69. package/dist/security/osv-client.js.map +1 -0
  70. package/dist/security/posture-analyzer.d.ts +44 -0
  71. package/dist/security/posture-analyzer.d.ts.map +1 -0
  72. package/dist/security/posture-analyzer.js +397 -0
  73. package/dist/security/posture-analyzer.js.map +1 -0
  74. package/dist/security/reachability.d.ts +59 -0
  75. package/dist/security/reachability.d.ts.map +1 -0
  76. package/dist/security/reachability.js +302 -0
  77. package/dist/security/reachability.js.map +1 -0
  78. package/dist/security/score.d.ts +36 -0
  79. package/dist/security/score.d.ts.map +1 -0
  80. package/dist/security/score.js +94 -0
  81. package/dist/security/score.js.map +1 -0
  82. package/dist/types/index.d.ts +587 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +14 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/package.json +75 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * In-memory model of `package-lock.json` v2/v3.
3
+ *
4
+ * The lockfile is the ground truth for *what's actually installed* in
5
+ * the user's `node_modules`. npm audit's output is a flat list keyed by
6
+ * package name and a partial `effects` array — useful, but it doesn't
7
+ * give us the full transitive chain or the direct dependency that
8
+ * brought a vulnerable package in. We need that for two features:
9
+ *
10
+ * 1. Root-cause routing — show "lodash@4.17.10 reached via
11
+ * express-session → fix by upgrading express-session 1.17.0 → 1.17.3".
12
+ * 2. Fix-group construction — collapse N CVEs that all resolve via
13
+ * one upgrade into a single "Upgrade X — fixes N advisories" row.
14
+ *
15
+ * The graph is intentionally minimal: we parse the lockfile once,
16
+ * resolve each "node_modules/<path>" entry's dependencies against npm's
17
+ * hoist rules (nearest ancestor wins), and store the forward edges. We
18
+ * do *not* try to invert it — root-cause queries do a small BFS from
19
+ * the project's direct deps, which is cheap enough at any realistic
20
+ * project size (thousands of packages, milliseconds).
21
+ */
22
+ import type { RootCause } from '../types/index.js';
23
+ /**
24
+ * Loaded lockfile graph. A `null` instance means we couldn't parse the
25
+ * lockfile (no file, unsupported version, etc.) — callers should treat
26
+ * that as "no transitive resolution available" and skip enrichment.
27
+ */
28
+ export declare class LockfileGraph {
29
+ private readonly root;
30
+ private readonly nodes;
31
+ /** Adjacency cache: node-key → array of resolved-child node-keys. */
32
+ private readonly childCache;
33
+ private constructor();
34
+ /**
35
+ * Parse the lockfile at `cwd`. Returns `null` for missing /
36
+ * unparseable lockfiles — root-cause routing then no-ops, which is
37
+ * better than a hard failure.
38
+ */
39
+ static load(cwd: string): LockfileGraph | null;
40
+ /** True when `pkg` is declared as a direct dep / devDep / optionalDep. */
41
+ isDirect(pkg: string): boolean;
42
+ /** Version of a direct dep, or undefined if it's not a direct dep. */
43
+ directVersion(pkg: string): string | undefined;
44
+ /** Returns the set of every distinct package name in the graph. */
45
+ packageNames(): Set<string>;
46
+ /**
47
+ * Find the shortest chain of dependency names from any direct dep to
48
+ * an installation of `vulnerablePkg`.
49
+ *
50
+ * Returns `null` when:
51
+ * - the vulnerable package isn't installed at all, or
52
+ * - the BFS exhausts the graph without finding a route (shouldn't
53
+ * happen if the lockfile is internally consistent).
54
+ */
55
+ findRootCause(vulnerablePkg: string): RootCause | null;
56
+ /**
57
+ * Resolve `parentKey`'s declared dep `depName` to the lockfile entry
58
+ * that actually fulfils it. npm's hoist rule is "nearest ancestor with
59
+ * a matching `node_modules/<name>` wins" — we walk back up by trimming
60
+ * the path one segment at a time until we hit a match.
61
+ */
62
+ private resolveDep;
63
+ /**
64
+ * Find *some* installation of `pkg`. We prefer a hoisted top-level
65
+ * one (most common); falling back to the first nested copy found.
66
+ */
67
+ private findInstalledNode;
68
+ }
69
+ //# sourceMappingURL=lockfile-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile-graph.d.ts","sourceRoot":"","sources":["../../src/security/lockfile-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAqBnD;;;;GAIG;AACH,qBAAa,aAAa;IAKJ,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAE1D,OAAO;IAIP;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IA4D9C,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI9B,sEAAsE;IACtE,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK9C,mEAAmE;IACnE,YAAY,IAAI,GAAG,CAAC,MAAM,CAAC;IAQ3B;;;;;;;;OAQG;IACH,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IA8DtD;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAyBlB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;CAQ1B"}
@@ -0,0 +1,245 @@
1
+ /**
2
+ * In-memory model of `package-lock.json` v2/v3.
3
+ *
4
+ * The lockfile is the ground truth for *what's actually installed* in
5
+ * the user's `node_modules`. npm audit's output is a flat list keyed by
6
+ * package name and a partial `effects` array — useful, but it doesn't
7
+ * give us the full transitive chain or the direct dependency that
8
+ * brought a vulnerable package in. We need that for two features:
9
+ *
10
+ * 1. Root-cause routing — show "lodash@4.17.10 reached via
11
+ * express-session → fix by upgrading express-session 1.17.0 → 1.17.3".
12
+ * 2. Fix-group construction — collapse N CVEs that all resolve via
13
+ * one upgrade into a single "Upgrade X — fixes N advisories" row.
14
+ *
15
+ * The graph is intentionally minimal: we parse the lockfile once,
16
+ * resolve each "node_modules/<path>" entry's dependencies against npm's
17
+ * hoist rules (nearest ancestor wins), and store the forward edges. We
18
+ * do *not* try to invert it — root-cause queries do a small BFS from
19
+ * the project's direct deps, which is cheap enough at any realistic
20
+ * project size (thousands of packages, milliseconds).
21
+ */
22
+ import * as fs from 'node:fs';
23
+ import * as path from 'node:path';
24
+ /**
25
+ * Loaded lockfile graph. A `null` instance means we couldn't parse the
26
+ * lockfile (no file, unsupported version, etc.) — callers should treat
27
+ * that as "no transitive resolution available" and skip enrichment.
28
+ */
29
+ export class LockfileGraph {
30
+ root;
31
+ nodes = new Map();
32
+ /** Adjacency cache: node-key → array of resolved-child node-keys. */
33
+ childCache = new Map();
34
+ constructor(root) {
35
+ this.root = root;
36
+ this.nodes.set(root.key, root);
37
+ }
38
+ /**
39
+ * Parse the lockfile at `cwd`. Returns `null` for missing /
40
+ * unparseable lockfiles — root-cause routing then no-ops, which is
41
+ * better than a hard failure.
42
+ */
43
+ static load(cwd) {
44
+ const file = path.join(cwd, 'package-lock.json');
45
+ let raw;
46
+ try {
47
+ raw = fs.readFileSync(file, 'utf-8');
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ let parsed;
53
+ try {
54
+ parsed = JSON.parse(raw);
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ // We only handle lockfile v2/v3 (which share the `packages` map).
60
+ // npm v7+ defaults to v2; npm v9+ to v3. Both are universally
61
+ // supported by the Node LTS releases we care about.
62
+ if (!parsed.packages || typeof parsed.packages !== 'object') {
63
+ return null;
64
+ }
65
+ const rootEntry = parsed.packages[''];
66
+ const root = {
67
+ key: '',
68
+ name: parsed.name ?? rootEntry?.name ?? '<project>',
69
+ version: parsed.version ?? rootEntry?.version ?? '0.0.0',
70
+ deps: {
71
+ ...(rootEntry?.dependencies ?? {}),
72
+ ...(rootEntry?.devDependencies ?? {}),
73
+ ...(rootEntry?.optionalDependencies ?? {}),
74
+ },
75
+ isRoot: true,
76
+ };
77
+ const graph = new LockfileGraph(root);
78
+ for (const [key, entry] of Object.entries(parsed.packages)) {
79
+ if (key === '')
80
+ continue;
81
+ // The lockfile key encodes the install path; the package name is
82
+ // the last segment after the final `node_modules/`. Take that
83
+ // rather than trust `entry.name` (which v2 sometimes omits).
84
+ const name = nameFromKey(key) ?? entry?.name;
85
+ if (!name)
86
+ continue;
87
+ graph.nodes.set(key, {
88
+ key,
89
+ name,
90
+ version: entry?.version ?? 'unknown',
91
+ deps: {
92
+ ...(entry?.dependencies ?? {}),
93
+ ...(entry?.optionalDependencies ?? {}),
94
+ },
95
+ isRoot: false,
96
+ });
97
+ }
98
+ return graph;
99
+ }
100
+ /** True when `pkg` is declared as a direct dep / devDep / optionalDep. */
101
+ isDirect(pkg) {
102
+ return pkg in this.root.deps;
103
+ }
104
+ /** Version of a direct dep, or undefined if it's not a direct dep. */
105
+ directVersion(pkg) {
106
+ const child = this.resolveDep(this.root.key, pkg);
107
+ return child ? this.nodes.get(child)?.version : undefined;
108
+ }
109
+ /** Returns the set of every distinct package name in the graph. */
110
+ packageNames() {
111
+ const out = new Set();
112
+ for (const node of this.nodes.values()) {
113
+ if (!node.isRoot)
114
+ out.add(node.name);
115
+ }
116
+ return out;
117
+ }
118
+ /**
119
+ * Find the shortest chain of dependency names from any direct dep to
120
+ * an installation of `vulnerablePkg`.
121
+ *
122
+ * Returns `null` when:
123
+ * - the vulnerable package isn't installed at all, or
124
+ * - the BFS exhausts the graph without finding a route (shouldn't
125
+ * happen if the lockfile is internally consistent).
126
+ */
127
+ findRootCause(vulnerablePkg) {
128
+ const vulnNode = this.findInstalledNode(vulnerablePkg);
129
+ if (!vulnNode)
130
+ return null;
131
+ // Direct dep? No further work — the "chain" is just the package itself.
132
+ if (this.isDirect(vulnerablePkg)) {
133
+ return {
134
+ rootPackage: vulnerablePkg,
135
+ rootInstalledVersion: vulnNode.version,
136
+ chain: [vulnerablePkg],
137
+ isDirect: true,
138
+ };
139
+ }
140
+ // BFS from every direct dep, taking the first chain that reaches a
141
+ // node whose `name === vulnerablePkg`. We keep a parent map keyed
142
+ // by node-key so we can walk back to the root once we find a hit.
143
+ // Tracking visited *nodes* (by key) prevents cycles and keeps the
144
+ // search bounded by the lockfile size.
145
+ const parent = new Map(); // childKey → parentKey ('' marks a root entry)
146
+ const queue = [];
147
+ for (const directName of Object.keys(this.root.deps)) {
148
+ const childKey = this.resolveDep(this.root.key, directName);
149
+ if (!childKey || parent.has(childKey))
150
+ continue;
151
+ parent.set(childKey, null);
152
+ queue.push(childKey);
153
+ }
154
+ while (queue.length > 0) {
155
+ const curKey = queue.shift();
156
+ const node = this.nodes.get(curKey);
157
+ if (!node)
158
+ continue;
159
+ if (node.name === vulnerablePkg) {
160
+ const chain = [];
161
+ let cursor = curKey;
162
+ while (cursor !== null) {
163
+ const n = this.nodes.get(cursor);
164
+ if (n)
165
+ chain.unshift(n.name);
166
+ cursor = parent.get(cursor) ?? null;
167
+ }
168
+ const rootName = chain[0] ?? vulnerablePkg;
169
+ return {
170
+ rootPackage: rootName,
171
+ rootInstalledVersion: this.directVersion(rootName) ?? 'unknown',
172
+ chain,
173
+ isDirect: false,
174
+ };
175
+ }
176
+ for (const depName of Object.keys(node.deps)) {
177
+ const nextKey = this.resolveDep(curKey, depName);
178
+ if (!nextKey || parent.has(nextKey))
179
+ continue;
180
+ parent.set(nextKey, curKey);
181
+ queue.push(nextKey);
182
+ }
183
+ }
184
+ return null;
185
+ }
186
+ /**
187
+ * Resolve `parentKey`'s declared dep `depName` to the lockfile entry
188
+ * that actually fulfils it. npm's hoist rule is "nearest ancestor with
189
+ * a matching `node_modules/<name>` wins" — we walk back up by trimming
190
+ * the path one segment at a time until we hit a match.
191
+ */
192
+ resolveDep(parentKey, depName) {
193
+ const cacheKey = `${parentKey}::${depName}`;
194
+ const hit = this.childCache.get(cacheKey);
195
+ if (hit !== undefined)
196
+ return hit[0] ?? null;
197
+ // Direct attempt: `<parentKey>/node_modules/<depName>`.
198
+ let cursor = parentKey;
199
+ while (true) {
200
+ const candidate = cursor
201
+ ? `${cursor}/node_modules/${depName}`
202
+ : `node_modules/${depName}`;
203
+ if (this.nodes.has(candidate)) {
204
+ this.childCache.set(cacheKey, [candidate]);
205
+ return candidate;
206
+ }
207
+ if (!cursor)
208
+ break;
209
+ // Strip the last `node_modules/<x>` segment, if any.
210
+ const idx = cursor.lastIndexOf('/node_modules/');
211
+ cursor = idx === -1 ? '' : cursor.slice(0, idx);
212
+ }
213
+ this.childCache.set(cacheKey, []);
214
+ return null;
215
+ }
216
+ /**
217
+ * Find *some* installation of `pkg`. We prefer a hoisted top-level
218
+ * one (most common); falling back to the first nested copy found.
219
+ */
220
+ findInstalledNode(pkg) {
221
+ const top = this.nodes.get(`node_modules/${pkg}`);
222
+ if (top)
223
+ return top;
224
+ for (const node of this.nodes.values()) {
225
+ if (!node.isRoot && node.name === pkg)
226
+ return node;
227
+ }
228
+ return null;
229
+ }
230
+ }
231
+ /**
232
+ * Pull the package name from a lockfile key. Handles scoped packages
233
+ * (`node_modules/@scope/name`) and nested ones
234
+ * (`node_modules/foo/node_modules/@scope/bar`).
235
+ */
236
+ function nameFromKey(key) {
237
+ const idx = key.lastIndexOf('node_modules/');
238
+ if (idx === -1)
239
+ return null;
240
+ const tail = key.slice(idx + 'node_modules/'.length);
241
+ if (!tail)
242
+ return null;
243
+ return tail; // `@scope/name` or `name`
244
+ }
245
+ //# sourceMappingURL=lockfile-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile-graph.js","sourceRoot":"","sources":["../../src/security/lockfile-graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * `npm audit --json` runner.
3
+ *
4
+ * Spawns the host project's `npm audit` in a child process, captures
5
+ * stdout, and normalises the result into `DependencyFinding[]` so the
6
+ * agent doesn't have to know about npm's wire format anywhere else.
7
+ *
8
+ * Design constraints:
9
+ * - **Async only.** This runs inside the host's process; blocking the
10
+ * event loop with `execSync` would freeze the host server.
11
+ * - **Bounded.** We cap stdout at 16 MB and impose a 30 s wall timeout
12
+ * to keep a misbehaving npm install from hanging Studio forever.
13
+ * - **Defensive.** `npm audit` writes useful JSON to stdout even when
14
+ * it exits 1 (the convention is "exit 1 means vulns were found").
15
+ * We treat exit codes 0 and 1 as success.
16
+ *
17
+ * The runner is decoupled from the engine: it returns a structured
18
+ * result plus a `state` value the engine maps onto `scanState.audit`.
19
+ */
20
+ import type { DependencyFinding } from '../types/index.js';
21
+ /**
22
+ * Per-package raw `fixAvailable` data lifted from npm audit's report,
23
+ * indexed by package name. Used by the fix-resolver to decide whether
24
+ * a finding can be cleared by `npm audit fix`, `npm audit fix --force`,
25
+ * or a manual `npm install`.
26
+ *
27
+ * npm's `fixAvailable` is tri-modal:
28
+ *
29
+ * - `false` → no upstream fix exists.
30
+ * - `true` → `npm audit fix` can resolve it, but npm declines
31
+ * to commit to a specific target version (typical
32
+ * for transitive vulns where the root upgrade is
33
+ * `audit-fix`'s choice).
34
+ * - `{ name, version, isSemVerMajor }` → exact target.
35
+ *
36
+ * We keep all three cases distinct here so the fix-resolver can produce
37
+ * the right command for each. Kept separate from the public
38
+ * `DependencyFinding` shape so the agent doesn't leak npm's wire format
39
+ * into the WS protocol.
40
+ */
41
+ export type AuditFixAvailability = {
42
+ kind: 'specific';
43
+ name: string;
44
+ version: string;
45
+ isSemVerMajor: boolean;
46
+ } | {
47
+ kind: 'auto';
48
+ } | {
49
+ kind: 'none';
50
+ };
51
+ export interface NpmAuditResult {
52
+ state: 'ok' | 'error' | 'missing-lockfile';
53
+ /** Empty on `missing-lockfile` / `error`. */
54
+ findings: DependencyFinding[];
55
+ /** Raw `fixAvailable` info keyed by *vulnerable package name*. */
56
+ fixAvailability: Map<string, AuditFixAvailability>;
57
+ /** Short, user-facing error message when state === 'error'. */
58
+ error?: string;
59
+ }
60
+ /**
61
+ * Run `npm audit --json` in `cwd` and return findings. The function is
62
+ * resilient to npm not being installed, the project missing a
63
+ * lockfile, or audit returning malformed JSON — every error path
64
+ * yields a structured `NpmAuditResult` instead of throwing.
65
+ */
66
+ export declare function runNpmAudit(cwd: string): Promise<NpmAuditResult>;
67
+ //# sourceMappingURL=npm-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm-audit.d.ts","sourceRoot":"","sources":["../../src/security/npm-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,mBAAmB,CAAC;AAO3B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,GAC3E;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,IAAI,GAAG,OAAO,GAAG,kBAAkB,CAAC;IAC3C,6CAA6C;IAC7C,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,kEAAkE;IAClE,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACnD,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAmCD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAoDtE"}