@roam-research/roam-tools-core 0.5.1 → 0.6.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.
Files changed (40) hide show
  1. package/README.md +23 -8
  2. package/dist/index.d.ts +3 -7
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +8 -8
  5. package/dist/operations/blocks.d.ts +21 -22
  6. package/dist/operations/blocks.d.ts.map +1 -1
  7. package/dist/operations/datalog.d.ts +2 -3
  8. package/dist/operations/datalog.d.ts.map +1 -1
  9. package/dist/operations/files.d.ts +6 -7
  10. package/dist/operations/files.d.ts.map +1 -1
  11. package/dist/operations/navigation.d.ts +9 -10
  12. package/dist/operations/navigation.d.ts.map +1 -1
  13. package/dist/operations/pages.d.ts +10 -11
  14. package/dist/operations/pages.d.ts.map +1 -1
  15. package/dist/operations/query.d.ts +10 -11
  16. package/dist/operations/query.d.ts.map +1 -1
  17. package/dist/operations/search.d.ts +7 -8
  18. package/dist/operations/search.d.ts.map +1 -1
  19. package/dist/tools.d.ts +37 -4
  20. package/dist/tools.d.ts.map +1 -1
  21. package/dist/tools.js +47 -52
  22. package/dist/types.d.ts +17 -10
  23. package/dist/types.d.ts.map +1 -1
  24. package/dist/types.js +9 -0
  25. package/package.json +7 -10
  26. package/dist/client.d.ts +0 -34
  27. package/dist/client.d.ts.map +0 -1
  28. package/dist/client.js +0 -275
  29. package/dist/connect.d.ts +0 -10
  30. package/dist/connect.d.ts.map +0 -1
  31. package/dist/connect.js +0 -475
  32. package/dist/graph-resolver.d.ts +0 -54
  33. package/dist/graph-resolver.d.ts.map +0 -1
  34. package/dist/graph-resolver.js +0 -339
  35. package/dist/operations/graphs.d.ts +0 -26
  36. package/dist/operations/graphs.d.ts.map +0 -1
  37. package/dist/operations/graphs.js +0 -213
  38. package/dist/roam-api.d.ts +0 -32
  39. package/dist/roam-api.d.ts.map +0 -1
  40. package/dist/roam-api.js +0 -50
