@pulsemcp/air-adapter-cursor 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 +84 -0
- package/dist/cursor-adapter.d.ts +225 -0
- package/dist/cursor-adapter.d.ts.map +1 -0
- package/dist/cursor-adapter.js +1018 -0
- package/dist/cursor-adapter.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/scan-local-skills.d.ts +12 -0
- package/dist/scan-local-skills.d.ts.map +1 -0
- package/dist/scan-local-skills.js +93 -0
- package/dist/scan-local-skills.js.map +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @pulsemcp/air-adapter-cursor
|
|
2
|
+
|
|
3
|
+
AIR adapter extension for the [Cursor CLI](https://cursor.com/docs/cli) (`cursor-agent`). Translates AIR artifacts into Cursor's native formats and prepares working directories for agent sessions.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pulsemcp/air-adapter-cursor
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### With the AIR CLI
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Install the adapter globally alongside the CLI
|
|
17
|
+
npm install -g @pulsemcp/air-cli @pulsemcp/air-adapter-cursor
|
|
18
|
+
|
|
19
|
+
# Start a Cursor session
|
|
20
|
+
air start cursor --root web-app
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Programmatic
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { resolveArtifacts } from "@pulsemcp/air-core";
|
|
27
|
+
import { CursorAdapter } from "@pulsemcp/air-adapter-cursor";
|
|
28
|
+
|
|
29
|
+
const artifacts = await resolveArtifacts("./air.json");
|
|
30
|
+
const adapter = new CursorAdapter();
|
|
31
|
+
|
|
32
|
+
// Prepare a working directory for a Cursor session
|
|
33
|
+
const session = await adapter.prepareSession(artifacts, "./my-project", {
|
|
34
|
+
root: artifacts.roots["web-app"],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// session.configFiles — [] (secrets are Cursor-native ${env:VAR}, see "Secrets" below)
|
|
38
|
+
// session.skillPaths — skill dirs created in .cursor/skills/
|
|
39
|
+
// session.hookPaths — hook dirs created in .cursor/hooks/
|
|
40
|
+
// session.startCommand — { command: "cursor-agent", args: [], cwd: "..." }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## What `prepareSession()` does
|
|
44
|
+
|
|
45
|
+
1. **Writes `.cursor/mcp.json`** — translates AIR MCP server configs into a top-level `mcpServers` map. User-authored servers and top-level keys are preserved; only AIR-owned keys are replaced.
|
|
46
|
+
2. **Writes `.cursor/hooks.json`** — registers path-based hooks under `hooks.<event>` with a required `version: 1`. AIR-owned entries are tagged with `_air_hook_id`; user-authored hooks are preserved.
|
|
47
|
+
3. **Injects skills** — copies `SKILL.md` files and associated content into `.cursor/skills/{name}/`, where Cursor discovers them.
|
|
48
|
+
4. **Injects hooks** — copies hook directories into `.cursor/hooks/{name}/` and registers their command in `hooks.json`, anchored to the repo root.
|
|
49
|
+
5. **Copies references** — attaches referenced documents into `{artifact}/references/`.
|
|
50
|
+
6. **Respects local priority** — if a skill or hook directory already exists in the target, it is not overwritten.
|
|
51
|
+
|
|
52
|
+
## Translation Details
|
|
53
|
+
|
|
54
|
+
| AIR Format | Cursor Format |
|
|
55
|
+
|------------|---------------|
|
|
56
|
+
| `mcp.json` (flat map with `type`, `title`, `description`) | `mcpServers.<name>` entries in `.cursor/mcp.json` (metadata stripped) |
|
|
57
|
+
| `stdio` servers | `{ command, args, env }` |
|
|
58
|
+
| `sse` / `streamable-http` servers | `{ url, headers }` (Cursor auto-detects transport from the URL) |
|
|
59
|
+
| Any `${VAR}` ref in `command` / `args` / `env` / `url` / `headers` | rewritten to Cursor-native `${env:VAR}` (anywhere in the string) |
|
|
60
|
+
| Skills (`SKILL.md` + content) | `.cursor/skills/{name}/` |
|
|
61
|
+
| Hooks (`HOOK.json` + scripts) | `.cursor/hooks/{name}/` + `hooks.<event>` registration in `.cursor/hooks.json` |
|
|
62
|
+
| Hook events `session_start`, `session_end`, `pre_tool_call`, `post_tool_call`, `user_prompt_submit`, `stop`, `subagent_stop`, `pre_compact` | Cursor `sessionStart`, `sessionEnd`, `preToolUse`, `postToolUse`, `beforeSubmitPrompt`, `stop`, `subagentStop`, `preCompact` |
|
|
63
|
+
| References | `{artifact}/references/` |
|
|
64
|
+
|
|
65
|
+
## Secrets
|
|
66
|
+
|
|
67
|
+
Cursor natively expands `${env:VAR}` references in `mcp.json` values. Instead of writing AIR `${VAR}` placeholders, the adapter rewrites every AIR `${VAR}` reference to Cursor's **`${env:VAR}`** form at translation time:
|
|
68
|
+
|
|
69
|
+
- `env: { GITHUB_TOKEN: "${GITHUB_TOKEN}" }` → `env: { GITHUB_TOKEN: "${env:GITHUB_TOKEN}" }`
|
|
70
|
+
- `headers: { Authorization: "Bearer ${API_TOKEN}" }` → `headers: { Authorization: "Bearer ${env:API_TOKEN}" }`
|
|
71
|
+
- `env: { KEY: "${OTHER}" }` (renamed) → `env: { KEY: "${env:OTHER}" }`
|
|
72
|
+
|
|
73
|
+
Cursor expands these from the host environment at launch. As a result, `prepareSession()` returns an **empty `configFiles` array** — there is no AIR `${VAR}` left for secret transforms to post-process.
|
|
74
|
+
|
|
75
|
+
**No unforwardable shapes.** Because Cursor's `${env:VAR}` interpolation works *anywhere in a string*, every secret reference forwards cleanly — whole-value, partial, and renamed alike. The adapter therefore never warns about secrets. Cursor's own built-in interpolation tokens (`${userHome}`, `${workspaceFolder}`, `${workspaceFolderBasename}`, `${pathSeparator}`) and already-`${env:…}` references are left untouched so they are not double-wrapped.
|
|
76
|
+
|
|
77
|
+
## Known gaps
|
|
78
|
+
|
|
79
|
+
These AIR features have no static Cursor equivalent and are handled out of band:
|
|
80
|
+
|
|
81
|
+
- **OAuth MCP servers** — AIR's detailed OAuth config (`clientId`/`scopes`/`redirectUri`/…) has no static `mcp.json` form. Cursor performs interactive OAuth via `cursor-agent mcp login <name>`.
|
|
82
|
+
- **Plugins** — Cursor has no local plugin package an adapter can materialize. AIR treats plugins as composition sugar: a plugin's declared MCP servers / skills / hooks are expanded into the activation set and materialized as their underlying Cursor-native artifacts.
|
|
83
|
+
- **Subagent context** — Cursor has no `--append-system-prompt` flag, so subagent-root context is returned to the caller via `PreparedSession.subagentContext` rather than passed to the CLI.
|
|
84
|
+
- **`notification` hook event** — AIR's `notification` event has no Cursor equivalent; hooks targeting it are skipped with a `console.warn`.
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type { AgentAdapter, AgentSessionConfig, StartCommand, ResolvedArtifacts, RootEntry, McpServerEntry, PluginEntry, PrepareSessionOptions, PreparedSession, CleanSessionOptions, CleanSessionResult, LocalArtifacts } from "@pulsemcp/air-core";
|
|
2
|
+
export declare class CursorAdapter implements AgentAdapter {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
/**
|
|
6
|
+
* Map AIR lifecycle event names to Cursor `hooks.json` event names.
|
|
7
|
+
*
|
|
8
|
+
* Accepts both snake_case AIR names and camelCase Cursor event names as
|
|
9
|
+
* identity mappings, so hook authors targeting the Cursor runtime can write
|
|
10
|
+
* Cursor-native event names directly without translating to snake_case.
|
|
11
|
+
*
|
|
12
|
+
* The only AIR event without a Cursor equivalent is `notification` — it is
|
|
13
|
+
* intentionally absent, so `reconcileHooks` warns and skips registration when
|
|
14
|
+
* it encounters it. Cursor-only events (beforeShellExecution, afterFileEdit,
|
|
15
|
+
* etc.) have no AIR analog and are therefore not generated by AIR, but
|
|
16
|
+
* user-authored hooks targeting them are preserved.
|
|
17
|
+
*/
|
|
18
|
+
private static readonly AIR_TO_CURSOR_EVENT;
|
|
19
|
+
/** Canonical AIR snake_case event names with a Cursor mapping. */
|
|
20
|
+
private static readonly AIR_EVENT_NAMES;
|
|
21
|
+
isAvailable(): Promise<boolean>;
|
|
22
|
+
generateConfig(artifacts: ResolvedArtifacts, root?: RootEntry, _workDir?: string): AgentSessionConfig;
|
|
23
|
+
buildStartCommand(config: AgentSessionConfig): StartCommand;
|
|
24
|
+
/**
|
|
25
|
+
* Prepare a working directory for a Cursor CLI session.
|
|
26
|
+
*
|
|
27
|
+
* Writes `.cursor/mcp.json` (MCP servers under `mcpServers`) and
|
|
28
|
+
* `.cursor/hooks.json` (hook registrations under `hooks.<event>`), injects
|
|
29
|
+
* skills + references into `.cursor/skills/<name>/`, copies path-based hooks
|
|
30
|
+
* into `.cursor/hooks/<name>/`, and returns the start command.
|
|
31
|
+
*
|
|
32
|
+
* Inputs to activation lists (root defaults, overrides, plugin-declared
|
|
33
|
+
* primitives) are accepted as either qualified (`@scope/id`) or short form;
|
|
34
|
+
* ambiguous short forms are rejected. Filesystem materialization uses
|
|
35
|
+
* shortnames — Cursor's config files, `.cursor/skills/`, `.cursor/hooks/`,
|
|
36
|
+
* and the manifest are scope-naive. Two activated qualified IDs that share
|
|
37
|
+
* a shortname hard-fail with a clear "add one to exclude" message.
|
|
38
|
+
*
|
|
39
|
+
* NOTE: `configFiles` is intentionally returned empty. AIR `${VAR}`
|
|
40
|
+
* references in MCP env/headers are rewritten to Cursor's native
|
|
41
|
+
* `${env:VAR}` interpolation at translation time, so there is no resolvable
|
|
42
|
+
* `${VAR}` left for AIR's transform/validation pipeline. The `.cursor/mcp.json`
|
|
43
|
+
* we wrote above is deliberately not surfaced as a config file.
|
|
44
|
+
*/
|
|
45
|
+
prepareSession(artifacts: ResolvedArtifacts, targetDir: string, options?: PrepareSessionOptions): Promise<PreparedSession>;
|
|
46
|
+
/**
|
|
47
|
+
* Enumerate skills checked into `<targetDir>/.cursor/skills/`. Cursor loads
|
|
48
|
+
* these directly from the filesystem regardless of AIR's involvement, so
|
|
49
|
+
* they're always active and must not be overwritten or removed. The TUI uses
|
|
50
|
+
* this list to surface them as read-only entries.
|
|
51
|
+
*/
|
|
52
|
+
listLocalArtifacts(targetDir: string): Promise<LocalArtifacts>;
|
|
53
|
+
/**
|
|
54
|
+
* Remove every artifact AIR has previously written into `targetDir`.
|
|
55
|
+
*
|
|
56
|
+
* Reads the per-target manifest, deletes each tracked skill / hook
|
|
57
|
+
* directory, removes tracked MCP server keys from `.cursor/mcp.json`, and
|
|
58
|
+
* removes AIR-managed hook entries from `.cursor/hooks.json`. When every
|
|
59
|
+
* category is cleaned, the manifest itself is deleted; partial cleans (any
|
|
60
|
+
* `keep*` flag set) update the manifest with the kept entries so future runs
|
|
61
|
+
* continue to track them.
|
|
62
|
+
*
|
|
63
|
+
* Items in the manifest that no longer exist on disk are silently skipped
|
|
64
|
+
* — the manifest can drift if a user removed files manually between runs.
|
|
65
|
+
*/
|
|
66
|
+
cleanSession(targetDir: string, options?: CleanSessionOptions): Promise<CleanSessionResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Read `.cursor/mcp.json` and return the subset of `ids` whose key is
|
|
69
|
+
* actually present under `mcpServers`. Returns an empty list if the file
|
|
70
|
+
* can't be parsed — we'd rather under-report than claim to have removed
|
|
71
|
+
* entries we never touched.
|
|
72
|
+
*/
|
|
73
|
+
private mcpServerIdsPresent;
|
|
74
|
+
/**
|
|
75
|
+
* Return true if `.cursor/hooks.json` contains at least one AIR-owned hook
|
|
76
|
+
* entry (`_air_hook_id` ∈ `managedHookIds`). Used to avoid rewriting a file
|
|
77
|
+
* we wouldn't actually change.
|
|
78
|
+
*/
|
|
79
|
+
private hookEntriesPresent;
|
|
80
|
+
/**
|
|
81
|
+
* Resolve subagent roots from the root's default_subagent_roots.
|
|
82
|
+
* IDs are already qualified after composition-time canonicalization.
|
|
83
|
+
*/
|
|
84
|
+
private resolveSubagentRoots;
|
|
85
|
+
/**
|
|
86
|
+
* Merge subagent roots' default_mcp_servers and default_skills into the
|
|
87
|
+
* parent's activated sets (union, preserving order with parent first).
|
|
88
|
+
*/
|
|
89
|
+
private mergeSubagentArtifacts;
|
|
90
|
+
/**
|
|
91
|
+
* Build a system prompt section describing the subagent root dependencies.
|
|
92
|
+
*/
|
|
93
|
+
private buildSubagentContext;
|
|
94
|
+
/**
|
|
95
|
+
* Translate a shortname-keyed MCP server map into Cursor's `mcpServers`
|
|
96
|
+
* shape (a plain object ready for JSON serialization). Callers convert
|
|
97
|
+
* qualified IDs to shortnames before invoking this — Cursor's config is
|
|
98
|
+
* scope-naive.
|
|
99
|
+
*
|
|
100
|
+
* Secret handling is Cursor-native: AIR `${VAR}` references in any string
|
|
101
|
+
* value (command, args, env values, url, headers) are rewritten to Cursor's
|
|
102
|
+
* `${env:VAR}` interpolation, which Cursor expands from the host environment
|
|
103
|
+
* at launch (see `toCursorVars`). Because Cursor's interpolation works
|
|
104
|
+
* anywhere in a string, partial (`"Bearer ${TOKEN}"`) and renamed
|
|
105
|
+
* (`KEY = "${OTHER}"`) references all forward cleanly — there are no
|
|
106
|
+
* unforwardable shapes, so no warnings are emitted.
|
|
107
|
+
*/
|
|
108
|
+
translateMcpServersByShort(servers: Record<string, McpServerEntry>): Record<string, Record<string, unknown>>;
|
|
109
|
+
private translateMcpServer;
|
|
110
|
+
/**
|
|
111
|
+
* Rewrite AIR `${VAR}` references into Cursor's native `${env:VAR}`
|
|
112
|
+
* interpolation. Each `${...}` token is wrapped unless it is already a
|
|
113
|
+
* Cursor `${env:...}` reference or one of Cursor's built-in interpolation
|
|
114
|
+
* tokens (`${userHome}`, `${workspaceFolder}`, …), which must be left
|
|
115
|
+
* untouched. Strings without a `${...}` token are returned unchanged.
|
|
116
|
+
*/
|
|
117
|
+
private toCursorVars;
|
|
118
|
+
/**
|
|
119
|
+
* Translate an AIR plugin to a Cursor-facing descriptor.
|
|
120
|
+
*
|
|
121
|
+
* Cursor's marketplace plugins are remote-installed and have no local package
|
|
122
|
+
* file an adapter can materialize. AIR therefore treats plugins as composition
|
|
123
|
+
* sugar: a plugin's declared MCP servers / skills / hooks are expanded into the
|
|
124
|
+
* activation set and materialized as their underlying Cursor-native artifacts.
|
|
125
|
+
* This descriptor is informational only.
|
|
126
|
+
*/
|
|
127
|
+
translatePlugin(shortId: string, plugin: PluginEntry): Record<string, unknown>;
|
|
128
|
+
/**
|
|
129
|
+
* Resolve a list of activation IDs (each qualified or short) into qualified
|
|
130
|
+
* IDs paired with shortnames suitable for filesystem materialization.
|
|
131
|
+
*
|
|
132
|
+
* Throws on:
|
|
133
|
+
* - unknown IDs (after attempting both qualified and short-form lookup)
|
|
134
|
+
* - ambiguous short references (multiple scopes contribute the shortname)
|
|
135
|
+
* - shortname collisions in the activation set itself (two qualified IDs
|
|
136
|
+
* with the same shortname can't share a single materialization dir)
|
|
137
|
+
*/
|
|
138
|
+
private resolveActivations;
|
|
139
|
+
/**
|
|
140
|
+
* Build the warning emitted when a registered artifact's `path` does not
|
|
141
|
+
* exist on disk at materialization time. The qualified ID encodes the
|
|
142
|
+
* declaring catalog's scope, so a reviewer can trace the offending entry
|
|
143
|
+
* back to its index file. Materialization is skipped for this artifact and
|
|
144
|
+
* the rest of the session proceeds.
|
|
145
|
+
*/
|
|
146
|
+
private missingSourceDirMessage;
|
|
147
|
+
private formatPoolKeys;
|
|
148
|
+
/**
|
|
149
|
+
* Copy referenced documents into a references/ subdirectory of the target.
|
|
150
|
+
* `refIds` are qualified IDs (post-canonicalization).
|
|
151
|
+
*/
|
|
152
|
+
private copyReferences;
|
|
153
|
+
/** Parse an existing JSON config, returning `{}` when absent/unparseable. */
|
|
154
|
+
private readJson;
|
|
155
|
+
private writeJson;
|
|
156
|
+
/**
|
|
157
|
+
* Merge AIR-managed MCP servers into `.cursor/mcp.json`, preserving any
|
|
158
|
+
* user-authored config. Keys in `staleMcpIds` are removed; keys in
|
|
159
|
+
* `translatedServers` are set/replaced; other servers and top-level keys pass
|
|
160
|
+
* through. A run that leaves the config empty deletes the file rather than
|
|
161
|
+
* leaving an empty stub. Returns the path written, or null if the file was
|
|
162
|
+
* deleted / not created.
|
|
163
|
+
*/
|
|
164
|
+
private writeCursorMcpConfig;
|
|
165
|
+
/**
|
|
166
|
+
* Merge AIR-owned hook registrations into `.cursor/hooks.json`, preserving
|
|
167
|
+
* any user-authored hooks. AIR-owned entries (tagged with `_air_hook_id`)
|
|
168
|
+
* whose ID is in `managedHookIds` are pruned, then the current selection is
|
|
169
|
+
* registered. A run that leaves no hooks deletes the file (an empty hooks
|
|
170
|
+
* config is a useless stub) unless the user kept other top-level keys.
|
|
171
|
+
* Returns the path written, or null if the file was deleted / not created.
|
|
172
|
+
*/
|
|
173
|
+
private writeCursorHooksConfig;
|
|
174
|
+
private isManagedHookEntry;
|
|
175
|
+
/**
|
|
176
|
+
* Remove `mcpIds` from `mcpServers` in `.cursor/mcp.json`, preserving
|
|
177
|
+
* user-authored entries and other top-level fields. Returns the path of the
|
|
178
|
+
* file that was rewritten, or null if the file became empty and was deleted.
|
|
179
|
+
*/
|
|
180
|
+
private pruneCursorMcpConfig;
|
|
181
|
+
/**
|
|
182
|
+
* Remove AIR-managed hook entries (matched by `_air_hook_id` ∈
|
|
183
|
+
* `managedHookIds`) from `.cursor/hooks.json`, preserving user-authored
|
|
184
|
+
* entries and other top-level fields. Returns the path of the file that was
|
|
185
|
+
* rewritten, or null if no hooks remain and the file was deleted.
|
|
186
|
+
*/
|
|
187
|
+
private pruneCursorHooksConfig;
|
|
188
|
+
/**
|
|
189
|
+
* Build a shell command string from HOOK.json's command and args fields.
|
|
190
|
+
*
|
|
191
|
+
* Hook authors write paths relative to their own hook directory. Cursor
|
|
192
|
+
* invokes hooks with a working directory that is not guaranteed to be the
|
|
193
|
+
* project root, so hook-relative paths are anchored to the repository root
|
|
194
|
+
* via `"$(git rev-parse --show-toplevel)/.cursor/hooks/<id>/<path>"`. The
|
|
195
|
+
* double quotes keep the path safe for project directories with spaces.
|
|
196
|
+
*
|
|
197
|
+
* - `command` is anchored if it starts with `./` (explicit hook-relative).
|
|
198
|
+
* - Each `args` entry is anchored if it looks like a path AND the file
|
|
199
|
+
* exists under the hook's installed directory.
|
|
200
|
+
*
|
|
201
|
+
* Args that are not rewritten and contain shell metacharacters are
|
|
202
|
+
* single-quoted for safety.
|
|
203
|
+
*/
|
|
204
|
+
private buildHookCommand;
|
|
205
|
+
/**
|
|
206
|
+
* Wrap a project-root-relative path in `"$(git rev-parse --show-toplevel)/..."`
|
|
207
|
+
* so the resolved path is independent of the cwd at hook invocation. Double
|
|
208
|
+
* quotes are required so the command substitution runs; characters that are
|
|
209
|
+
* special inside double quotes (`$`, `` ` ``, `"`, `\`) are escaped in the
|
|
210
|
+
* path component so an unusual hook ID or filename can't break out of the
|
|
211
|
+
* quoting.
|
|
212
|
+
*/
|
|
213
|
+
private anchorHookPath;
|
|
214
|
+
/**
|
|
215
|
+
* If `arg` is a hook-relative path that points at a real file under the
|
|
216
|
+
* hook's installed directory, return its project-root-relative form
|
|
217
|
+
* (`.cursor/hooks/<id>/<path>`) flagged as anchored so the caller can wrap it.
|
|
218
|
+
* Otherwise return `arg` unchanged with `anchored: false`.
|
|
219
|
+
*/
|
|
220
|
+
private rewriteHookArgPath;
|
|
221
|
+
/** Human-readable list of the supported AIR hook event names (snake_case only). */
|
|
222
|
+
private supportedEventList;
|
|
223
|
+
private copyDirRecursive;
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=cursor-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-adapter.d.ts","sourceRoot":"","sources":["../src/cursor-adapter.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,cAAc,EACd,WAAW,EACX,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EAEf,MAAM,oBAAoB,CAAC;AAsC5B,qBAAa,aAAc,YAAW,YAAY;IAChD,IAAI,SAAY;IAChB,WAAW,SAAY;IAEvB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAgCzC;IAEF,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CASrC;IAEI,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IASrC,cAAc,CACZ,SAAS,EAAE,iBAAiB,EAC5B,IAAI,CAAC,EAAE,SAAS,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,kBAAkB;IAmDrB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY;IAa3D;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,cAAc,CAClB,SAAS,EAAE,iBAAiB,EAC5B,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,eAAe,CAAC;IAqM3B;;;;;OAKG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAIpE;;;;;;;;;;;;OAYG;IACG,YAAY,CAChB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC;IA6G9B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAM3B;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAwB9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA4B5B;;;;;;;;;;;;;OAaG;IACH,0BAA0B,CACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GACtC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAQ1C,OAAO,CAAC,kBAAkB;IAgC1B;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IAUpB;;;;;;;;OAQG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQ9E;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;IAgD1B;;;;;;OAMG;IACH,OAAO,CAAC,uBAAuB;IAY/B,OAAO,CAAC,cAAc;IAStB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAyBtB,6EAA6E;IAC7E,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,SAAS;IAKjB;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IA0B5B;;;;;;;OAOG;IACH,OAAO,CAAC,sBAAsB;IA8F9B,OAAO,CAAC,kBAAkB;IAO1B;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAkC9B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,gBAAgB;IA4BxB;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;IAKtB;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAoB1B,mFAAmF;IACnF,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,gBAAgB;CAYzB"}
|