@popoverinstall/cli 0.8.0 → 0.9.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 (60) hide show
  1. package/CHANGELOG.md +196 -69
  2. package/LICENSE +21 -21
  3. package/README.md +142 -141
  4. package/dist/config-command.d.ts +2 -0
  5. package/dist/config-command.d.ts.map +1 -0
  6. package/dist/config-command.js +80 -0
  7. package/dist/config-command.js.map +1 -0
  8. package/dist/cursor-hooks.d.ts +18 -0
  9. package/dist/cursor-hooks.d.ts.map +1 -0
  10. package/dist/cursor-hooks.js +105 -0
  11. package/dist/cursor-hooks.js.map +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +46 -25
  14. package/dist/index.js.map +1 -1
  15. package/dist/keys.d.ts +10 -0
  16. package/dist/keys.d.ts.map +1 -0
  17. package/dist/keys.js +44 -0
  18. package/dist/keys.js.map +1 -0
  19. package/dist/next-steps.d.ts +1 -5
  20. package/dist/next-steps.d.ts.map +1 -1
  21. package/dist/next-steps.js +9 -5
  22. package/dist/next-steps.js.map +1 -1
  23. package/dist/repo-scan.d.ts +130 -0
  24. package/dist/repo-scan.d.ts.map +1 -0
  25. package/dist/repo-scan.js +281 -0
  26. package/dist/repo-scan.js.map +1 -0
  27. package/dist/repos.d.ts +180 -0
  28. package/dist/repos.d.ts.map +1 -0
  29. package/dist/repos.js +1002 -0
  30. package/dist/repos.js.map +1 -0
  31. package/dist/snapshot.d.ts +35 -0
  32. package/dist/snapshot.d.ts.map +1 -1
  33. package/dist/snapshot.js +16 -16
  34. package/dist/snapshot.js.map +1 -1
  35. package/dist/terminal.d.ts.map +1 -1
  36. package/dist/terminal.js +21 -0
  37. package/dist/terminal.js.map +1 -1
  38. package/dist/vaults.d.ts +276 -0
  39. package/dist/vaults.d.ts.map +1 -0
  40. package/dist/vaults.js +1224 -0
  41. package/dist/vaults.js.map +1 -0
  42. package/package.json +47 -47
  43. package/plugin/.claude-plugin/plugin.json +19 -19
  44. package/plugin/.mcp.json +9 -9
  45. package/plugin/README.md +84 -76
  46. package/plugin/commands/ask.md +65 -65
  47. package/plugin/commands/fork.md +119 -118
  48. package/plugin/commands/repos.md +107 -0
  49. package/plugin/commands/team.md +60 -60
  50. package/plugin/commands/tell.md +66 -66
  51. package/plugin/commands/vault.md +173 -0
  52. package/plugin/hooks/hooks.json +111 -111
  53. package/plugin/mcp/index.mjs +585 -355
  54. package/plugin/scripts/_ipc.mjs +146 -146
  55. package/plugin/scripts/announce-roster.mjs +141 -131
  56. package/plugin/scripts/deliver-messages.mjs +77 -77
  57. package/plugin/scripts/emit-event.mjs +44 -44
  58. package/plugin/scripts/ensure-daemon.mjs +156 -156
  59. package/plugin/scripts/roster.mjs +52 -52
  60. package/plugin/skills/popover/SKILL.md +175 -160