package/dist/connect.js DELETED
@@ -1,475 +0,0 @@
1
- import { search, select, input } from "@inquirer/prompts";
2
- import { getPort, getConfiguredGraphsSafe, saveGraphToConfig, removeGraphFromConfig, } from "./graph-resolver.js";
3
- import { fetchAvailableGraphs, requestToken, sleep, openRoamApp, slugify } from "./roam-api.js";
4
- // ============================================================================
5
- // API Functions
6
- // ============================================================================
7
- async function fetchOpenGraphs(port) {
8
- const url = `http://127.0.0.1:${port}/api/graphs/open`;
9
- const response = await fetch(url, {
10
- method: "GET",
11
- headers: { "Content-Type": "application/json" },
12
- });
13
- const data = (await response.json());
14
- if (!data.success) {
15
- // Open graphs failing is not critical, return empty
16
- return [];
17
- }
18
- return data.result || [];
19
- }
20
- // ============================================================================
21
- // Helpers
22
- // ============================================================================
23
- function getErrorMessage(error) {
24
- if (!error)
25
- return "Unknown error";
26
- if (typeof error === "string")
27
- return error;
28
- return error.message || "Unknown error";
29
- }
30
- function getErrorCode(error) {
31
- if (!error || typeof error === "string")
32
- return undefined;
33
- return error.code;
34
- }
35
- // ============================================================================
36
- // Main Connect Function
37
- // ============================================================================
38
- export async function connect(options = {}) {
39
- const VALID_ACCESS_LEVELS = ["full", "read-append", "read-only"];
40
- const VALID_TYPES = ["hosted", "offline"];
41
- // ── Handle --remove mode ──────────────────────────────────────────────
42
- if (options.remove) {
43
- if (!options.graph && !options.nickname) {
44
- console.error("Error: --remove requires --graph <name> or --nickname <slug>.");
45
- process.exit(1);
46
- }
47
- const configuredGraphs = await getConfiguredGraphsSafe();
48
- let target;
49
- if (options.nickname) {
50
- const slug = slugify(options.nickname);
51
- target = configuredGraphs.find((g) => g.nickname.toLowerCase() === slug.toLowerCase());
52
- }
53
- else if (options.graph) {
54
- target = configuredGraphs.find((g) => g.name === options.graph);
55
- }
56
- if (!target) {
57
- console.error(`Error: No configured graph found matching ${options.nickname ? `nickname "${slugify(options.nickname)}"` : `name "${options.graph}"`}.`);
58
- if (configuredGraphs.length > 0) {
59
- console.error("\nConfigured graphs:");
60
- for (const g of configuredGraphs) {
61
- console.error(` - ${g.nickname} (${g.name})`);
62
- }
63
- }
64
- process.exit(1);
65
- }
66
- await removeGraphFromConfig(target.nickname);
67
- console.log(`Removed "${target.nickname}" (${target.name}) from config.`);
68
- return;
69
- }
70
- // ── Non-interactive mode detection ────────────────────────────────────
71
- const nonInteractive = !!options.graph;
72
- // ── Validate flags (non-interactive) ──────────────────────────────────
73
- if (nonInteractive) {
74
- if (options.accessLevel && !VALID_ACCESS_LEVELS.includes(options.accessLevel)) {
75
- console.error(`Error: Invalid access level "${options.accessLevel}". Valid options: ${VALID_ACCESS_LEVELS.join(", ")}`);
76
- process.exit(1);
77
- }
78
- if (options.type && !VALID_TYPES.includes(options.type)) {
79
- console.error(`Error: Invalid type "${options.type}". Valid options: ${VALID_TYPES.join(", ")}`);
80
- process.exit(1);
81
- }
82
- if (options.public && options.type && options.type !== "hosted") {
83
- console.error(`Error: Public graphs are always hosted. Remove --type or set it to "hosted".`);
84
- process.exit(1);
85
- }
86
- if (options.public && options.accessLevel && options.accessLevel !== "read-only") {
87
- console.error(`Error: Public graphs only support read-only access. Remove --access-level or set it to "read-only".`);
88
- process.exit(1);
89
- }
90
- if (!options.nickname) {
91
- console.error("Error: --nickname is required when using --graph.");
92
- console.error('Provide a short name you\'ll use to refer to this graph, e.g. --nickname "my work graph"');
93
- process.exit(1);
94
- }
95
- if (!slugify(options.nickname)) {
96
- console.error("Error: Nickname cannot be empty.");
97
- process.exit(1);
98
- }
99
- }
100
- let port;
101
- // 1. Get port and try to connect
102
- try {
103
- port = await getPort();
104
- }
105
- catch {
106
- port = 3333; // Default port
107
- }
108
- // 2. Fetch available graphs (with retry if Roam not running)
109
- let availableGraphs;
110
- try {
111
- availableGraphs = await fetchAvailableGraphs(port);
112
- }
113
- catch (error) {
114
- const err = error;
115
- if (err.cause?.code === "ECONNREFUSED" || err.message.includes("fetch failed")) {
116
- console.log("Roam Desktop is not running. Opening...");
117
- await openRoamApp();
118
- console.log("Waiting for Roam to start...");
119
- await sleep(5000);
120
- // Retry
121
- try {
122
- availableGraphs = await fetchAvailableGraphs(port);
123
- }
124
- catch {
125
- console.error("\nCould not connect to Roam Desktop.");
126
- console.error("Please make sure Roam is running and try again.");
127
- process.exit(1);
128
- }
129
- }
130
- else {
131
- throw error;
132
- }
133
- }
134
- if (availableGraphs.length === 0 && !options.public) {
135
- console.error("No graphs available. Please log in to Roam and try again.");
136
- process.exit(1);
137
- }
138
- // 3. Fetch open graphs (for highlighting)
139
- const openGraphs = await fetchOpenGraphs(port);
140
- // 4. Get currently configured graphs
141
- const configuredGraphs = await getConfiguredGraphsSafe();
142
- // 5a. Validate nickname collision early (non-interactive only)
143
- if (nonInteractive) {
144
- const nicknameSlug = slugify(options.nickname);
145
- const graphType = options.type;
146
- const existing = configuredGraphs.find((g) => g.nickname === nicknameSlug &&
147
- !(g.name === options.graph && (!graphType || g.type === graphType)));
148
- if (existing) {
149
- console.error(`Error: Nickname "${nicknameSlug}" is already used by graph "${existing.name}".`);
150
- console.error("Please choose a different nickname with --nickname.");
151
- process.exit(1);
152
- }
153
- }
154
- // 5b. Resolve selected graph
155
- let finalSelectedGraph;
156
- if (nonInteractive) {
157
- // ── Non-interactive graph resolution ─────────────────────────────────
158
- if (options.public) {
159
- // Public graph: skip available graphs lookup, construct directly
160
- const graphType = options.type || "hosted";
161
- const configured = configuredGraphs.find((c) => c.name === options.graph && c.type === graphType);
162
- finalSelectedGraph = {
163
- name: options.graph,
164
- type: graphType,
165
- isOpen: false,
166
- isConnected: !!configured,
167
- existingNickname: configured?.nickname,
168
- isPublicGraph: true,
169
- };
170
- }
171
- else {
172
- // Match against available graphs
173
- const graphType = options.type;
174
- const match = availableGraphs.find((g) => g.name === options.graph && (!graphType || g.type === graphType));
175
- if (!match) {
176
- console.error(`Error: Graph "${options.graph}" not found in available graphs.`);
177
- if (availableGraphs.length > 0) {
178
- console.error("\nAvailable graphs:");
179
- for (const g of availableGraphs) {
180
- console.error(` - ${g.name} (${g.type})`);
181
- }
182
- }
183
- console.error("\nIf this is a public graph, use --public.");
184
- process.exit(1);
185
- }
186
- const configured = configuredGraphs.find((c) => c.name === match.name && c.type === match.type);
187
- finalSelectedGraph = {
188
- ...match,
189
- isOpen: openGraphs.some((o) => o.name === match.name && o.type === match.type),
190
- isConnected: !!configured,
191
- existingNickname: configured?.nickname,
192
- lastKnownTokenStatus: configured?.lastKnownTokenStatus,
193
- };
194
- }
195
- }
196
- else {
197
- // ── Interactive graph selection ──────────────────────────────────────
198
- const choices = availableGraphs.map((g) => {
199
- const isOpen = openGraphs.some((o) => o.name === g.name && o.type === g.type);
200
- const configured = configuredGraphs.find((c) => c.name === g.name && c.type === g.type);
201
- return {
202
- ...g,
203
- isOpen,
204
- isConnected: !!configured,
205
- existingNickname: configured?.nickname,
206
- lastKnownTokenStatus: configured?.lastKnownTokenStatus,
207
- };
208
- });
209
- // Add configured graphs that aren't in available list (e.g., public graphs)
210
- for (const configured of configuredGraphs) {
211
- const alreadyInList = choices.some((c) => c.name === configured.name && c.type === configured.type);
212
- if (!alreadyInList) {
213
- choices.push({
214
- name: configured.name,
215
- type: configured.type,
216
- isOpen: false,
217
- isConnected: true,
218
- existingNickname: configured.nickname,
219
- isPublicGraph: true,
220
- lastKnownTokenStatus: configured.lastKnownTokenStatus,
221
- });
222
- }
223
- }
224
- // Sort: open first, then by name
225
- choices.sort((a, b) => {
226
- if (a.isOpen !== b.isOpen)
227
- return a.isOpen ? -1 : 1;
228
- return a.name.localeCompare(b.name);
229
- });
230
- // Add "Enter custom graph name" option at the end
231
- const customOption = {
232
- name: "__custom__",
233
- type: "hosted",
234
- isOpen: false,
235
- isConnected: false,
236
- isCustomOption: true,
237
- };
238
- choices.push(customOption);
239
- // Interactive graph selection with search
240
- const selectedGraph = await search({
241
- message: "Select a graph to connect (or type to search):",
242
- source: async (input) => {
243
- // Filter available graphs (exclude custom option placeholder)
244
- const filtered = input
245
- ? choices.filter((g) => !g.isCustomOption && g.name.toLowerCase().includes(input.toLowerCase()))
246
- : choices.filter((g) => !g.isCustomOption);
247
- // Build results with custom option
248
- const results = filtered.map((g) => {
249
- let label = `${g.name} (${g.type})`;
250
- if (g.isOpen)
251
- label += " [open]";
252
- if (g.isPublicGraph && g.isConnected) {
253
- label += ` [public, connected as "${g.existingNickname}"${g.lastKnownTokenStatus === "revoked" ? ", token revoked" : ""}]`;
254
- }
255
- else if (g.isConnected) {
256
- label += ` [connected as "${g.existingNickname}"${g.lastKnownTokenStatus === "revoked" ? ", token revoked" : ""}]`;
257
- }
258
- return {
259
- name: label,
260
- value: g,
261
- };
262
- });
263
- // Always show custom option at the end, with search term as hint
264
- const customLabel = input
265
- ? `── Connect to public graph "${input}"`
266
- : "── Connect to a public graph...";
267
- results.push({
268
- name: customLabel,
269
- value: { ...customOption, name: input || "__custom__" },
270
- });
271
- return results;
272
- },
273
- });
274
- // Handle custom graph name option
275
- if (selectedGraph.isCustomOption) {
276
- // Use the search term as default if provided
277
- const defaultName = selectedGraph.name !== "__custom__" ? selectedGraph.name : "";
278
- const customName = await input({
279
- message: "Enter the graph name:",
280
- default: defaultName,
281
- validate: (value) => (value.trim() ? true : "Graph name cannot be empty"),
282
- });
283
- // Public graphs are always hosted
284
- const customType = "hosted";
285
- // Check if already configured
286
- const configured = configuredGraphs.find((c) => c.name === customName.trim() && c.type === customType);
287
- finalSelectedGraph = {
288
- name: customName.trim(),
289
- type: customType,
290
- isOpen: false,
291
- isConnected: !!configured,
292
- existingNickname: configured?.nickname,
293
- isPublicGraph: true,
294
- };
295
- }
296
- else {
297
- finalSelectedGraph = selectedGraph;
298
- }
299
- }
300
- // 6. Handle already connected graph
301
- if (finalSelectedGraph.isConnected) {
302
- if (nonInteractive) {
303
- // Non-interactive: error with hint to use --remove
304
- console.error(`Error: Graph "${finalSelectedGraph.name}" is already connected as "${finalSelectedGraph.existingNickname}".`);
305
- console.error(`To replace the token, first remove it:\n roam connect --remove --nickname ${finalSelectedGraph.existingNickname}`);
306
- process.exit(1);
307
- }
308
- const existingConfig = configuredGraphs.find((c) => c.name === finalSelectedGraph.name && c.type === finalSelectedGraph.type);
309
- if (existingConfig?.lastKnownTokenStatus === "revoked") {
310
- // Token has been revoked — show revoked-specific menu
311
- const action = await select({
312
- message: `The token for "${finalSelectedGraph.existingNickname}" has been revoked. What would you like to do?`,
313
- choices: [
314
- {
315
- name: "Replace with a new token",
316
- value: "replace",
317
- },
318
- {
319
- name: "Remove from config",
320
- value: "remove",
321
- },
322
- {
323
- name: "Cancel",
324
- value: "cancel",
325
- },
326
- ],
327
- });
328
- if (action === "cancel") {
329
- console.log("Cancelled.");
330
- return;
331
- }
332
- if (action === "remove") {
333
- await removeGraphFromConfig(finalSelectedGraph.existingNickname);
334
- console.log(`Removed "${finalSelectedGraph.existingNickname}" from config.`);
335
- return;
336
- }
337
- // action === "replace" — fall through to token request flow
338
- }
339
- else {
340
- // Normal connected flow
341
- const action = await select({
342
- message: `This graph is already connected as "${finalSelectedGraph.existingNickname}". What would you like to do?`,
343
- choices: [
344
- {
345
- name: "Change token permissions",
346
- value: "change-permissions",
347
- },
348
- {
349
- name: "Remove from config",
350
- value: "remove",
351
- },
352
- {
353
- name: "Cancel",
354
- value: "cancel",
355
- },
356
- ],
357
- });
358
- if (action === "cancel") {
359
- console.log("Cancelled.");
360
- return;
361
- }
362
- if (action === "remove") {
363
- await removeGraphFromConfig(finalSelectedGraph.existingNickname);
364
- console.log(`Removed "${finalSelectedGraph.existingNickname}" from config.`);
365
- return;
366
- }
367
- if (action === "change-permissions") {
368
- console.log("\nTo change this token's permissions:");
369
- console.log(" 1. Open Roam Desktop and open the graph");
370
- console.log(" 2. Go to Settings > Graph > Local API Tokens");
371
- console.log(" 3. Find the token and adjust its permissions");
372
- // Permission changes are synced when get_graph_guidelines calls getTokenInfo()
373
- console.log("\nChanges will be synced automatically next time the MCP is started.");
374
- return;
375
- }
376
- }
377
- }
378
- // 7. Select access level
379
- let accessLevel;
380
- if (finalSelectedGraph.isPublicGraph) {
381
- accessLevel = "read-only";
382
- console.log("\nPublic graphs only support read-only access.");
383
- }
384
- else if (nonInteractive) {
385
- accessLevel = options.accessLevel || "full";
386
- }
387
- else {
388
- accessLevel = await select({
389
- message: "Select permissions:",
390
- choices: [
391
- {
392
- name: "Full (read, create, edit, delete)",
393
- value: "full",
394
- },
395
- {
396
- name: "Read + Append (read, create only)",
397
- value: "read-append",
398
- },
399
- {
400
- name: "Read Only",
401
- value: "read-only",
402
- },
403
- ],
404
- });
405
- }
406
- // 8. Get nickname (before token request so all input is collected upfront)
407
- let nickname;
408
- if (nonInteractive) {
409
- nickname = slugify(options.nickname);
410
- console.log(`→ Using nickname: ${nickname}`);
411
- }
412
- else {
413
- const rawNickname = await input({
414
- message: "Enter a short name you'll use to refer to this graph:",
415
- validate: (value) => {
416
- const slug = slugify(value.trim());
417
- if (!slug)
418
- return "Nickname cannot be empty";
419
- // Check for existing nickname (excluding the current graph if updating)
420
- const existing = configuredGraphs.find((g) => g.nickname === slug &&
421
- !(g.name === finalSelectedGraph.name && g.type === finalSelectedGraph.type));
422
- if (existing) {
423
- return `Nickname "${slug}" is already used by "${existing.name}"`;
424
- }
425
- return true;
426
- },
427
- });
428
- nickname = slugify(rawNickname.trim());
429
- console.log(`→ Using nickname: ${nickname}`);
430
- }
431
- // 9. Request token (blocks until user approves in Roam)
432
- console.log("\nWaiting for approval in Roam Desktop...");
433
- console.log("(A dialog should appear in the Roam app - please approve it)");
434
- const result = await requestToken(port, finalSelectedGraph.name, finalSelectedGraph.type, accessLevel);
435
- if (!result.success) {
436
- const errorCode = getErrorCode(result.error);
437
- const errorMessage = getErrorMessage(result.error);
438
- switch (errorCode) {
439
- case "USER_REJECTED":
440
- console.error("\nToken request was denied in Roam.");
441
- break;
442
- case "GRAPH_BLOCKED":
443
- console.error("\nThis graph has blocked token requests. Unblock it in Roam Settings > Graph > Local API Tokens.");
444
- break;
445
- case "TIMEOUT":
446
- console.error("\nNo response after 5 minutes. Please try again.");
447
- break;
448
- case "REQUEST_IN_PROGRESS":
449
- console.error("\nAnother token request is already pending for this graph.");
450
- break;
451
- default:
452
- console.error(`\nError: ${errorMessage}`);
453
- }
454
- process.exit(1);
455
- }
456
- // 10. Save to config
457
- if (!result.token) {
458
- // should not happen but just in case
459
- console.error("\nError: Server returned success but no token was provided.");
460
- process.exit(1);
461
- }
462
- const graphConfig = {
463
- name: finalSelectedGraph.name,
464
- type: finalSelectedGraph.type,
465
- token: result.token,
466
- nickname,
467
- accessLevel: result.grantedAccessLevel,
468
- };
469
- await saveGraphToConfig(graphConfig);
470
- console.log(`\nConnected! Graph ${finalSelectedGraph.name} (nickname: ${nickname}) has been saved to ~/.roam-tools.json`);
471
- console.log(`\nGranted permissions: ${result.grantedAccessLevel}`);
472
- if (result.grantedAccessLevel !== accessLevel) {
473
- console.log(`(Note: You requested "${accessLevel}" but were granted "${result.grantedAccessLevel}" based on your permissions)`);
474
- }
475
- }
@@ -1,54 +0,0 @@
1
- import { RoamMcpConfig, GraphConfig, ResolvedGraph, AccessLevel } from "./types.js";
2
- export declare function getPort(): Promise<number>;
3
- export declare function getMcpConfig(): Promise<RoamMcpConfig>;
4
- /**
5
- * Save a graph configuration to ~/.roam-tools.json
6
- * If a graph with the same name+type exists, it will be updated.
7
- * Otherwise, the graph will be added.
8
- */
9
- export declare function saveGraphToConfig(newGraph: GraphConfig): Promise<void>;
10
- /**
11
- * Remove a graph from ~/.roam-tools.json by nickname
12
- */
13
- export declare function removeGraphFromConfig(nickname: string): Promise<boolean>;
14
- /**
15
- * Update a graph's access level and/or token status in config.
16
- * No-ops if nothing changed (avoids unnecessary disk writes).
17
- */
18
- export declare function updateGraphTokenStatus(nickname: string, updates: {
19
- accessLevel?: AccessLevel;
20
- lastKnownTokenStatus?: "active" | "revoked";
21
- }): Promise<void>;
22
- /**
23
- * Get all configured graphs (returns empty array if config doesn't exist)
24
- * Unlike getMcpConfig(), this doesn't throw if config is missing.
25
- */
26
- export declare function getConfiguredGraphsSafe(): Promise<GraphConfig[]>;
27
- /**
28
- * Find a graph config by nickname (case-insensitive) or name
29
- */
30
- export declare function findGraphConfig(nameOrNickname: string): Promise<GraphConfig | undefined>;
31
- /**
32
- * Get list of all configured graphs (for list_graphs tool and error messages)
33
- */
34
- export declare function getConfiguredGraphs(): Promise<Array<{
35
- nickname: string;
36
- name: string;
37
- accessLevel: string;
38
- lastKnownTokenStatus?: string;
39
- }>>;
40
- /**
41
- * Resolve which graph to use and return full config.
42
- * Stateless: explicit param → single configured graph → error
43
- */
44
- export declare function resolveGraph(providedGraph?: string): Promise<ResolvedGraph>;
45
- /**
46
- * Fetch list of open graphs from Roam's Local API.
47
- * Note: This is NOT used for graph resolution in v2.0.0.
48
- * It's kept for potential future use (e.g., showing which graphs are open).
49
- */
50
- export declare function getOpenGraphs(): Promise<Array<{
51
- name: string;
52
- type: string;
53
- }>>;
54
- //# sourceMappingURL=graph-resolver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"graph-resolver.d.ts","sourceRoot":"","sources":["../src/graph-resolver.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,aAAa,EACb,WAAW,EACX,aAAa,EAGb,WAAW,EAEZ,MAAM,YAAY,CAAC;AAyBpB,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAG/C;AAmCD,wBAAsB,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC,CA0G3D;AA+CD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B5E;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAc9E;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,WAAW,CAAC;IAAC,oBAAoB,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,GAClF,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAOtE;AAMD;;GAEG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAU9F;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAClD,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAC9F,CAQA;AAMD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAkDjF;AAiBD;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAgBpF"}