harnery 0.7.0 → 0.8.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 +1 -1
- package/dist/commander.d.ts +9 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commands/callers.d.ts.map +1 -1
- package/dist/commands/callers.js +69 -17
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +46 -2
- package/dist/commands/grep.d.ts +44 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +246 -45
- package/dist/core/agents/cli.js +48 -0
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +10 -3
- package/dist/core/config.d.ts +9 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +19 -0
- package/dist/lib/tools/ripgrep.d.ts +61 -0
- package/dist/lib/tools/ripgrep.d.ts.map +1 -0
- package/dist/lib/tools/ripgrep.js +217 -0
- package/package.json +1 -1
- package/src/commander.ts +9 -0
- package/src/commands/callers.ts +90 -26
- package/src/commands/doctor.ts +50 -2
- package/src/commands/grep.ts +277 -46
- package/src/core/agents/cli.ts +55 -0
- package/src/core/agents/state/heartbeat-projector.ts +11 -3
- package/src/core/config.ts +24 -0
- package/src/lib/tools/ripgrep.ts +244 -0
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
- **Standalone web UI:** `harn web up` boots a local Next.js dashboard for the coord layer, councils, per-project state, and a local coding-agent status view (Claude Code / Codex / Cursor usage, backed by `harn devtools`). Ships with the git clone, not the npm package (see [Install](#install)).
|
|
24
24
|
- **Backup + sync:** `harn backup` snapshots `.harnery/` via [restic](https://restic.net/); `harn sync` keeps a curated subset live across machines via [rclone](https://rclone.org/) (Google Drive or any rclone remote).
|
|
25
25
|
|
|
26
|
-
The CLI also ships batteries: portable utility commands (`tokens`, `eml`, `env`, `grep
|
|
26
|
+
The CLI also ships batteries: portable utility commands (`tokens`, `eml`, `env`, `grep` — parallel, ripgrep-accelerated monorepo search — `docs`, `browse`, `fetch`, `read`, `devtools`, and more) plus the library toolkit they're built from. Useful, cross-platform, dependency-light — and deliberately not the headline. Coordination is why harnery exists; the toolkit exists because the CLI needed it (see [Public surface tiers](#public-surface-tiers)).
|
|
27
27
|
|
|
28
28
|
## Install
|
|
29
29
|
|
package/dist/commander.d.ts
CHANGED
|
@@ -78,6 +78,15 @@ export interface HarneryProgramContext {
|
|
|
78
78
|
* carrying provider-specific checks. harn standalone ships none.
|
|
79
79
|
*/
|
|
80
80
|
envSections?: Record<string, EnvSection>;
|
|
81
|
+
/**
|
|
82
|
+
* Extra directory names for `grep` to exclude by default — host-generated
|
|
83
|
+
* mirrors, vendored trees, and similar directories that only ever produce
|
|
84
|
+
* duplicate or noisy matches. Names match at any depth (same semantics as
|
|
85
|
+
* grep's `--exclude-dir`). Applied alongside the built-in skip list and
|
|
86
|
+
* disabled together with it by `--no-default-excludes`. harn standalone
|
|
87
|
+
* ships none.
|
|
88
|
+
*/
|
|
89
|
+
grepExcludeDirs?: readonly string[];
|
|
81
90
|
/**
|
|
82
91
|
* Optional callback that returns extra HTTP headers to attach to outbound
|
|
83
92
|
* `fetch` calls based on the target URL. Useful for consumers that need
|
package/dist/commander.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callers.d.ts","sourceRoot":"","sources":["../../src/commands/callers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"callers.d.ts","sourceRoot":"","sources":["../../src/commands/callers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AA0E1E,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA6BN"}
|
package/dist/commands/callers.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { resolveSearchEngine } from "../lib/tools/ripgrep.js";
|
|
2
3
|
const DEFAULT_EXCLUDE_DIRS = [
|
|
3
4
|
".git",
|
|
4
5
|
".cache",
|
|
@@ -62,20 +63,29 @@ async function runCallers(symbol, opts, context) {
|
|
|
62
63
|
throw new Error(`symbol must be a valid identifier (got: ${symbol})`);
|
|
63
64
|
}
|
|
64
65
|
const started = Date.now();
|
|
66
|
+
const { engine, rgBin } = await resolveSearchEngine("callers");
|
|
65
67
|
const repos = resolveRepos(opts, context);
|
|
66
68
|
const limit = opts.limit ? Number.parseInt(opts.limit, 10) : 500;
|
|
69
|
+
// Search every repo concurrently, each capped at the global limit, then
|
|
70
|
+
// apply the global budget in repo order so truncation stays deterministic.
|
|
71
|
+
// In --all-repos mode the parent scan prunes submodule dirs (each submodule
|
|
72
|
+
// is scanned on its own), so a match is attributed to exactly one repo.
|
|
73
|
+
const perRepo = await Promise.all(repos.map((repo) => {
|
|
74
|
+
const pruneDirs = opts.allRepos && repo.name === "parent" ? (context?.submodules ?? []) : [];
|
|
75
|
+
return searchInRepo(symbol, opts, repo, limit, engine, rgBin, pruneDirs);
|
|
76
|
+
}));
|
|
67
77
|
const allCallers = [];
|
|
68
78
|
let truncated = false;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
const remaining = limit - allCallers.length;
|
|
75
|
-
const found = await grepInRepo(symbol, opts, repo, remaining);
|
|
79
|
+
let budget = limit;
|
|
80
|
+
for (let i = 0; i < repos.length; i++) {
|
|
81
|
+
const found = perRepo[i] ?? { callers: [], truncated: false };
|
|
76
82
|
if (found.truncated)
|
|
77
83
|
truncated = true;
|
|
78
|
-
|
|
84
|
+
const take = Math.min(found.callers.length, Math.max(0, budget));
|
|
85
|
+
if (found.callers.length > take)
|
|
86
|
+
truncated = true;
|
|
87
|
+
allCallers.push(...found.callers.slice(0, take));
|
|
88
|
+
budget -= take;
|
|
79
89
|
}
|
|
80
90
|
const by_kind = {};
|
|
81
91
|
for (const c of allCallers) {
|
|
@@ -121,22 +131,61 @@ function resolveRepos(opts, context) {
|
|
|
121
131
|
}
|
|
122
132
|
return [{ name: "cwd", cwd: process.cwd() }];
|
|
123
133
|
}
|
|
124
|
-
function
|
|
125
|
-
const args = ["-rnw", "--color=never", "-I", "-E"];
|
|
126
|
-
for (const d of DEFAULT_EXCLUDE_DIRS)
|
|
127
|
-
args.push(`--exclude-dir=${d}`);
|
|
128
|
-
for (const f of DEFAULT_EXCLUDE_FILES)
|
|
129
|
-
args.push(`--exclude=${f}`);
|
|
134
|
+
function resolveLangGlobs(opts) {
|
|
130
135
|
const langGlobs = opts.lang ? LANG_GLOBS[opts.lang] : undefined;
|
|
131
136
|
if (opts.lang && !langGlobs) {
|
|
132
137
|
throw new Error(`unknown --lang "${opts.lang}". Valid: ${Object.keys(LANG_GLOBS).join(", ")}`);
|
|
133
138
|
}
|
|
139
|
+
return langGlobs;
|
|
140
|
+
}
|
|
141
|
+
function buildCallersGrepArgs(symbol, opts, extraExcludeDirs) {
|
|
142
|
+
const args = ["-rnw", "-H", "--color=never", "-I", "-E"];
|
|
143
|
+
for (const d of DEFAULT_EXCLUDE_DIRS)
|
|
144
|
+
args.push(`--exclude-dir=${d}`);
|
|
145
|
+
for (const f of DEFAULT_EXCLUDE_FILES)
|
|
146
|
+
args.push(`--exclude=${f}`);
|
|
147
|
+
for (const d of extraExcludeDirs)
|
|
148
|
+
args.push(`--exclude-dir=${d}`);
|
|
149
|
+
const langGlobs = resolveLangGlobs(opts);
|
|
134
150
|
if (langGlobs)
|
|
135
151
|
for (const g of langGlobs)
|
|
136
152
|
args.push(`--include=${g}`);
|
|
137
153
|
args.push("--", symbol, ".");
|
|
154
|
+
return args;
|
|
155
|
+
}
|
|
156
|
+
function buildCallersRgArgs(symbol, opts, extraExcludeDirs) {
|
|
157
|
+
// --hidden --no-ignore --no-config: match grep semantics (dotdirs searched,
|
|
158
|
+
// .gitignore ignored, user rg config can't skew results). -w = whole word.
|
|
159
|
+
// Lang globs (positives) go before dir excludes (negatives) since rg globs
|
|
160
|
+
// are last-match-wins.
|
|
161
|
+
const args = [
|
|
162
|
+
"-nw",
|
|
163
|
+
"--no-heading",
|
|
164
|
+
"--with-filename",
|
|
165
|
+
"--color=never",
|
|
166
|
+
"--no-config",
|
|
167
|
+
"--hidden",
|
|
168
|
+
"--no-ignore",
|
|
169
|
+
];
|
|
170
|
+
const langGlobs = resolveLangGlobs(opts);
|
|
171
|
+
if (langGlobs)
|
|
172
|
+
for (const g of langGlobs)
|
|
173
|
+
args.push(`--glob=${g}`);
|
|
174
|
+
for (const d of DEFAULT_EXCLUDE_DIRS)
|
|
175
|
+
args.push(`--glob=!${d}`);
|
|
176
|
+
for (const f of DEFAULT_EXCLUDE_FILES)
|
|
177
|
+
args.push(`--glob=!${f}`);
|
|
178
|
+
for (const d of extraExcludeDirs)
|
|
179
|
+
args.push(`--glob=!${d}`);
|
|
180
|
+
args.push("--", symbol, ".");
|
|
181
|
+
return args;
|
|
182
|
+
}
|
|
183
|
+
function searchInRepo(symbol, opts, repo, limit, engine, rgBin, extraExcludeDirs) {
|
|
184
|
+
const [bin, args] = engine === "rg"
|
|
185
|
+
? [rgBin, buildCallersRgArgs(symbol, opts, extraExcludeDirs)]
|
|
186
|
+
: ["grep", buildCallersGrepArgs(symbol, opts, extraExcludeDirs)];
|
|
138
187
|
return new Promise((resolveP, reject) => {
|
|
139
|
-
const proc = spawn(
|
|
188
|
+
const proc = spawn(bin, args, { cwd: repo.cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
140
189
|
const callers = [];
|
|
141
190
|
let buffer = "";
|
|
142
191
|
let stderr = "";
|
|
@@ -174,8 +223,11 @@ function grepInRepo(symbol, opts, repo, limit) {
|
|
|
174
223
|
if (parsed)
|
|
175
224
|
callers.push(parsed);
|
|
176
225
|
}
|
|
177
|
-
|
|
178
|
-
|
|
226
|
+
// Exit 1 = no matches (both engines): normal. Exit 2 with nothing
|
|
227
|
+
// collected = a real failure; with matches, a partial walk error we
|
|
228
|
+
// tolerate (surface what was found).
|
|
229
|
+
if (code !== null && code !== 0 && code !== 1 && !truncated && callers.length === 0) {
|
|
230
|
+
reject(new Error(`${bin} exited ${code}: ${stderr.trim() || "(no stderr)"}`));
|
|
179
231
|
return;
|
|
180
232
|
}
|
|
181
233
|
resolveP({ callers, truncated });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAKnD,KAAK,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAOD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAmD/E;AAED,wBAAgB,SAAS,IAAI,KAAK,EAAE,CAanC"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -14,15 +14,25 @@ import { spawnSync } from "node:child_process";
|
|
|
14
14
|
import { existsSync } from "node:fs";
|
|
15
15
|
import os from "node:os";
|
|
16
16
|
import path from "node:path";
|
|
17
|
-
import { resolveBinName } from "../core/config.js";
|
|
17
|
+
import { resolveBinName, ripgrepAutoInstall } from "../core/config.js";
|
|
18
18
|
import { loadHarnessWiring } from "../core/hooks/harness/wiring.js";
|
|
19
|
+
import { findRg, installRg, managedRgPath, rgInstallSupported } from "../lib/tools/ripgrep.js";
|
|
19
20
|
export function registerDoctorCommand(program, emit) {
|
|
20
21
|
program
|
|
21
22
|
.command("doctor")
|
|
22
23
|
.description("Verify the runtime + optional deps harnery commands expect. Exits 0 " +
|
|
23
24
|
"unless a required dep (Node, git) is missing.")
|
|
24
25
|
.option("--json", "Machine-readable JSON output")
|
|
25
|
-
.
|
|
26
|
+
.option("--fix", "Install missing managed tools (currently: ripgrep, pinned + checksum-verified)")
|
|
27
|
+
.action(async (opts) => {
|
|
28
|
+
if (opts.fix && !findRg() && rgInstallSupported()) {
|
|
29
|
+
try {
|
|
30
|
+
await installRg((line) => emit.text(`${line}\n`));
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
emit.text(`ripgrep install failed: ${err.message}\n`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
26
36
|
const checks = runChecks();
|
|
27
37
|
const requiredFailed = checks.some((c) => c.severity === "fail");
|
|
28
38
|
if (opts.json) {
|
|
@@ -56,6 +66,7 @@ export function runChecks() {
|
|
|
56
66
|
checkNode(),
|
|
57
67
|
checkGit(),
|
|
58
68
|
checkBun(),
|
|
69
|
+
checkRipgrep(),
|
|
59
70
|
checkHarneryDir(),
|
|
60
71
|
checkHarnessHooks(),
|
|
61
72
|
checkRestic(),
|
|
@@ -64,6 +75,39 @@ export function runChecks() {
|
|
|
64
75
|
checkPython(),
|
|
65
76
|
];
|
|
66
77
|
}
|
|
78
|
+
function checkRipgrep() {
|
|
79
|
+
const found = findRg();
|
|
80
|
+
if (found) {
|
|
81
|
+
const r = whichVersion(found);
|
|
82
|
+
const managed = found === managedRgPath() ? ", managed" : "";
|
|
83
|
+
return {
|
|
84
|
+
name: "ripgrep",
|
|
85
|
+
severity: "ok",
|
|
86
|
+
detail: `${r.out.replace(/^ripgrep\s*/, "")} (${found === "rg" ? "PATH" : found}${managed})`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (!rgInstallSupported()) {
|
|
90
|
+
return {
|
|
91
|
+
name: "ripgrep",
|
|
92
|
+
severity: "warn",
|
|
93
|
+
detail: "missing (grep fallback; no pinned artifact for this OS/arch)",
|
|
94
|
+
hint: "https://github.com/BurntSushi/ripgrep#installation",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (ripgrepAutoInstall()) {
|
|
98
|
+
return {
|
|
99
|
+
name: "ripgrep",
|
|
100
|
+
severity: "warn",
|
|
101
|
+
detail: "missing (autoInstall on: will self-provision on first grep)",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
name: "ripgrep",
|
|
106
|
+
severity: "warn",
|
|
107
|
+
detail: "missing (grep fallback works, just slower)",
|
|
108
|
+
hint: `${resolveBinName()} doctor --fix (pinned + checksum-verified, installs to the harnery tools dir)`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
67
111
|
function whichVersion(bin, args = ["--version"]) {
|
|
68
112
|
const r = spawnSync(bin, args, { encoding: "utf-8" });
|
|
69
113
|
if (r.status !== 0)
|
package/dist/commands/grep.d.ts
CHANGED
|
@@ -1,4 +1,48 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
import type { EmitContext, HarneryProgramContext } from "../commander.js";
|
|
3
|
+
import { type SearchEngine } from "../lib/tools/ripgrep.js";
|
|
4
|
+
export type GrepEngine = SearchEngine;
|
|
5
|
+
export interface GrepOpts {
|
|
6
|
+
repo?: string;
|
|
7
|
+
allRepos?: boolean;
|
|
8
|
+
lang?: string;
|
|
9
|
+
ignoreCase?: boolean;
|
|
10
|
+
wholeWord?: boolean;
|
|
11
|
+
literal?: boolean;
|
|
12
|
+
filesOnly?: boolean;
|
|
13
|
+
files?: boolean;
|
|
14
|
+
count?: boolean;
|
|
15
|
+
context?: string;
|
|
16
|
+
maxCount?: string;
|
|
17
|
+
limit?: string;
|
|
18
|
+
include?: string[];
|
|
19
|
+
exclude?: string[];
|
|
20
|
+
noDefaultExcludes?: boolean;
|
|
21
|
+
json?: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface Match {
|
|
24
|
+
repo: string;
|
|
25
|
+
file: string;
|
|
26
|
+
line: number;
|
|
27
|
+
text: string;
|
|
28
|
+
}
|
|
3
29
|
export declare function registerGrepCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
|
|
30
|
+
export interface GrepResult {
|
|
31
|
+
pattern: string;
|
|
32
|
+
mode: "regex" | "literal" | "files";
|
|
33
|
+
engine: GrepEngine;
|
|
34
|
+
repos: {
|
|
35
|
+
name: string;
|
|
36
|
+
cwd: string;
|
|
37
|
+
matches: Match[];
|
|
38
|
+
truncated: boolean;
|
|
39
|
+
}[];
|
|
40
|
+
total_matches: number;
|
|
41
|
+
total_files: number;
|
|
42
|
+
truncated: boolean;
|
|
43
|
+
elapsed_ms: number;
|
|
44
|
+
}
|
|
45
|
+
/** Exported for tests (not part of the package exports map). */
|
|
46
|
+
export declare function runGrep(pattern: string, paths: string[], opts: GrepOpts, context: HarneryProgramContext | undefined): Promise<GrepResult>;
|
|
47
|
+
export {};
|
|
4
48
|
//# sourceMappingURL=grep.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/commands/grep.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/commands/grep.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAgEjF,MAAM,MAAM,UAAU,GAAG,YAAY,CAAC;AAEtC,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA0CN;AAMD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,KAAK,EAAE,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,gEAAgE;AAChE,wBAAsB,OAAO,CAC3B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,qBAAqB,GAAG,SAAS,GACzC,OAAO,CAAC,UAAU,CAAC,CA4FrB"}
|