@moikapy/lich 0.5.1 → 0.7.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/CHANGELOG.md +33 -0
- package/README.md +84 -4
- package/dist/chunk-JYURFAGB.js +92 -0
- package/dist/chunk-JYURFAGB.js.map +1 -0
- package/dist/{chunk-CV2YH3FH.js → chunk-QVJCIZIF.js} +748 -13
- package/dist/chunk-QVJCIZIF.js.map +1 -0
- package/dist/chunk-SAEB3QL3.js +58 -0
- package/dist/chunk-SAEB3QL3.js.map +1 -0
- package/dist/cli.js +263 -22
- package/dist/cli.js.map +1 -1
- package/dist/{gateway-W6S43ETE.js → gateway-5BG3YCZF.js} +2 -2
- package/dist/index.d.ts +229 -118
- package/dist/index.js +5 -3
- package/dist/{tui-2VO6LAFM.js → tui-L6RABP2J.js} +43 -41
- package/dist/tui-L6RABP2J.js.map +1 -0
- package/docs/.vitepress/config.mts +6 -1
- package/docs/architecture/agent-loop.md +5 -3
- package/docs/architecture/overview.md +36 -22
- package/docs/architecture/plugins.md +1 -1
- package/docs/architecture/tools.md +7 -2
- package/docs/getting-started.md +9 -7
- package/docs/index.md +6 -4
- package/docs/user-guide/cli.md +24 -9
- package/docs/user-guide/games.md +93 -0
- package/docs/user-guide/godot.md +3 -1
- package/docs/user-guide/library.md +8 -2
- package/docs/user-guide/plugins.md +2 -2
- package/docs/user-guide/redot.md +93 -0
- package/docs/user-guide/tui.md +11 -11
- package/examples/game_bridge/README.md +2 -0
- package/optional-mcps/godot/manifest.json +6 -0
- package/optional-mcps/redot/manifest.json +18 -0
- package/package.json +2 -1
- package/dist/chunk-6M6OAQGN.js +0 -17
- package/dist/chunk-6M6OAQGN.js.map +0 -1
- package/dist/chunk-CV2YH3FH.js.map +0 -1
- package/dist/tui-2VO6LAFM.js.map +0 -1
- /package/dist/{gateway-W6S43ETE.js.map → gateway-5BG3YCZF.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0 (unreleased)
|
|
4
|
+
|
|
5
|
+
- add a general MCP client. `mcp_servers` is a closed record of named
|
|
6
|
+
stdio or loopback-http entries, default off. tool names are
|
|
7
|
+
`mcp_<server>_<tool>`. `tools_enabled: []` drops them. the commander
|
|
8
|
+
persona does not copy `mcp_servers`. redot is a catalog entry (local
|
|
9
|
+
`redot` binary, `--headless --mcp-server --path <project>`, five
|
|
10
|
+
upstream controllers, no execute). godot has no official server; lich
|
|
11
|
+
does not download a community addon. play stays the webhook and
|
|
12
|
+
game_bridge, the opposite direction. see docs/user-guide/redot.md.
|
|
13
|
+
- add `lich mcp` to list, add, enable, disable, and remove servers.
|
|
14
|
+
add uses the catalog or `--command`/`--url`, stays disabled, and
|
|
15
|
+
updates `.lich/config.json` through the existing writer without
|
|
16
|
+
dropping other keys.
|
|
17
|
+
- sync user docs with this tree. the published package is 0.6.0
|
|
18
|
+
(`lich --version` reads package.json). MCP commands are not in that
|
|
19
|
+
package. CLI, config, plugin load, and sidebar links match the code.
|
|
20
|
+
0.7.0 is not released.
|
|
21
|
+
|
|
22
|
+
## 0.6.0
|
|
23
|
+
|
|
24
|
+
- document a per-persona orchestrator example: one agent per NPC, the
|
|
25
|
+
webhook `POST /message` shape, and history serialization the game repo
|
|
26
|
+
copies. lich does not ship the service. see
|
|
27
|
+
examples/persona_orchestrator.
|
|
28
|
+
- document session JSONL as a combat log, with jq recipes for rationale,
|
|
29
|
+
ability use, vetoes, rejects, and `run_end` token totals. see
|
|
30
|
+
docs/user-guide/games.md.
|
|
31
|
+
- append a `run_end` meta record (`stopped_reason`, `usage`) on every
|
|
32
|
+
completed run so token spend is in the transcript. `budget_exhausted`
|
|
33
|
+
is still written when the stop reason is budget.
|
|
34
|
+
- add a display-only undead theme: frozen `lich` strings, `theme` / `--theme`, and `~/.lich/themes/<name>.json` that falls back to the built-in theme when missing or invalid.
|
|
35
|
+
|
|
3
36
|
## 0.5.1
|
|
4
37
|
|
|
5
38
|
- fix the CLI so bun's global bin enters main (symlink argv no longer skips the entry).
|
package/README.md
CHANGED
|
@@ -1,20 +1,44 @@
|
|
|
1
|
-
# lich
|
|
1
|
+
# ⚱ lich
|
|
2
2
|
|
|
3
3
|
Lich is a TypeScript AI agent harness (library + CLI) that runs a
|
|
4
4
|
Think-Act-Observe loop: an LLM plans, calls tools, observes results, and
|
|
5
5
|
repeats until it produces a final answer. It ships with provider failover,
|
|
6
6
|
tool guardrails, context compression, and JSONL session persistence.
|
|
7
7
|
|
|
8
|
+
## Lore glossary
|
|
9
|
+
|
|
10
|
+
Lore names are prose only. Identifiers, config keys, event types, and tool
|
|
11
|
+
names do not change. The default system prompt is a myth-free behavior spec;
|
|
12
|
+
mythology lives in display strings only.
|
|
13
|
+
|
|
14
|
+
| Lore term | Actual concept | Where it appears |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| **phylacteries** | JSONL session files in `.lich/sessions/` — conversations survive process death | This glossary; TUI `/sessions` listing label |
|
|
17
|
+
| **vessel-hopping** | Provider failover: 429/5xx retried with backoff, then the next provider takes over | This glossary |
|
|
18
|
+
| **the lair / wards** | `work_dir` confinement + `path_escape` guardrails | This glossary |
|
|
19
|
+
| **lair actions** | Plugin hooks that observe or veto tool calls | This glossary |
|
|
20
|
+
| **familiars** | Gateway adapters (webhook/telegram/discord/twitch) routing into one shared agent | This glossary |
|
|
21
|
+
| **spells** | Builtin tools in the registry | This glossary |
|
|
22
|
+
| **distillation** | Context compression: old turns summarized to fit the token budget | TUI compress notice |
|
|
23
|
+
| **the ritual is spent** | Turn-budget exhaustion | TUI + CLI budget notices |
|
|
24
|
+
| **dormant / deliberating / casting** | idle / thinking / tool phases | TUI status bar phase labels |
|
|
25
|
+
| **mortal** | The human user | TUI user transcript label |
|
|
26
|
+
|
|
8
27
|
## Documentation
|
|
9
28
|
|
|
10
29
|
| Page | Contents |
|
|
11
30
|
| --- | --- |
|
|
12
31
|
| [Docs home](docs/index.md) | Overview, feature map, and a 60-second quickstart. |
|
|
13
32
|
| [Getting started](docs/getting-started.md) | Zero-to-first-reply: install, config paths, one-shot, TUI, gateway. |
|
|
14
|
-
| [CLI reference](docs/user-guide/cli.md) |
|
|
33
|
+
| [CLI reference](docs/user-guide/cli.md) | Modes, flags, provider resolution, config schema, `lich mcp`, recipes. |
|
|
15
34
|
| [TUI guide](docs/user-guide/tui.md) | Launch, slash commands, status bar, memory semantics. |
|
|
16
35
|
| [Gateway guide](docs/user-guide/gateway.md) | Webhook/Telegram/Discord/Twitch setup and the webhook API. |
|
|
17
36
|
| [Library guide](docs/user-guide/library.md) | Embedding: `create_agent`, events, multi-turn history, errors. |
|
|
37
|
+
| [Plugins guide](docs/user-guide/plugins.md) | User tools and hooks, and the self-improvement loop. |
|
|
38
|
+
| [Godot guide](docs/user-guide/godot.md) | The game connects to lich (webhook + `game_bridge`). |
|
|
39
|
+
| [Redot guide](docs/user-guide/redot.md) | lich connects to editor MCP servers. Redot is a catalog entry. |
|
|
40
|
+
| [Games guide](docs/user-guide/games.md) | Session JSONL as a combat log, and jq recipes over it. |
|
|
41
|
+
| [Persona example](examples/persona_orchestrator/README.md) | Per-NPC agents the game repo copies. Not a second core. |
|
|
18
42
|
|
|
19
43
|
## Quick start (CLI)
|
|
20
44
|
|
|
@@ -42,7 +66,8 @@ lich gateway webhook
|
|
|
42
66
|
|
|
43
67
|
The CLI has four modes: **one-shot** (`lich "task"`), **chat**
|
|
44
68
|
(`lich chat`), **tui** (`lich tui`), and **gateway**
|
|
45
|
-
(`lich gateway <platform...>`).
|
|
69
|
+
(`lich gateway <platform...>`). Other commands do not start an agent:
|
|
70
|
+
`lich init`, `lich config`, `lich update`, and `lich mcp`.
|
|
46
71
|
|
|
47
72
|
Or use a JSON config file: `lich --config lich.json "task"` (see
|
|
48
73
|
`AgentConfig` in `src/agent/config.ts` for the schema).
|
|
@@ -92,7 +117,9 @@ snake_case args and are registered under the `builtin` toolset.
|
|
|
92
117
|
Customize lich with your own tools and lifecycle hooks: keep a `Plugin`
|
|
93
118
|
object (`{name, tools?, hooks?}`) in your repo, list its file path in the
|
|
94
119
|
`plugins` config array, and the agent merges your tools and lets your hooks
|
|
95
|
-
observe or veto tool calls.
|
|
120
|
+
observe or veto tool calls. Bare `lich`, one-shot, chat, tui, and gateway
|
|
121
|
+
all load `config.plugins` (`create_agent_with_plugins`). `create_agent` does
|
|
122
|
+
not. See
|
|
96
123
|
[docs/user-guide/plugins.md](docs/user-guide/plugins.md).
|
|
97
124
|
|
|
98
125
|
Skills are markdown files you write to `.lich/skills/` with `write_file`;
|
|
@@ -184,6 +211,59 @@ lich tui
|
|
|
184
211
|
Slash commands: `/help`, `/model`, `/usage`, `/clear`, `/sessions`,
|
|
185
212
|
`/exit` (also `/quit`, `/q`). The transcript shows the newest 50 blocks.
|
|
186
213
|
|
|
214
|
+
## Themes
|
|
215
|
+
|
|
216
|
+
Display strings come from one active theme per process. Set `"theme": "lich"`
|
|
217
|
+
in `.lich/config.json`, or pass `--theme <name>`. The built-in `lich` theme
|
|
218
|
+
is frozen data: `~/.lich/themes/lich.json` is ignored. Any other name is read
|
|
219
|
+
from `~/.lich/themes/<name>.json`. A missing file, invalid JSON, or a file
|
|
220
|
+
that fails the theme schema logs one warning and falls back to `lich`. Themes
|
|
221
|
+
do not change the system prompt, tool descriptions, event types, or slash
|
|
222
|
+
command names.
|
|
223
|
+
|
|
224
|
+
The built-in tagline is `the agent that will not stay dead`, and it appears once, in the TUI banner (`welcome`). Greppable keywords
|
|
225
|
+
stay in place: budget notices still start with `budget exhausted`, and
|
|
226
|
+
compression notices still start with `context compressed`.
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"name": "vampire",
|
|
231
|
+
"agent_name": "vampire",
|
|
232
|
+
"glyph": "🦇",
|
|
233
|
+
"tagline": "night's clerk, unpaid",
|
|
234
|
+
"welcome": "🦇 vampire v{version} — night's clerk, unpaid · {model} ({kind})",
|
|
235
|
+
"goodbye": "dawn approaches",
|
|
236
|
+
"response_label": "vampire",
|
|
237
|
+
"user_label": "mortal",
|
|
238
|
+
"phase_labels": { "idle": "sleeping", "thinking": "scheming", "tool": "feeding" },
|
|
239
|
+
"notices": {
|
|
240
|
+
"budget_exhausted": "budget exhausted — the blood bank is dry (turn cap reached)",
|
|
241
|
+
"compressed": "context compressed — memories enthralled (summary {chars} chars)",
|
|
242
|
+
"sessions": "coffins ({count}):"
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`welcome` substitutes `{version}`, `{model}`, and `{kind}`. `notices.compressed`
|
|
248
|
+
substitutes `{chars}`; `notices.sessions` substitutes `{count}`.
|
|
249
|
+
|
|
250
|
+
## Editor MCP
|
|
251
|
+
|
|
252
|
+
Two directions. lich connects **to** an editor MCP server (stdio local
|
|
253
|
+
command, or loopback HTTP). The game connects **to** lich through the webhook
|
|
254
|
+
and [`examples/game_bridge`](examples/game_bridge/README.md). Default is off.
|
|
255
|
+
`mcp_servers` is a closed record. Names are `mcp_<server>_<tool>`. `npx`,
|
|
256
|
+
`npm`, `bunx`, `uvx`, `curl`, `wget`, remote URLs, and shell metacharacters
|
|
257
|
+
are refused.
|
|
258
|
+
|
|
259
|
+
This source has `lich mcp list|add|enable|disable|remove` (changelog 0.7.0,
|
|
260
|
+
unreleased). The published npm package is 0.6.0 and does not include those
|
|
261
|
+
commands; `lich --version` still prints `0.6.0` because it reads
|
|
262
|
+
`package.json`. From a clone: `bun src/cli.ts mcp list`. Redot is a catalog
|
|
263
|
+
entry (`redot --headless --mcp-server`), not a fork inside lich. Godot has
|
|
264
|
+
no official MCP server; lich does not download a community addon. See
|
|
265
|
+
[docs/user-guide/redot.md](docs/user-guide/redot.md).
|
|
266
|
+
|
|
187
267
|
## Development
|
|
188
268
|
|
|
189
269
|
```sh
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-QVJCIZIF.js";
|
|
4
|
+
|
|
5
|
+
// src/util/theme.ts
|
|
6
|
+
import { readFileSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
// src/util/lore.ts
|
|
12
|
+
var phase_labels = Object.freeze({
|
|
13
|
+
idle: "dormant",
|
|
14
|
+
thinking: "deliberating",
|
|
15
|
+
tool: "casting"
|
|
16
|
+
});
|
|
17
|
+
var notices = Object.freeze({
|
|
18
|
+
budget_exhausted: "budget exhausted \u2014 the ritual is spent (turn cap reached)",
|
|
19
|
+
compressed: "context compressed \u2014 memories distilled (summary {chars} chars)",
|
|
20
|
+
sessions: "phylacteries ({count}):"
|
|
21
|
+
});
|
|
22
|
+
var LICH_THEME = Object.freeze({
|
|
23
|
+
name: "lich",
|
|
24
|
+
agent_name: "lich",
|
|
25
|
+
glyph: "\u26B1",
|
|
26
|
+
tagline: "the agent that will not stay dead",
|
|
27
|
+
welcome: "\u26B1 lich v{version} \u2014 the agent that will not stay dead \xB7 {model} ({kind})",
|
|
28
|
+
goodbye: "the lich endures",
|
|
29
|
+
response_label: "lich",
|
|
30
|
+
user_label: "mortal",
|
|
31
|
+
phase_labels,
|
|
32
|
+
notices
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// src/util/theme.ts
|
|
36
|
+
var text = z.string().min(1);
|
|
37
|
+
var theme_schema = z.object({
|
|
38
|
+
name: text,
|
|
39
|
+
agent_name: text,
|
|
40
|
+
glyph: text,
|
|
41
|
+
tagline: text,
|
|
42
|
+
welcome: text,
|
|
43
|
+
goodbye: text,
|
|
44
|
+
response_label: text,
|
|
45
|
+
user_label: text,
|
|
46
|
+
phase_labels: z.object({ idle: text, thinking: text, tool: text }),
|
|
47
|
+
notices: z.object({ budget_exhausted: text, compressed: text, sessions: text })
|
|
48
|
+
});
|
|
49
|
+
function fill_template(template, vars) {
|
|
50
|
+
let filled = template;
|
|
51
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
52
|
+
filled = filled.replaceAll(`{${key}}`, String(value));
|
|
53
|
+
}
|
|
54
|
+
return filled;
|
|
55
|
+
}
|
|
56
|
+
function notice_flavor(notice) {
|
|
57
|
+
const start = notice.indexOf(" \u2014 ");
|
|
58
|
+
if (start === -1) {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
const rest = notice.slice(start + 3);
|
|
62
|
+
const end = rest.indexOf(" (");
|
|
63
|
+
return end === -1 ? rest : rest.slice(0, end);
|
|
64
|
+
}
|
|
65
|
+
function load_theme(name, themes_dir) {
|
|
66
|
+
if (name === "lich" || name.length === 0) {
|
|
67
|
+
return LICH_THEME;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return read_theme_file(name, themes_dir);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logger.warn(`theme '${name}' unavailable; using default lich theme`, error);
|
|
73
|
+
return LICH_THEME;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function read_theme_file(name, themes_dir) {
|
|
77
|
+
if (path.basename(name) !== name) {
|
|
78
|
+
throw new Error("invalid theme name");
|
|
79
|
+
}
|
|
80
|
+
const root = themes_dir ?? path.join(homedir(), ".lich", "themes");
|
|
81
|
+
const theme = theme_schema.parse(JSON.parse(readFileSync(path.join(root, `${name}.json`), "utf8")));
|
|
82
|
+
Object.freeze(theme.phase_labels);
|
|
83
|
+
Object.freeze(theme.notices);
|
|
84
|
+
return Object.freeze(theme);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
fill_template,
|
|
89
|
+
notice_flavor,
|
|
90
|
+
load_theme
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=chunk-JYURFAGB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util/theme.ts","../src/util/lore.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { logger } from \"./log.js\";\nimport { LICH_THEME, type ThemeSpec } from \"./lore.js\";\n\nconst text = z.string().min(1);\n\nexport const theme_schema = z.object({\n name: text, agent_name: text, glyph: text, tagline: text,\n welcome: text, goodbye: text, response_label: text, user_label: text,\n phase_labels: z.object({ idle: text, thinking: text, tool: text }),\n notices: z.object({ budget_exhausted: text, compressed: text, sessions: text }),\n});\n\nexport function fill_template(template: string, vars: Record<string, string | number>): string {\n let filled = template;\n for (const [key, value] of Object.entries(vars)) {\n filled = filled.replaceAll(`{${key}}`, String(value));\n }\n return filled;\n}\n\nexport function notice_flavor(notice: string): string {\n const start = notice.indexOf(\" — \");\n if (start === -1) {\n return \"\";\n }\n const rest = notice.slice(start + 3);\n const end = rest.indexOf(\" (\");\n return end === -1 ? rest : rest.slice(0, end);\n}\n\nexport function load_theme(name: string, themes_dir?: string): ThemeSpec {\n if (name === \"lich\" || name.length === 0) {\n return LICH_THEME;\n }\n try {\n return read_theme_file(name, themes_dir);\n } catch (error) {\n logger.warn(`theme '${name}' unavailable; using default lich theme`, error);\n return LICH_THEME;\n }\n}\n\nfunction read_theme_file(name: string, themes_dir?: string): ThemeSpec {\n if (path.basename(name) !== name) {\n throw new Error(\"invalid theme name\");\n }\n const root = themes_dir ?? path.join(homedir(), \".lich\", \"themes\");\n const theme: ThemeSpec = theme_schema.parse(JSON.parse(readFileSync(path.join(root, `${name}.json`), \"utf8\")));\n Object.freeze(theme.phase_labels);\n Object.freeze(theme.notices);\n return Object.freeze(theme);\n}\n","/**\n * Default lich theme as frozen display data. No loader logic here.\n * Mythology stays in these strings; the system prompt stays myth-free.\n */\nexport interface ThemeSpec {\n readonly name: string;\n readonly agent_name: string;\n readonly glyph: string;\n readonly tagline: string;\n readonly welcome: string;\n readonly goodbye: string;\n readonly response_label: string;\n readonly user_label: string;\n readonly phase_labels: {\n readonly idle: string;\n readonly thinking: string;\n readonly tool: string;\n };\n readonly notices: {\n readonly budget_exhausted: string;\n readonly compressed: string;\n readonly sessions: string;\n };\n}\n\nconst phase_labels = Object.freeze({\n idle: \"dormant\",\n thinking: \"deliberating\",\n tool: \"casting\",\n});\n\nconst notices = Object.freeze({\n budget_exhausted: \"budget exhausted — the ritual is spent (turn cap reached)\",\n compressed: \"context compressed — memories distilled (summary {chars} chars)\",\n sessions: \"phylacteries ({count}):\",\n});\n\nexport const LICH_THEME: ThemeSpec = Object.freeze({\n name: \"lich\",\n agent_name: \"lich\",\n glyph: \"⚱\",\n tagline: \"the agent that will not stay dead\",\n welcome: \"⚱ lich v{version} — the agent that will not stay dead · {model} ({kind})\",\n goodbye: \"the lich endures\",\n response_label: \"lich\",\n user_label: \"mortal\",\n phase_labels,\n notices,\n});\n"],"mappings":";;;;;AAAA,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AACxB,OAAO,UAAU;AACjB,SAAS,SAAS;;;ACsBlB,IAAM,eAAe,OAAO,OAAO;AAAA,EACjC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AACR,CAAC;AAED,IAAM,UAAU,OAAO,OAAO;AAAA,EAC5B,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,UAAU;AACZ,CAAC;AAEM,IAAM,aAAwB,OAAO,OAAO;AAAA,EACjD,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ;AAAA,EACA;AACF,CAAC;;;ADzCD,IAAM,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;AAEtB,IAAM,eAAe,EAAE,OAAO;AAAA,EACnC,MAAM;AAAA,EAAM,YAAY;AAAA,EAAM,OAAO;AAAA,EAAM,SAAS;AAAA,EACpD,SAAS;AAAA,EAAM,SAAS;AAAA,EAAM,gBAAgB;AAAA,EAAM,YAAY;AAAA,EAChE,cAAc,EAAE,OAAO,EAAE,MAAM,MAAM,UAAU,MAAM,MAAM,KAAK,CAAC;AAAA,EACjE,SAAS,EAAE,OAAO,EAAE,kBAAkB,MAAM,YAAY,MAAM,UAAU,KAAK,CAAC;AAChF,CAAC;AAEM,SAAS,cAAc,UAAkB,MAA+C;AAC7F,MAAI,SAAS;AACb,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,aAAS,OAAO,WAAW,IAAI,GAAG,KAAK,OAAO,KAAK,CAAC;AAAA,EACtD;AACA,SAAO;AACT;AAEO,SAAS,cAAc,QAAwB;AACpD,QAAM,QAAQ,OAAO,QAAQ,UAAK;AAClC,MAAI,UAAU,IAAI;AAChB,WAAO;AAAA,EACT;AACA,QAAM,OAAO,OAAO,MAAM,QAAQ,CAAC;AACnC,QAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,SAAO,QAAQ,KAAK,OAAO,KAAK,MAAM,GAAG,GAAG;AAC9C;AAEO,SAAS,WAAW,MAAc,YAAgC;AACvE,MAAI,SAAS,UAAU,KAAK,WAAW,GAAG;AACxC,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,gBAAgB,MAAM,UAAU;AAAA,EACzC,SAAS,OAAO;AACd,WAAO,KAAK,UAAU,IAAI,2CAA2C,KAAK;AAC1E,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,MAAc,YAAgC;AACrE,MAAI,KAAK,SAAS,IAAI,MAAM,MAAM;AAChC,UAAM,IAAI,MAAM,oBAAoB;AAAA,EACtC;AACA,QAAM,OAAO,cAAc,KAAK,KAAK,QAAQ,GAAG,SAAS,QAAQ;AACjE,QAAM,QAAmB,aAAa,MAAM,KAAK,MAAM,aAAa,KAAK,KAAK,MAAM,GAAG,IAAI,OAAO,GAAG,MAAM,CAAC,CAAC;AAC7G,SAAO,OAAO,MAAM,YAAY;AAChC,SAAO,OAAO,MAAM,OAAO;AAC3B,SAAO,OAAO,OAAO,KAAK;AAC5B;","names":[]}
|