@@ -0,0 +1,276 @@
1
+ import { type SnapshotEnvelope, type TranscriptEntry, type VaultSummary } from "@popoverinstall/shared";
2
+ /**
3
+ * `popover vault` — a conversation frozen for the team, and asked later.
4
+ *
5
+ * The design is docs/vaults.md; this file is the terminal half of it. What matters when
6
+ * reading it is which of the three "copy of a conversation" things it is:
7
+ *
8
+ * - `popover fork` hands one conversation to one named person, keyed by a code the server
9
+ * never sees, and they carry it on as their own live session. It expires in 24 hours.
10
+ * - a vault is published to a *team*, never expires, and is never carried on. It is only
11
+ * ever asked, and the answer comes back attributed rather than as fact.
12
+ *
13
+ * Three consequences run through everything below.
14
+ *
15
+ * **The agent authors the description.** `--title` and `--answers` are written by the model,
16
+ * not prompted for, because the one moment anybody knows what a conversation is authoritative
17
+ * about is while they are still inside it (docs/vaults.md §3). So `create` validates the
18
+ * description properly and refuses a bad one with a sentence a model can act on, rather than
19
+ * relaying a constraint violation from Postgres.
20
+ *
21
+ * **Looking is free and asking is not.** `list` is stage one — a string comparison, no model
22
+ * call — and `ask` is stage two, which resumes a whole conversation and bills for it at
23
+ * roughly the cold-ask numbers in `fork.ts` ($0.46, 97s). They are separate verbs and
24
+ * separate IPC arms precisely so that an agent that cannot do the first without the second
25
+ * does not end up doing the second by default (§6).
26
+ *
27
+ * **A vault is readable by the service, and `popover fork` is not.** Anyone who has
28
+ * internalized the fork's promise will assume this one makes it too. §1 requires the publish
29
+ * confirmation to say otherwise out loud, so `create` prints it every time and reports
30
+ * `readable_by_service` in its JSON.
31
+ *
32
+ * There is deliberately no edit verb. A vault is archived, never rewritten; a correction is a
33
+ * `note`, which sits beside the original rather than replacing it (§2, §5). What a vault
34
+ * originally claimed is evidence about what the team believed at the time.
35
+ */
36
+ export declare function vaultCommand(rest: string[]): Promise<number>;
37
+ /** Everything that is not a flag and not a flag's value, in the order it was typed. */
38
+ export declare function positionals(args: string[]): string[];
39
+ /** Every occurrence of a repeatable flag. `--answers` is the only one, and it is the point. */
40
+ export declare function flagAll(args: string[], name: string): string[];
41
+ export type AnswersResult = {
42
+ ok: true;
43
+ answers: string[];
44
+ } | {
45
+ ok: false;
46
+ message: string;
47
+ hints: string[];
48
+ };
49
+ /**
50
+ * Read `--answers`, which an agent may pass either repeatably or as one JSON array.
51
+ *
52
+ * Both forms exist because both are natural to generate: a shell-shaped caller repeats the
53
+ * flag, and a model writing a single argument writes JSON. What must not happen is the third
54
+ * outcome — a JSON array that fails to parse being stored verbatim as one long question, which
55
+ * would upload a vault whose entire retrieval surface is the string `["why did we...`. So
56
+ * anything that opens with `[` is committed to being JSON, and is refused rather than
57
+ * salvaged if it is not.
58
+ */
59
+ export declare function parseAnswers(raw: string[]): AnswersResult;
60
+ export interface VaultPlanInput {
61
+ name?: string | undefined;
62
+ title?: string | undefined;
63
+ answers: string[];
64
+ }
65
+ export type VaultPlan = {
66
+ ok: true;
67
+ name: string;
68
+ title: string;
69
+ answers: string[];
70
+ } | {
71
+ ok: false;
72
+ code: string;
73
+ message: string;
74
+ hints: string[];
75
+ };
76
+ /**
77
+ * Decide what this vault is called and whether it is describable at all, before anything is
78
+ * read off the network.
79
+ *
80
+ * The name is checked here rather than left to the server for two reasons. The unique index
81
+ * is on `lower(name)`, so `normalizeVaultName` can produce the same collision locally and say
82
+ * something better than a constraint violation; and a name that normalizes to nothing —
83
+ * a title of "???" — would otherwise reach the route as an empty string and come back as a
84
+ * 400 that reads like a bug in popover rather than a title worth rewriting.
85
+ *
86
+ * `answers` is the one that matters most and is the one an agent is most likely to skip.
87
+ * It holds *questions this conversation can answer*, not a summary, because the incoming
88
+ * query at retrieval time is a question and matching question against summary retrieves
89
+ * badly (§6). Refusing an empty list is the only moment anybody is in a position to enforce
90
+ * that.
91
+ */
92
+ export declare function planVault(input: VaultPlanInput): VaultPlan;
93
+ export type VaultMatch = {
94
+ kind: "one";
95
+ vault: VaultSummary;
96
+ } | {
97
+ kind: "none";
98
+ } | {
99
+ kind: "many";
100
+ matches: VaultSummary[];
101
+ };
102
+ /**
103
+ * Which vault a typed reference means: by id, then exact name, then unique prefix.
104
+ *
105
+ * Every match is returned rather than the first, and the same widening `resolveTeam` does is
106
+ * used, for the same reason: two names sharing a prefix is a real arrangement, and asking the
107
+ * wrong vault costs a dollar and produces a confident answer about the wrong subsystem, which
108
+ * is the failure nobody notices.
109
+ */
110
+ export declare function resolveVaultRef(vaults: VaultSummary[], typed: string): VaultMatch;
111
+ /**
112
+ * Whether a reference is already an id, so no lookup is needed to use it.
113
+ *
114
+ * The full UUID shape rather than "has hyphens", because vault names have hyphens in them by
115
+ * construction — `repo-key-gating` is the example the spec itself uses. Treating one as an id
116
+ * would POST a note to `/api/vaults/repo-key-gating/notes` and report the 404 as a missing
117
+ * vault, when the vault is right there under a name we simply never resolved.
118
+ */
119
+ export declare function looksLikeVaultId(ref: string): boolean;
120
+ /**
121
+ * The freeze date, in the form §4 writes it: `15 Aug 2026`.
122
+ *
123
+ * The locale is pinned rather than left to the machine because this string is part of an
124
+ * attribution that gets pasted into other conversations and read by other models, so
125
+ * `8/15/2026` on one laptop and `15/08/2026` on the next is an ambiguity with no upside.
126
+ */
127
+ export declare function freezeDate(iso: string): string;
128
+ /**
129
+ * The sentence a vault's answer is always wrapped in.
130
+ *
131
+ * Never optional, and never assembled by the caller. A human reading an answer supplies the
132
+ * skepticism unprompted; an agent will not unless the format forces it, and a vault can hold
133
+ * a hypothesis the team later disproved. So the title and the freeze date travel on
134
+ * `VaultAnswer` itself and this turns them into the one line that must precede the answer —
135
+ * including in `--json`, where the assembled string is a field rather than a suggestion.
136
+ */
137
+ export declare function attribution(answer: {
138
+ title: string;
139
+ frozenAt: string;
140
+ gitSha?: string | undefined;
141
+ }): string;
142
+ /**
143
+ * How a staleness score reads to a person.
144
+ *
145
+ * Words rather than the number, because the number is a ratio of paths and nobody ranks
146
+ * vaults by reading ratios. A vault that recorded no paths at all scores 0 the same way an
147
+ * untouched one does — `stalenessScore` deliberately does not punish absent evidence — so
148
+ * "nothing it touched has changed" is only claimed when the server sent a score at all.
149
+ */
150
+ export declare function stalenessNote(staleness: number | undefined): string;
151
+ /** One vault as a person reads it in a list: a heading, a byline, and its questions. */
152
+ export declare function summaryLines(v: VaultSummary): string[];
153
+ /**
154
+ * One vault as a script reads it.
155
+ *
156
+ * Absent values are `null` rather than missing, because the consumer is usually a model
157
+ * deciding whether to spend a dollar on stage two, and a key that is sometimes there reads as
158
+ * a key it forgot to look at.
159
+ */
160
+ export declare function summaryJson(v: VaultSummary): Record<string, unknown>;
161
+ /**
162
+ * Read a listing from the HTTP route, accepting either spelling of its fields.
163
+ *
164
+ * The IPC path parses `VaultSummarySchema` and gets camelCase by construction. The HTTP route
165
+ * is written in another package against §10, which names those same columns `author_label`,
166
+ * `created_at` and `note_count` — so both spellings are plausible and neither is worth a
167
+ * cross-package coordination round to settle. Accepting both here costs a few lines and means
168
+ * `note` and `archive` cannot be broken by a naming choice made in `apps/web`.
169
+ *
170
+ * Returns null for a body that is not a listing at all, which is what an SSO proxy or a
171
+ * protected preview answers with — see `ApiFailure.malformed`.
172
+ */
173
+ export declare function readSummaries(body: unknown): VaultSummary[] | null;
174
+ /**
175
+ * Keep `--limit` inside what the daemon's schema will accept.
176
+ *
177
+ * Clamped rather than relayed, because `VaultSearchRequestSchema` caps it at 50 and floors it
178
+ * at 1, and a rejected request comes back as a generic `bad_request` from the daemon that
179
+ * says nothing about the number somebody typed.
180
+ */
181
+ export declare function clampLimit(raw: string | undefined, fallback?: number): number;
182
+ /** Same clamp, for `--timeout`, whose schema window is 5 to 600 seconds. */
183
+ export declare function clampTimeout(raw: string | undefined, fallback?: number): number;
184
+ /**
185
+ * The files this conversation read or wrote, from the transcript itself.
186
+ *
187
+ * This is the primary source for `touched_paths`, and the working tree is the secondary one,
188
+ * which is the opposite of how it first looked. `touched_paths` exists to answer "does this
189
+ * conversation's reasoning depend on files that have since changed", and `git status` answers
190
+ * a narrower question: what was uncommitted at the moment somebody vaulted. Two ordinary
191
+ * cases fall through that gap — a conversation that committed its work before vaulting, and a
192
+ * read-only conversation that changed nothing — and both land on an empty list.
193
+ *
194
+ * Empty is not neutral. `stalenessScore` deliberately returns 0 for a vault that recorded no
195
+ * paths, on the correct reasoning that absence of evidence is not churn; so a vault with an
196
+ * empty list ranks as permanently current and never decays. The two behaviours are each right
197
+ * and together wrong, and the read-heavy vaults they hit hardest are the valuable ones.
198
+ *
199
+ * Paths that do not fall under any of `roots` are dropped rather than stored absolute. They
200
+ * could never match a path from `git status`, so keeping them would only pad the denominator
201
+ * of `hits / touchedPaths.length` and make every vault look more current than it is.
202
+ */
203
+ export declare function collectToolPaths(entries: TranscriptEntry[], roots: readonly string[], cap?: number): string[];
204
+ /**
205
+ * The two sources, deduped and capped once.
206
+ *
207
+ * The transcript comes first so that it survives truncation: if a conversation named more
208
+ * than the cap on its own, what it read and wrote is a better description of what it depended
209
+ * on than whatever happened to be dirty in the tree at the time.
210
+ */
211
+ export declare function unionPaths(primary: string[], secondary: string[], cap?: number): string[];
212
+ /**
213
+ * The uncommitted half, from `git status --porcelain`.
214
+ *
215
+ * Unioned with `collectToolPaths` rather than replaced by it, because work that a conversation
216
+ * left dirty is genuine evidence about what it touched — it is only insufficient on its own.
217
+ *
218
+ * Renames are recorded at their destination: `R old -> new` is churn in `new`, and ranking a
219
+ * vault against a path that no longer exists would make every renamed file look permanently
220
+ * untouched. Quoted paths are unescaped because `core.quotePath` is on by default and a
221
+ * filename with an accent in it would otherwise be stored complete with its surrounding
222
+ * quotes and never match anything.
223
+ *
224
+ * The cap is `VaultIndexSchema`'s 500. A conversation that dirtied more than that is a
225
+ * generated-file situation where the first 500 are as good a signal as any.
226
+ */
227
+ export declare function parseTouchedPaths(porcelain: string, cap?: number): string[];
228
+ /**
229
+ * A commit sha, or nothing.
230
+ *
231
+ * `git rev-parse HEAD` in a repository with no commits prints the literal string `HEAD` and
232
+ * exits nonzero on some versions and zero on others. `VaultIndexSchema.gitSha` is a hex regex,
233
+ * so letting that through turns a first-commit-pending repo into a 400 from the create route
234
+ * that reads as a popover bug rather than as "there is no commit yet".
235
+ */
236
+ export declare function cleanSha(raw: string | undefined): string | undefined;
237
+ /**
238
+ * The transcript, compressed, as one opaque string.
239
+ *
240
+ * Deliberately **not** `sealSnapshot`. That derives its AES key from the fork code, and a
241
+ * vault has no code — team membership is the whole credential, which is exactly the decision
242
+ * §1 records and the reason the server can read a vault at all. Encrypting here with a key
243
+ * stored beside the ciphertext would be worse than not encrypting: a promise that reads as
244
+ * strong and is not. The real protection is at-rest encryption under a key held outside the
245
+ * database, which is the server's to do.
246
+ *
247
+ * gzip + base64 anyway, because `body` is one opaque column by design (§2) so a later
248
+ * end-to-end version can wrap a per-vault key around this without moving anything else, and
249
+ * because a transcript compresses about 10:1.
250
+ */
251
+ export declare function packBody(envelope: SnapshotEnvelope): string;
252
+ export interface ApiFailure {
253
+ ok: false;
254
+ status: number;
255
+ /** The `error` code from any of our own error bodies. */
256
+ error?: string;
257
+ /** A sentence the server wrote, when it sent one. Relayed rather than rewritten. */
258
+ message?: string;
259
+ /**
260
+ * Something answered with a success status and a body that is not what this route returns.
261
+ * Its own flag rather than a status, because the status is 200 and "failed (200)" is not a
262
+ * sentence anybody can act on. In practice it is an auth wall that redirects rather than
263
+ * refusing, and `fetch` follows the redirect.
264
+ */
265
+ malformed?: true;
266
+ /**
267
+ * Whether our API refused this, or something in front of it did.
268
+ *
269
+ * A 401 from the route means the device token is no longer good and the fix is
270
+ * `popover login`; a 401 from an SSO proxy or a protected preview means nothing of the sort,
271
+ * and sending somebody to re-authenticate over it wastes their time on the wrong problem.
272
+ * Our routes always answer with a JSON `error` string; a wall answers with something else.
273
+ */
274
+ ours: boolean;
275
+ }
276
+ //# sourceMappingURL=vaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vaults.d.ts","sourceRoot":"","sources":["../src/vaults.ts"],"names":[],"mappings":"AAGA,OAAO,EAaL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EAEpB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAKhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAMH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBlE;AAyCD,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAYpD;AAOD,+FAA+F;AAC/F,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAM9D;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEpD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,aAAa,CA8BzD;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAElE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CA8D1D;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC;AAE9C;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAiBjF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,GAAG,MAAM,CAGT;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAKnE;AAED,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,EAAE,CAUtD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,EAAE,GAAG,IAAI,CAqClE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,SAAK,GAAG,MAAM,CAIzE;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,SAAK,GAAG,MAAM,CAI3E;AAYD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,EAAE,EAC1B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,GAAG,SAAM,GACR,MAAM,EAAE,CA2BV;AA4CD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,SAAM,GAAG,MAAM,EAAE,CAMtF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,EAAE,CAWxE;AAuCD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGpE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAE3D;AAokBD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB;;;;;;;OAOG;IACH,IAAI,EAAE,OAAO,CAAC;CACf"}