@namzu/cli 15.0.0 → 15.1.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 +11 -1
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +2 -1
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +14 -3
- package/dist/commands/run.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +62 -73
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/Composer.js +3 -3
- package/dist/tui/Composer.js.map +1 -1
- package/dist/tui/StatusBar.js +3 -3
- package/dist/tui/StatusBar.js.map +1 -1
- package/dist/tui/Transcript.d.ts +15 -8
- package/dist/tui/Transcript.d.ts.map +1 -1
- package/dist/tui/Transcript.js +11 -10
- package/dist/tui/Transcript.js.map +1 -1
- package/dist/tui/agent.d.ts +33 -3
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +38 -12
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/live-window.d.ts +17 -11
- package/dist/tui/live-window.d.ts.map +1 -1
- package/dist/tui/live-window.js +64 -20
- package/dist/tui/live-window.js.map +1 -1
- package/dist/tui/run-interruption.d.ts +14 -0
- package/dist/tui/run-interruption.d.ts.map +1 -0
- package/dist/tui/run-interruption.js +67 -0
- package/dist/tui/run-interruption.js.map +1 -0
- package/package.json +7 -7
- package/dist/tui/bottom-spacer.d.ts +0 -95
- package/dist/tui/bottom-spacer.d.ts.map +0 -1
- package/dist/tui/bottom-spacer.js +0 -83
- package/dist/tui/bottom-spacer.js.map +0 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AgentEvent } from './agent.js';
|
|
2
|
+
type Interruption = Extract<AgentEvent, {
|
|
3
|
+
kind: 'error' | 'paused';
|
|
4
|
+
}>;
|
|
5
|
+
/**
|
|
6
|
+
* Human copy for a terminal interruption, derived only from event facts.
|
|
7
|
+
*
|
|
8
|
+
* No countdown: `retryAfterMs` is a duration reported at the failure boundary,
|
|
9
|
+
* not a clock this process keeps updating. No generic remedy either: when the
|
|
10
|
+
* SDK catalog did not claim a failure, the provider reason is all we know.
|
|
11
|
+
*/
|
|
12
|
+
export declare function describeRunInterruption(event: Interruption): string;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=run-interruption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-interruption.d.ts","sourceRoot":"","sources":["../../src/tui/run-interruption.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAG5C,KAAK,YAAY,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;CAAE,CAAC,CAAA;AAsCrE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAyBnE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { terminalDisplayText } from './terminal-display.js';
|
|
2
|
+
/** One terminal-safe line, bounded so a provider response cannot own the screen. */
|
|
3
|
+
function line(value, max = 480) {
|
|
4
|
+
const clean = terminalDisplayText(value).replace(/\s+/g, ' ').trim();
|
|
5
|
+
return clean.length <= max ? clean : `${clean.slice(0, Math.max(0, max - 1))}…`;
|
|
6
|
+
}
|
|
7
|
+
function retryAfterMs(event) {
|
|
8
|
+
const direct = event.providerError?.retryAfterMs;
|
|
9
|
+
const projected = event.failure?.details?.retryAfterMs;
|
|
10
|
+
const value = typeof direct === 'number' ? direct : projected;
|
|
11
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
12
|
+
}
|
|
13
|
+
function duration(ms) {
|
|
14
|
+
if (ms < 1_000)
|
|
15
|
+
return `${Math.ceil(ms)} ms`;
|
|
16
|
+
const seconds = Math.ceil(ms / 1_000);
|
|
17
|
+
if (seconds < 120)
|
|
18
|
+
return `${seconds} second${seconds === 1 ? '' : 's'}`;
|
|
19
|
+
const minutes = Math.ceil(seconds / 60);
|
|
20
|
+
if (minutes < 120)
|
|
21
|
+
return `${minutes} minute${minutes === 1 ? '' : 's'}`;
|
|
22
|
+
const hours = Math.ceil(minutes / 60);
|
|
23
|
+
if (hours < 48)
|
|
24
|
+
return `${hours} hour${hours === 1 ? '' : 's'}`;
|
|
25
|
+
const days = Math.ceil(hours / 24);
|
|
26
|
+
return `${days} day${days === 1 ? '' : 's'}`;
|
|
27
|
+
}
|
|
28
|
+
function materiallyDifferent(candidate, earlier) {
|
|
29
|
+
const normalized = line(candidate).toLocaleLowerCase('en-US');
|
|
30
|
+
return (normalized.length > 0 &&
|
|
31
|
+
!earlier.some((value) => {
|
|
32
|
+
const prior = line(value).toLocaleLowerCase('en-US');
|
|
33
|
+
return prior === normalized || prior.includes(normalized) || normalized.includes(prior);
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Human copy for a terminal interruption, derived only from event facts.
|
|
38
|
+
*
|
|
39
|
+
* No countdown: `retryAfterMs` is a duration reported at the failure boundary,
|
|
40
|
+
* not a clock this process keeps updating. No generic remedy either: when the
|
|
41
|
+
* SDK catalog did not claim a failure, the provider reason is all we know.
|
|
42
|
+
*/
|
|
43
|
+
export function describeRunInterruption(event) {
|
|
44
|
+
const explained = event.explanation;
|
|
45
|
+
const rawReason = event.kind === 'paused' ? event.reason : event.message;
|
|
46
|
+
const lead = line(explained?.message || rawReason);
|
|
47
|
+
const id = explained?.id ? ` [${line(explained.id, 120)}]` : '';
|
|
48
|
+
const rows = [`${event.kind === 'paused' ? 'Run paused' : 'Error'}${id}: ${lead}`];
|
|
49
|
+
const providerDetail = event.providerError?.detail;
|
|
50
|
+
if (providerDetail && materiallyDifferent(providerDetail, [lead, rawReason])) {
|
|
51
|
+
rows.push(`Provider detail: ${line(providerDetail)}`);
|
|
52
|
+
}
|
|
53
|
+
if (explained && materiallyDifferent(rawReason, [lead, providerDetail ?? ''])) {
|
|
54
|
+
rows.push(`Reason: ${line(rawReason)}`);
|
|
55
|
+
}
|
|
56
|
+
const retryAfter = retryAfterMs(event);
|
|
57
|
+
if (retryAfter !== undefined) {
|
|
58
|
+
rows.push(`Provider retry delay: at least ${duration(retryAfter)} from this failure.`);
|
|
59
|
+
}
|
|
60
|
+
if (explained?.hint)
|
|
61
|
+
rows.push(`Next: ${line(explained.hint, 720)}`);
|
|
62
|
+
if (event.kind === 'paused') {
|
|
63
|
+
rows.push(`Checkpoint preserved: ${line(event.checkpointId, 180)}`);
|
|
64
|
+
}
|
|
65
|
+
return rows.join('\n');
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=run-interruption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-interruption.js","sourceRoot":"","sources":["../../src/tui/run-interruption.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@namzu/cli",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.1.1",
|
|
4
4
|
"description": "Interactive and headless terminal agent for Namzu, with provider discovery, durable sessions, tools and diagnostics.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"namzu",
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"ink": "7.0.3",
|
|
45
45
|
"react": "19.2.6",
|
|
46
46
|
"yaml": "^2.9.0",
|
|
47
|
-
"@namzu/anthropic": "4.0.1",
|
|
48
47
|
"@namzu/computer-use": "1.4.0",
|
|
49
|
-
"@namzu/deepseek": "^1.1.
|
|
48
|
+
"@namzu/deepseek": "^1.1.1",
|
|
50
49
|
"@namzu/files": "1.1.0",
|
|
51
|
-
"@namzu/
|
|
52
|
-
"@namzu/
|
|
53
|
-
"@namzu/
|
|
54
|
-
"@namzu/sdk": "^33.
|
|
50
|
+
"@namzu/anthropic": "4.0.3",
|
|
51
|
+
"@namzu/ollama": "2.2.2",
|
|
52
|
+
"@namzu/openai": "2.1.0",
|
|
53
|
+
"@namzu/sdk": "^33.1.0",
|
|
54
|
+
"@namzu/openrouter": "2.3.2"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@biomejs/biome": "^1.9.4",
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* How many blank rows to put above the composer so it sits at the bottom.
|
|
3
|
-
*
|
|
4
|
-
* ## The narrow defect
|
|
5
|
-
*
|
|
6
|
-
* Ink writes the transcript to native scrollback via `<Static>` and re-renders
|
|
7
|
-
* the live region — activity, composer, status bar — beneath it. Once the
|
|
8
|
-
* output exceeds the viewport the terminal has scrolled, and the composer is
|
|
9
|
-
* already at the bottom. The complaint is entirely the case before that: five
|
|
10
|
-
* lines on a forty-row terminal leaves the composer at row six with blank space
|
|
11
|
-
* under it, and the operator's eye has to find it.
|
|
12
|
-
*
|
|
13
|
-
* So this does not try to pin the composer always. It pads only while the
|
|
14
|
-
* transcript is unambiguously shorter than the viewport, and returns 0 the
|
|
15
|
-
* moment that stops being knowable. After that, the terminal does it.
|
|
16
|
-
*
|
|
17
|
-
* ## Why a rough estimate is safe here, and would not be in general
|
|
18
|
-
*
|
|
19
|
-
* The rendered height of a transcript is not knowable from this side: ink does
|
|
20
|
-
* not report it, and wrapping depends on ANSI, wide glyphs and markdown that
|
|
21
|
-
* this module does not model. That is exactly why the whole-session version of
|
|
22
|
-
* this idea is a bad one.
|
|
23
|
-
*
|
|
24
|
-
* It is safe in the short case because the error is **asymmetric**:
|
|
25
|
-
*
|
|
26
|
-
* - Overestimate the content ⇒ pad less, or not at all ⇒ the composer floats,
|
|
27
|
-
* which is precisely today's behaviour. No worse than not doing this.
|
|
28
|
-
* - Underestimate the content ⇒ pad too much ⇒ the composer is pushed off the
|
|
29
|
-
* bottom, which is worse than today.
|
|
30
|
-
*
|
|
31
|
-
* So every uncertainty is resolved toward *more* content and *less* padding:
|
|
32
|
-
* `estimateRenderedLines` counts a minimum, {@link SAFETY_ROWS} is subtracted
|
|
33
|
-
* on top, and anything unknown returns 0. Being wrong costs the feature, never
|
|
34
|
-
* the usability.
|
|
35
|
-
*/
|
|
36
|
-
/**
|
|
37
|
-
* Rows held back from the calculation.
|
|
38
|
-
*
|
|
39
|
-
* Absorbs what the estimate cannot see — a wrapped line the width maths missed,
|
|
40
|
-
* a wide glyph, a markdown block that renders taller than its source. Six is
|
|
41
|
-
* chosen to be visibly generous rather than tuned: the cost of it being too
|
|
42
|
-
* large is a small gap under the composer on a nearly-empty screen, and the
|
|
43
|
-
* cost of it being too small is a composer pushed out of view.
|
|
44
|
-
*/
|
|
45
|
-
export declare const SAFETY_ROWS = 6;
|
|
46
|
-
export interface SpacerInput {
|
|
47
|
-
/** Terminal height. `undefined` when not a TTY. */
|
|
48
|
-
readonly rows: number | undefined;
|
|
49
|
-
/** Terminal width, for wrapping. `undefined` falls back to 80. */
|
|
50
|
-
readonly columns: number | undefined;
|
|
51
|
-
/**
|
|
52
|
-
* The lines of the transcript that have been printed to scrollback, as
|
|
53
|
-
* emitted — before ink renders them.
|
|
54
|
-
*
|
|
55
|
-
* Every line such a row will print, including the collapsed body under a
|
|
56
|
-
* tool call. The caller used to pass each row's `content` alone, which
|
|
57
|
-
* counted an eight-row tool result as one row: the estimate then ran low by
|
|
58
|
-
* seven, and the asymmetry above says exactly what running low costs.
|
|
59
|
-
* `/expand` makes a row whose whole substance is its body, so a caller that
|
|
60
|
-
* passed content alone would hand a two-hundred-line row over as a single
|
|
61
|
-
* line.
|
|
62
|
-
*
|
|
63
|
-
* Rows still in the live window belong in {@link liveRows}, not here. They
|
|
64
|
-
* are two different quantities — content above the live region, and the live
|
|
65
|
-
* region's own height — and this one is only the first.
|
|
66
|
-
*/
|
|
67
|
-
readonly transcript: readonly string[];
|
|
68
|
-
/**
|
|
69
|
-
* Rows the live region occupies: the window of transcript rows that are
|
|
70
|
-
* still redrawable, then the activity line, composer frame and status bar.
|
|
71
|
-
*
|
|
72
|
-
* A constant until the live window existed. It is now a function of what is
|
|
73
|
-
* in that window, because an expanded body in it is thirty rows of live
|
|
74
|
-
* region rather than eight, and a spacer that kept assuming the old constant
|
|
75
|
-
* would pad the composer straight off the bottom. See `live-window.ts`.
|
|
76
|
-
*/
|
|
77
|
-
readonly liveRows: number;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* A deliberate UNDER-count of how tall the transcript will render.
|
|
81
|
-
*
|
|
82
|
-
* Under, not over, because it is subtracted from the space available: counting
|
|
83
|
-
* low leaves more apparent room, and the safety margin plus the guard below are
|
|
84
|
-
* what stop that becoming padding. Each entry is at least one row, and a long
|
|
85
|
-
* entry is divided by the width to approximate wrapping.
|
|
86
|
-
*/
|
|
87
|
-
export declare function estimateRenderedLines(transcript: readonly string[], columns: number | undefined): number;
|
|
88
|
-
/**
|
|
89
|
-
* Blank rows to insert above the composer, or 0 to leave the layout alone.
|
|
90
|
-
*
|
|
91
|
-
* Returns 0 whenever the answer could be wrong: no TTY, an implausible height,
|
|
92
|
-
* or a transcript long enough that the terminal has plausibly begun to scroll.
|
|
93
|
-
*/
|
|
94
|
-
export declare function bottomSpacerRows(input: SpacerInput): number;
|
|
95
|
-
//# sourceMappingURL=bottom-spacer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bottom-spacer.d.ts","sourceRoot":"","sources":["../../src/tui/bottom-spacer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B,MAAM,WAAW,WAAW;IAC3B,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IACpC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,EAAE,MAAM,GAAG,SAAS,GACzB,MAAM,CASR;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAc3D"}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* How many blank rows to put above the composer so it sits at the bottom.
|
|
3
|
-
*
|
|
4
|
-
* ## The narrow defect
|
|
5
|
-
*
|
|
6
|
-
* Ink writes the transcript to native scrollback via `<Static>` and re-renders
|
|
7
|
-
* the live region — activity, composer, status bar — beneath it. Once the
|
|
8
|
-
* output exceeds the viewport the terminal has scrolled, and the composer is
|
|
9
|
-
* already at the bottom. The complaint is entirely the case before that: five
|
|
10
|
-
* lines on a forty-row terminal leaves the composer at row six with blank space
|
|
11
|
-
* under it, and the operator's eye has to find it.
|
|
12
|
-
*
|
|
13
|
-
* So this does not try to pin the composer always. It pads only while the
|
|
14
|
-
* transcript is unambiguously shorter than the viewport, and returns 0 the
|
|
15
|
-
* moment that stops being knowable. After that, the terminal does it.
|
|
16
|
-
*
|
|
17
|
-
* ## Why a rough estimate is safe here, and would not be in general
|
|
18
|
-
*
|
|
19
|
-
* The rendered height of a transcript is not knowable from this side: ink does
|
|
20
|
-
* not report it, and wrapping depends on ANSI, wide glyphs and markdown that
|
|
21
|
-
* this module does not model. That is exactly why the whole-session version of
|
|
22
|
-
* this idea is a bad one.
|
|
23
|
-
*
|
|
24
|
-
* It is safe in the short case because the error is **asymmetric**:
|
|
25
|
-
*
|
|
26
|
-
* - Overestimate the content ⇒ pad less, or not at all ⇒ the composer floats,
|
|
27
|
-
* which is precisely today's behaviour. No worse than not doing this.
|
|
28
|
-
* - Underestimate the content ⇒ pad too much ⇒ the composer is pushed off the
|
|
29
|
-
* bottom, which is worse than today.
|
|
30
|
-
*
|
|
31
|
-
* So every uncertainty is resolved toward *more* content and *less* padding:
|
|
32
|
-
* `estimateRenderedLines` counts a minimum, {@link SAFETY_ROWS} is subtracted
|
|
33
|
-
* on top, and anything unknown returns 0. Being wrong costs the feature, never
|
|
34
|
-
* the usability.
|
|
35
|
-
*/
|
|
36
|
-
/**
|
|
37
|
-
* Rows held back from the calculation.
|
|
38
|
-
*
|
|
39
|
-
* Absorbs what the estimate cannot see — a wrapped line the width maths missed,
|
|
40
|
-
* a wide glyph, a markdown block that renders taller than its source. Six is
|
|
41
|
-
* chosen to be visibly generous rather than tuned: the cost of it being too
|
|
42
|
-
* large is a small gap under the composer on a nearly-empty screen, and the
|
|
43
|
-
* cost of it being too small is a composer pushed out of view.
|
|
44
|
-
*/
|
|
45
|
-
export const SAFETY_ROWS = 6;
|
|
46
|
-
/**
|
|
47
|
-
* A deliberate UNDER-count of how tall the transcript will render.
|
|
48
|
-
*
|
|
49
|
-
* Under, not over, because it is subtracted from the space available: counting
|
|
50
|
-
* low leaves more apparent room, and the safety margin plus the guard below are
|
|
51
|
-
* what stop that becoming padding. Each entry is at least one row, and a long
|
|
52
|
-
* entry is divided by the width to approximate wrapping.
|
|
53
|
-
*/
|
|
54
|
-
export function estimateRenderedLines(transcript, columns) {
|
|
55
|
-
const width = Math.max(20, columns ?? 80);
|
|
56
|
-
let total = 0;
|
|
57
|
-
for (const entry of transcript) {
|
|
58
|
-
for (const line of entry.split('\n')) {
|
|
59
|
-
total += Math.max(1, Math.ceil(line.length / width));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return total;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Blank rows to insert above the composer, or 0 to leave the layout alone.
|
|
66
|
-
*
|
|
67
|
-
* Returns 0 whenever the answer could be wrong: no TTY, an implausible height,
|
|
68
|
-
* or a transcript long enough that the terminal has plausibly begun to scroll.
|
|
69
|
-
*/
|
|
70
|
-
export function bottomSpacerRows(input) {
|
|
71
|
-
const { rows, columns, transcript, liveRows } = input;
|
|
72
|
-
// Not a terminal, or a height too small to reason about. A pipe has no
|
|
73
|
-
// bottom to pin to.
|
|
74
|
-
if (rows === undefined || !Number.isFinite(rows) || rows < 12)
|
|
75
|
-
return 0;
|
|
76
|
-
const used = estimateRenderedLines(transcript, columns) + liveRows + SAFETY_ROWS;
|
|
77
|
-
// The transcript is at or past the viewport: the terminal is scrolling and
|
|
78
|
-
// the composer is already where it should be. Padding now would push it off.
|
|
79
|
-
if (used >= rows)
|
|
80
|
-
return 0;
|
|
81
|
-
return rows - used;
|
|
82
|
-
}
|
|
83
|
-
//# sourceMappingURL=bottom-spacer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bottom-spacer.js","sourceRoot":"","sources":["../../src/tui/bottom-spacer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAA;AAoC5B;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACpC,UAA6B,EAC7B,OAA2B;IAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,IAAI,EAAE,CAAC,CAAA;IACzC,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAA;QACrD,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAkB;IAClD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IAErD,uEAAuE;IACvE,oBAAoB;IACpB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE;QAAE,OAAO,CAAC,CAAA;IAEvE,MAAM,IAAI,GAAG,qBAAqB,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,QAAQ,GAAG,WAAW,CAAA;IAEhF,2EAA2E;IAC3E,6EAA6E;IAC7E,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,CAAC,CAAA;IAE1B,OAAO,IAAI,GAAG,IAAI,CAAA;AACnB,CAAC"}
|