zcode-acp-server 0.46.7 → 0.47.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.
Files changed (55) hide show
  1. package/README.md +1 -1
  2. package/dist/code-fingerprint.json +2 -2
  3. package/dist/remote/endpoint.d.ts.map +1 -1
  4. package/dist/remote/endpoint.js +6 -0
  5. package/dist/remote/endpoint.js.map +1 -1
  6. package/dist/remote/hub-server.d.ts.map +1 -1
  7. package/dist/remote/hub-server.js +102 -0
  8. package/dist/remote/hub-server.js.map +1 -1
  9. package/dist/remote/settings-endpoint.d.ts +45 -0
  10. package/dist/remote/settings-endpoint.d.ts.map +1 -0
  11. package/dist/remote/settings-endpoint.js +920 -0
  12. package/dist/remote/settings-endpoint.js.map +1 -0
  13. package/dist/settings/agents-config.d.ts +158 -0
  14. package/dist/settings/agents-config.d.ts.map +1 -0
  15. package/dist/settings/agents-config.js +598 -0
  16. package/dist/settings/agents-config.js.map +1 -0
  17. package/dist/settings/app-update.d.ts +193 -0
  18. package/dist/settings/app-update.d.ts.map +1 -0
  19. package/dist/settings/app-update.js +659 -0
  20. package/dist/settings/app-update.js.map +1 -0
  21. package/dist/settings/atomic-write.d.ts +106 -0
  22. package/dist/settings/atomic-write.d.ts.map +1 -0
  23. package/dist/settings/atomic-write.js +228 -0
  24. package/dist/settings/atomic-write.js.map +1 -0
  25. package/dist/settings/cli-config.d.ts +145 -0
  26. package/dist/settings/cli-config.d.ts.map +1 -0
  27. package/dist/settings/cli-config.js +320 -0
  28. package/dist/settings/cli-config.js.map +1 -0
  29. package/dist/settings/coding-plan.d.ts +106 -0
  30. package/dist/settings/coding-plan.d.ts.map +1 -0
  31. package/dist/settings/coding-plan.js +299 -0
  32. package/dist/settings/coding-plan.js.map +1 -0
  33. package/dist/settings/file-lock.d.ts +39 -0
  34. package/dist/settings/file-lock.d.ts.map +1 -0
  35. package/dist/settings/file-lock.js +282 -0
  36. package/dist/settings/file-lock.js.map +1 -0
  37. package/dist/settings/provider-config.d.ts +123 -0
  38. package/dist/settings/provider-config.d.ts.map +1 -0
  39. package/dist/settings/provider-config.js +311 -0
  40. package/dist/settings/provider-config.js.map +1 -0
  41. package/dist/settings/skills.d.ts +72 -0
  42. package/dist/settings/skills.d.ts.map +1 -0
  43. package/dist/settings/skills.js +245 -0
  44. package/dist/settings/skills.js.map +1 -0
  45. package/dist/settings/usage-stats.d.ts +74 -0
  46. package/dist/settings/usage-stats.d.ts.map +1 -0
  47. package/dist/settings/usage-stats.js +235 -0
  48. package/dist/settings/usage-stats.js.map +1 -0
  49. package/dist/utils.d.ts +14 -0
  50. package/dist/utils.d.ts.map +1 -1
  51. package/dist/utils.js +22 -0
  52. package/dist/utils.js.map +1 -1
  53. package/docs/ARCHITECTURE.md +65 -0
  54. package/docs/REMOTE-CLIENTS.md +252 -0
  55. package/package.json +1 -1
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Read/write for the ZCode CLI config (`~/.zcode/cli/config.json`).
3
+ *
4
+ * ONE file, four independent owners: skills enablement, MCP servers, hooks, and
5
+ * plugins. Every writer is therefore a read-modify-write that preserves keys it
6
+ * does not recognise — the file also carries agent-runtime settings owned by
7
+ * the backend, and rewriting from a partial model would silently delete them.
8
+ *
9
+ * The enable/disable spellings are not symmetric, and getting them wrong makes
10
+ * a toggle look broken while the file is technically valid:
11
+ *
12
+ * - **skills**: key = the SKILL.md absolute path, value `{enable: false}`.
13
+ * `enable: true` is the DEFAULT and must DELETE the key — writing `true`
14
+ * leaves a permanent override that shadows later enablement changes.
15
+ * - **MCP**: `enabled: false` inside the server object. Same rule: `true`
16
+ * deletes the key (and a legacy `enable` key is migrated on the way).
17
+ * - **hooks**: a whole different shape — a nested tree under `hooks`, gated by
18
+ * a top-level `hooks.enabled: true`. WITHOUT that flag every hook in the file
19
+ * is inert, which is the single most common way a hand-written config does
20
+ * nothing.
21
+ * - **plugins**: `enabledPlugins` / `suppressedBuiltins` maps keyed by
22
+ * `<name>@<marketplace>`.
23
+ *
24
+ * None of these are hot-reloaded by the agent: `cli/config.json` is read once
25
+ * at agent start (MCP servers are frozen into the runtime config), except the
26
+ * skills enablement map which the host re-reads live. Writes therefore report
27
+ * `needs-restart` for MCP and hooks, `immediate` for skills.
28
+ */
29
+ import { readFile } from "node:fs/promises";
30
+ import { readJsonDocument, writeJsonAtomic } from "./atomic-write.js";
31
+ import { zcodeCliConfigPath } from "../utils.js";
32
+ /** The seven hook events the schema accepts, in its own order. */
33
+ export const HOOK_EVENT_NAMES = [
34
+ "SessionStart",
35
+ "UserPromptSubmit",
36
+ "PreToolUse",
37
+ "PermissionRequest",
38
+ "PostToolUse",
39
+ "PostToolUseFailure",
40
+ "Stop",
41
+ ];
42
+ /**
43
+ * Read the CLI config.
44
+ *
45
+ * @throws when the file exists but is malformed. Unlike the app's skills
46
+ * writer (which silently reads damage as `{}` and would then rewrite
47
+ * the file from an empty base), this refuses — the caller surfaces an
48
+ * error instead of destroying what is left of the document.
49
+ */
50
+ export async function readCliConfig() {
51
+ const doc = await readJsonDocument(zcodeCliConfigPath());
52
+ return (doc ?? {});
53
+ }
54
+ /** Structural check for the parts of the file this module owns. */
55
+ export function validateCliConfig(doc) {
56
+ const skills = doc["skills"];
57
+ if (skills !== undefined && !isRecord(skills))
58
+ return "skills must be an object";
59
+ const mcp = doc["mcp"];
60
+ if (mcp !== undefined) {
61
+ if (!isRecord(mcp))
62
+ return "mcp must be an object";
63
+ const servers = mcp["servers"];
64
+ if (servers !== undefined && !isRecord(servers))
65
+ return "mcp.servers must be an object";
66
+ if (isRecord(servers)) {
67
+ for (const [name, server] of Object.entries(servers)) {
68
+ if (!isRecord(server))
69
+ return `mcp.servers.${name} must be an object`;
70
+ }
71
+ }
72
+ }
73
+ const hooks = doc["hooks"];
74
+ if (hooks !== undefined) {
75
+ if (!isRecord(hooks))
76
+ return "hooks must be an object";
77
+ const events = hooks["events"];
78
+ if (events !== undefined) {
79
+ if (!isRecord(events))
80
+ return "hooks.events must be an object";
81
+ for (const [event, matchers] of Object.entries(events)) {
82
+ if (!HOOK_EVENT_NAMES.includes(event)) {
83
+ return `hooks.events.${event} is not a known event name`;
84
+ }
85
+ if (!Array.isArray(matchers))
86
+ return `hooks.events.${event} must be an array`;
87
+ for (const matcher of matchers) {
88
+ if (!isRecord(matcher))
89
+ return `hooks.events.${event} entries must be objects`;
90
+ if (!Array.isArray(matcher["hooks"]) || matcher["hooks"].length === 0) {
91
+ return `hooks.events.${event} matcher needs at least one hook`;
92
+ }
93
+ for (const hook of matcher["hooks"]) {
94
+ if (!isRecord(hook))
95
+ return "a hook entry must be an object";
96
+ const type = hook["type"];
97
+ if (type !== "command" && type !== "process") {
98
+ return "a hook entry needs type 'command' or 'process'";
99
+ }
100
+ if (typeof hook["command"] !== "string" || !hook["command"]) {
101
+ return "a hook entry needs a non-empty command";
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }
108
+ const plugins = doc["plugins"];
109
+ if (plugins !== undefined && !isRecord(plugins))
110
+ return "plugins must be an object";
111
+ return true;
112
+ }
113
+ function isRecord(value) {
114
+ return typeof value === "object" && value !== null && !Array.isArray(value);
115
+ }
116
+ /** The single write path for this file. */
117
+ async function saveCliConfig(mutator) {
118
+ const { doc } = await writeJsonAtomic(zcodeCliConfigPath(), async (current) => {
119
+ const next = await mutator(current);
120
+ return next;
121
+ }, { validate: (d) => validateCliConfig(d) });
122
+ return doc;
123
+ }
124
+ // ---------- skills ----------
125
+ /**
126
+ * Set a skill's enabled state.
127
+ *
128
+ * `enable: true` REMOVES the override rather than writing `true` — enabled is
129
+ * the default state, and a stored `true` is a permanent pin that would shadow
130
+ * the file's real state.
131
+ */
132
+ export async function setSkillEnabled(skillPath, enable) {
133
+ return saveCliConfig((doc) => {
134
+ const skills = { ...(doc.skills ?? {}) };
135
+ if (enable)
136
+ delete skills[skillPath];
137
+ else
138
+ skills[skillPath] = { enable: false };
139
+ return applySkillsBlock(doc, skills);
140
+ });
141
+ }
142
+ /** Drop the whole `skills` block when every entry is enabled (the app's rule). */
143
+ function applySkillsBlock(doc, skills) {
144
+ const next = { ...doc };
145
+ if (Object.keys(skills).length === 0)
146
+ delete next.skills;
147
+ else
148
+ next.skills = skills;
149
+ return next;
150
+ }
151
+ // ---------- mcp ----------
152
+ /**
153
+ * Create or update an MCP server.
154
+ *
155
+ * `enabled: true` deletes the flag (and a legacy `enable` key) instead of
156
+ * storing it; only a DISABLE is worth persisting.
157
+ */
158
+ export async function upsertMcpServer(name, server) {
159
+ assertMcpName(name);
160
+ return saveCliConfig((doc) => {
161
+ const servers = { ...(doc.mcp?.servers ?? {}) };
162
+ const existing = servers[name] ?? {};
163
+ const merged = { ...existing, ...server };
164
+ delete merged["enable"];
165
+ if (merged.enabled === true)
166
+ delete merged.enabled;
167
+ servers[name] = merged;
168
+ return { ...doc, mcp: { ...(doc.mcp ?? {}), servers } };
169
+ });
170
+ }
171
+ /** Enable or disable an existing MCP server. */
172
+ export async function setMcpServerEnabled(name, enabled) {
173
+ return saveCliConfig((doc) => {
174
+ const servers = { ...(doc.mcp?.servers ?? {}) };
175
+ const existing = servers[name];
176
+ if (!existing)
177
+ throw new Error(`MCP server '${name}' is not configured`);
178
+ const merged = { ...existing };
179
+ delete merged["enable"];
180
+ if (enabled)
181
+ delete merged.enabled;
182
+ else
183
+ merged.enabled = false;
184
+ servers[name] = merged;
185
+ return { ...doc, mcp: { ...(doc.mcp ?? {}), servers } };
186
+ });
187
+ }
188
+ export async function removeMcpServer(name) {
189
+ return saveCliConfig((doc) => {
190
+ const servers = { ...(doc.mcp?.servers ?? {}) };
191
+ if (!(name in servers))
192
+ throw new Error(`MCP server '${name}' is not configured`);
193
+ delete servers[name];
194
+ return { ...doc, mcp: { ...(doc.mcp ?? {}), servers } };
195
+ });
196
+ }
197
+ function assertMcpName(name) {
198
+ if (!name || /[\\/]/.test(name)) {
199
+ throw new Error("MCP server name must be non-empty and free of path separators");
200
+ }
201
+ }
202
+ // ---------- hooks ----------
203
+ /**
204
+ * Read the hooks tree. Returns the raw config; an absent file yields `{}`.
205
+ *
206
+ * Note the returned `enabled` flag: hooks are inert unless the top-level flag
207
+ * is `true`, so callers must surface it rather than inferring it.
208
+ */
209
+ export async function readHooks() {
210
+ const doc = await readCliConfig();
211
+ return doc.hooks ?? {};
212
+ }
213
+ /**
214
+ * Set the global hooks switch.
215
+ *
216
+ * A file full of hooks with `enabled` absent runs NOTHING — the runtime root
217
+ * defaults to disabled. This is the one field that turns the whole tree on.
218
+ */
219
+ export async function setHooksEnabled(enabled) {
220
+ return saveCliConfig((doc) => {
221
+ const hooks = { ...(doc.hooks ?? {}) };
222
+ if (enabled)
223
+ hooks.enabled = true;
224
+ else
225
+ delete hooks.enabled;
226
+ return { ...doc, hooks };
227
+ });
228
+ }
229
+ /**
230
+ * Edit ONE existing hook entry in place.
231
+ *
232
+ * Deliberately not a create path: the matcher list is an array of arrays, and
233
+ * an insert shifts every later index, so a remote "add a hook" call races with
234
+ * any concurrent edit of the same event. Editing an existing entry's
235
+ * command/timeout/enabled covers the real use case (temporarily disable one,
236
+ * tweak a command) without that hazard.
237
+ *
238
+ * Unknown keys on the hook object are preserved: the schema is `.passthrough()`
239
+ * precisely so extensions can ride along.
240
+ */
241
+ export async function updateHookEntry(event, matcherIndex, hookIndex, patch) {
242
+ if (!HOOK_EVENT_NAMES.includes(event)) {
243
+ throw new Error(`unknown hook event '${event}'`);
244
+ }
245
+ if (!Number.isInteger(matcherIndex) || matcherIndex < 0) {
246
+ throw new Error("matcherIndex must be a non-negative integer");
247
+ }
248
+ if (!Number.isInteger(hookIndex) || hookIndex < 0) {
249
+ throw new Error("hookIndex must be a non-negative integer");
250
+ }
251
+ if (patch.timeoutMs !== undefined &&
252
+ (!Number.isFinite(patch.timeoutMs) || patch.timeoutMs <= 0)) {
253
+ throw new Error("timeoutMs must be a positive number");
254
+ }
255
+ if (patch.timeout !== undefined && (!Number.isFinite(patch.timeout) || patch.timeout <= 0)) {
256
+ throw new Error("timeout must be a positive number of seconds");
257
+ }
258
+ if (patch.command !== undefined && !patch.command) {
259
+ throw new Error("command must be non-empty");
260
+ }
261
+ return saveCliConfig((doc) => {
262
+ const hooks = { ...(doc.hooks ?? {}) };
263
+ const events = { ...(hooks.events ?? {}) };
264
+ const matchers = [...(events[event] ?? [])];
265
+ const matcher = matchers[matcherIndex];
266
+ if (!matcher)
267
+ throw new Error(`no matcher at index ${matcherIndex} for ${event}`);
268
+ const entries = [...matcher.hooks];
269
+ const entry = entries[hookIndex];
270
+ if (!entry)
271
+ throw new Error(`no hook at index ${hookIndex} in ${event} matcher ${matcherIndex}`);
272
+ // Explicit `enabled: true` removes the flag: enabled is the default, and a
273
+ // stored `true` is a pin that survives later enablement changes.
274
+ const merged = { ...entry };
275
+ for (const [key, value] of Object.entries(patch)) {
276
+ if (value === undefined)
277
+ continue;
278
+ if (key === "enabled") {
279
+ if (value === true)
280
+ delete merged.enabled;
281
+ else
282
+ merged.enabled = value;
283
+ continue;
284
+ }
285
+ merged[key] = value;
286
+ }
287
+ entries[hookIndex] = merged;
288
+ matchers[matcherIndex] = { ...matcher, hooks: entries };
289
+ events[event] = matchers;
290
+ hooks.events = events;
291
+ return { ...doc, hooks };
292
+ });
293
+ }
294
+ // ---------- plugins ----------
295
+ /** Enable or disable a plugin by its `<name>@<marketplace>` key. */
296
+ export async function setPluginEnabled(pluginKey, enabled) {
297
+ if (!pluginKey.includes("@")) {
298
+ throw new Error("plugin key must be '<name>@<marketplace>'");
299
+ }
300
+ return saveCliConfig((doc) => {
301
+ const plugins = { ...(doc.plugins ?? {}) };
302
+ const enabledPlugins = { ...(plugins.enabledPlugins ?? {}) };
303
+ if (enabled)
304
+ delete enabledPlugins[pluginKey];
305
+ else
306
+ enabledPlugins[pluginKey] = false;
307
+ plugins.enabledPlugins = enabledPlugins;
308
+ return { ...doc, plugins };
309
+ });
310
+ }
311
+ /** Raw text for the settings snapshot. Absent → null. */
312
+ export async function readCliConfigRaw() {
313
+ try {
314
+ return await readFile(zcodeCliConfigPath(), "utf8");
315
+ }
316
+ catch {
317
+ return null;
318
+ }
319
+ }
320
+ //# sourceMappingURL=cli-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-config.js","sourceRoot":"","sources":["../../src/settings/cli-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Coding-plan quota reset cards.
3
+ *
4
+ * The desktop app exposes "reset cards" — a limited number of quota resets the
5
+ * user can spend to clear a 5-hour or weekly window. This module is the headless
6
+ * equivalent: the same four endpoints, the same headers, the same credentials,
7
+ * so a client that cannot open the app still gets the feature.
8
+ *
9
+ * Credentials are the hard part. They live in `~/.zcode/v2/credentials.json`,
10
+ * AES-256-GCM encrypted, keyed by `ZCODE_CREDENTIAL_SECRET` or a machine-bound
11
+ * fallback string. Two tokens are needed and they are NOT interchangeable:
12
+ *
13
+ * - `zcodejwttoken` → `Authorization: Bearer …`
14
+ * - the OAuth access token for the CURRENT provider family → `X-Bigmodel-Authorization`
15
+ *
16
+ * Picking the family correctly matters: a `zai`-only account has no bigmodel
17
+ * token, and falling back across families would send a token the backend
18
+ * rejects. The family is read from the provider id (`account:zai-…` vs
19
+ * `account:bigmodel-…`), which is the same spelling the registry uses.
20
+ *
21
+ * Known limit: a TEAM plan's requests need `Bigmodel-Organization` /
22
+ * `Bigmodel-Project` headers, and the ids are not in the credential store a
23
+ * headless process can read. Team-plan resets therefore answer a backend error
24
+ * rather than working; personal plans (the common case) are unaffected.
25
+ *
26
+ * This is the ONLY irreversible operation in the settings API. A spent card is
27
+ * gone. `use()` therefore takes an idempotency key so a retry (a dropped
28
+ * response, a double tap) cannot burn two cards, and the route layer requires a
29
+ * fresh status nonce before it will call it.
30
+ */
31
+ export type ResetFamily = "zai" | "bigmodel";
32
+ export type ResetType = "FIVE_HOUR" | "WEEK";
33
+ export interface ResetCardStatus {
34
+ /** Cards still available, with their expiry (epoch ms). */
35
+ availableFiveHour: Array<{
36
+ expireAt: number;
37
+ }>;
38
+ availableWeek: Array<{
39
+ expireAt: number;
40
+ }>;
41
+ latestFiveHour: {
42
+ usedAt: number;
43
+ } | null;
44
+ latestWeek: {
45
+ usedAt: number;
46
+ } | null;
47
+ hasUnreadHistory: boolean;
48
+ /**
49
+ * Opaque token the client must send back with `use()`. Ties a spend to a
50
+ * status read made moments earlier, so a stale screen cannot consume a card
51
+ * the user has already seen change.
52
+ */
53
+ nonce: string;
54
+ }
55
+ export interface ResetAuthorization {
56
+ zcodeAuthorization: string;
57
+ codingPlanAuthorization: string;
58
+ /** Present for team plans; absent for personal ones. */
59
+ teamContext?: {
60
+ organizationId: string;
61
+ projectId: string;
62
+ };
63
+ }
64
+ /** Which family a provider id belongs to. */
65
+ export declare function resetFamilyFor(providerId: string): ResetFamily;
66
+ /** True when the provider id can own a coding plan. */
67
+ export declare function isCodingPlanProvider(providerId: string): boolean;
68
+ /**
69
+ * Resolve both tokens for a reset request.
70
+ *
71
+ * @throws `coding_plan_provider_required` for a non-account provider,
72
+ * `credentials_unavailable` when the store cannot be decrypted, and
73
+ * `coding_plan_<x>_jwt_required` when a token is missing.
74
+ */
75
+ export declare function resolveResetAuthorization(providerId: string, env?: NodeJS.ProcessEnv): Promise<ResetAuthorization>;
76
+ /** Fetch the current card inventory. */
77
+ export declare function readResetStatus(providerId: string, fetchImpl?: typeof globalThis.fetch, env?: NodeJS.ProcessEnv): Promise<ResetCardStatus>;
78
+ export interface ResetUseResult {
79
+ used: boolean;
80
+ /** Set when the backend declined the opportunity (code 3301). */
81
+ nextTryAt?: number;
82
+ }
83
+ /**
84
+ * Spend one card.
85
+ *
86
+ * The idempotency key is what makes a retry safe: the same key always answers
87
+ * the same outcome instead of consuming a second card. The caller supplies it
88
+ * (the route layer derives one per request), so a client that retries can
89
+ * reuse the value it already sent.
90
+ */
91
+ export declare function useResetCard(providerId: string, resetType: ResetType, idempotencyKey: string, fetchImpl?: typeof globalThis.fetch, env?: NodeJS.ProcessEnv): Promise<ResetUseResult>;
92
+ /**
93
+ * Ask whether a reset would be granted right now.
94
+ *
95
+ * A denial (code 3301) is a normal answer carrying `next_try_at`, not a
96
+ * failure — the UI shows a countdown rather than an error.
97
+ */
98
+ export declare function requestResetOpportunity(providerId: string, idempotencyKey: string, fetchImpl?: typeof globalThis.fetch, env?: NodeJS.ProcessEnv): Promise<{
99
+ granted: boolean;
100
+ nextTryAt: number | null;
101
+ }>;
102
+ /** Mark the reset history read (clears the unread badge). */
103
+ export declare function markResetHistoryRead(providerId: string, fetchImpl?: typeof globalThis.fetch, env?: NodeJS.ProcessEnv): Promise<void>;
104
+ /** Exposed for the eligibility check the route layer runs before any spend. */
105
+ export declare function codingPlanProviderIds(): string[];
106
+ //# sourceMappingURL=coding-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coding-plan.d.ts","sourceRoot":"","sources":["../../src/settings/coding-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA4BH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,UAAU,CAAC;AAC7C,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,iBAAiB,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,aAAa,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3C,cAAc,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1C,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACtC,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uBAAuB,EAAE,MAAM,CAAC;IAChC,wDAAwD;IACxD,WAAW,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AA0ED,6CAA6C;AAC7C,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAE9D;AAED,uDAAuD;AACvD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAGhE;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,kBAAkB,CAAC,CAc7B;AAmED,wCAAwC;AACxC,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,EACrD,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,eAAe,CAAC,CAqC1B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,MAAM,EACtB,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,EACrD,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,cAAc,CAAC,CAiBzB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EACtB,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,EACrD,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAiBzD;AAED,6DAA6D;AAC7D,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,OAAO,UAAU,CAAC,KAAwB,EACrD,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAShD"}