@pellux/goodvibes-sdk 0.27.2 → 0.27.3

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 (43) hide show
  1. package/dist/_internal/contracts/artifacts/operator-contract.json +134 -4
  2. package/dist/_internal/contracts/generated/foundation-metadata.d.ts +2 -2
  3. package/dist/_internal/contracts/generated/foundation-metadata.js +2 -2
  4. package/dist/_internal/contracts/generated/operator-contract.d.ts.map +1 -1
  5. package/dist/_internal/contracts/generated/operator-contract.js +134 -4
  6. package/dist/_internal/contracts/generated/operator-method-ids.d.ts +1 -1
  7. package/dist/_internal/contracts/generated/operator-method-ids.d.ts.map +1 -1
  8. package/dist/_internal/contracts/generated/operator-method-ids.js +1 -0
  9. package/dist/_internal/platform/control-plane/method-catalog-homegraph.d.ts.map +1 -1
  10. package/dist/_internal/platform/control-plane/method-catalog-homegraph.js +10 -1
  11. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts +1 -0
  12. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.d.ts.map +1 -1
  13. package/dist/_internal/platform/control-plane/operator-contract-schemas-knowledge.js +7 -0
  14. package/dist/_internal/platform/daemon/http/home-graph-routes.d.ts.map +1 -1
  15. package/dist/_internal/platform/daemon/http/home-graph-routes.js +7 -0
  16. package/dist/_internal/platform/knowledge/extractors.d.ts.map +1 -1
  17. package/dist/_internal/platform/knowledge/extractors.js +1 -116
  18. package/dist/_internal/platform/knowledge/home-graph/auto-link.d.ts +27 -0
  19. package/dist/_internal/platform/knowledge/home-graph/auto-link.d.ts.map +1 -0
  20. package/dist/_internal/platform/knowledge/home-graph/auto-link.js +236 -0
  21. package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts +1 -0
  22. package/dist/_internal/platform/knowledge/home-graph/generated-pages.d.ts.map +1 -1
  23. package/dist/_internal/platform/knowledge/home-graph/generated-pages.js +14 -8
  24. package/dist/_internal/platform/knowledge/home-graph/index.d.ts +1 -1
  25. package/dist/_internal/platform/knowledge/home-graph/index.d.ts.map +1 -1
  26. package/dist/_internal/platform/knowledge/home-graph/pages.d.ts +11 -0
  27. package/dist/_internal/platform/knowledge/home-graph/pages.d.ts.map +1 -0
  28. package/dist/_internal/platform/knowledge/home-graph/pages.js +44 -0
  29. package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts +3 -1
  30. package/dist/_internal/platform/knowledge/home-graph/rendering.d.ts.map +1 -1
  31. package/dist/_internal/platform/knowledge/home-graph/rendering.js +161 -4
  32. package/dist/_internal/platform/knowledge/home-graph/service.d.ts +6 -1
  33. package/dist/_internal/platform/knowledge/home-graph/service.d.ts.map +1 -1
  34. package/dist/_internal/platform/knowledge/home-graph/service.js +56 -3
  35. package/dist/_internal/platform/knowledge/home-graph/types.d.ts +17 -0
  36. package/dist/_internal/platform/knowledge/home-graph/types.d.ts.map +1 -1
  37. package/dist/_internal/platform/knowledge/index.d.ts +1 -1
  38. package/dist/_internal/platform/knowledge/index.d.ts.map +1 -1
  39. package/dist/_internal/platform/knowledge/pdf-extractor.d.ts +3 -0
  40. package/dist/_internal/platform/knowledge/pdf-extractor.d.ts.map +1 -0
  41. package/dist/_internal/platform/knowledge/pdf-extractor.js +346 -0
  42. package/dist/_internal/platform/version.js +1 -1
  43. package/package.json +1 -1
@@ -2,6 +2,7 @@ import JSZip from 'jszip';
2
2
  import { extname } from 'node:path';
3
3
  import { guessMimeType } from '../artifacts/types.js';
4
4
  import { extractReadableHtml } from './html-readability.js';
5
+ import { extractPdf } from './pdf-extractor.js';
5
6
  const MAX_STRUCTURE_SEARCH_TEXT_CHARS = 128 * 1024;
6
7
  function decodeHtmlEntities(value) {
7
8
  return value
@@ -306,122 +307,6 @@ function extractYaml(buffer) {
306
307
  metadata: {},
307
308
  };
308
309
  }
309
- async function extractPdf(buffer) {
310
- const parsed = await extractPdfWithPdfJs(buffer);
311
- if (parsed)
312
- return parsed;
313
- return extractPdfRawStreams(buffer);
314
- }
315
- async function extractPdfWithPdfJs(buffer) {
316
- try {
317
- const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
318
- const loadingTask = pdfjs.getDocument({
319
- data: new Uint8Array(buffer),
320
- useSystemFonts: true,
321
- });
322
- const document = await loadingTask.promise;
323
- const pageCount = document.numPages;
324
- const pageTexts = [];
325
- for (let pageNumber = 1; pageNumber <= pageCount; pageNumber += 1) {
326
- const page = await document.getPage(pageNumber);
327
- const content = await page.getTextContent();
328
- const lines = textContentItemsToLines(content.items);
329
- if (lines.length > 0)
330
- pageTexts.push(lines.join('\n'));
331
- page.cleanup();
332
- }
333
- await document.destroy();
334
- const text = cleanText(pageTexts.join('\n\n'));
335
- if (!text)
336
- return undefined;
337
- const searchText = searchTextPayload(text);
338
- return {
339
- extractorId: 'pdfjs',
340
- format: 'pdf',
341
- title: firstNonEmptyLine(text) ?? 'PDF document',
342
- summary: summarizeText(text) ?? 'PDF document.',
343
- excerpt: excerptText(text),
344
- sections: uniqueStrings(text.split(/\n+/), 24),
345
- links: uniqueStrings(Array.from(text.matchAll(/\bhttps?:\/\/[^\s)]+/g), (match) => match[0]), 50),
346
- estimatedTokens: estimateTokens(text),
347
- structure: {
348
- pageCount,
349
- extractedTextChars: text.length,
350
- ...(searchText ? { searchText } : {}),
351
- },
352
- metadata: {
353
- limitations: ['PDF text extraction does not perform OCR for scanned images.'],
354
- },
355
- };
356
- }
357
- catch {
358
- return undefined;
359
- }
360
- }
361
- function textContentItemsToLines(items) {
362
- const lines = [];
363
- let current = '';
364
- for (const item of items) {
365
- const record = unknownRecord(item);
366
- const text = typeof record.str === 'string' ? cleanText(record.str) : '';
367
- if (text)
368
- current = current ? `${current} ${text}` : text;
369
- if (record.hasEOL === true && current) {
370
- lines.push(current);
371
- current = '';
372
- }
373
- }
374
- if (current)
375
- lines.push(current);
376
- return lines;
377
- }
378
- function unknownRecord(value) {
379
- return value && typeof value === 'object' ? value : {};
380
- }
381
- function extractPdfRawStreams(buffer) {
382
- const body = buffer.toString('latin1');
383
- const texts = [];
384
- const streamRe = /stream\r?\n([\s\S]*?)\r?\nendstream/g;
385
- let match;
386
- while ((match = streamRe.exec(body)) !== null) {
387
- const chunk = match[1];
388
- const parenRe = /\(([^)\\]*(?:\\.[^)\\]*)*)\)/g;
389
- let textMatch;
390
- while ((textMatch = parenRe.exec(chunk)) !== null) {
391
- const text = cleanText(textMatch[1]
392
- .replace(/\\n/g, '\n')
393
- .replace(/\\r/g, '\r')
394
- .replace(/\\t/g, '\t')
395
- .replace(/\\\\/g, '\\')
396
- .replace(/\\\(/g, '(')
397
- .replace(/\\\)/g, ')'));
398
- if (text.length > 1)
399
- texts.push(text);
400
- }
401
- }
402
- const combined = uniqueStrings(texts, 64).join('\n');
403
- const searchable = uniqueStrings(texts, 512).join('\n');
404
- const searchText = searchTextPayload(searchable);
405
- return {
406
- extractorId: 'pdf',
407
- format: 'pdf',
408
- title: firstNonEmptyLine(combined) ?? 'PDF document',
409
- summary: summarizeText(combined) ?? 'PDF extraction produced limited text; OCR is not used in-core.',
410
- excerpt: excerptText(combined),
411
- sections: uniqueStrings(combined.split(/\n+/), 8),
412
- links: uniqueStrings(Array.from(combined.matchAll(/\bhttps?:\/\/[^\s)]+/g), (match) => match[0]), 50),
413
- estimatedTokens: estimateTokens(combined),
414
- structure: {
415
- extractedStringCount: texts.length,
416
- ...(searchText ? { searchText } : {}),
417
- },
418
- metadata: {
419
- limitations: texts.length === 0
420
- ? ['No readable text streams were found. Complex PDFs need OCR or a dedicated provider.']
421
- : ['PDF extraction is best-effort and does not use OCR.'],
422
- },
423
- };
424
- }
425
310
  async function extractDocx(buffer) {
426
311
  const zip = await JSZip.loadAsync(buffer);
427
312
  const file = zip.file('word/document.xml');
@@ -0,0 +1,27 @@
1
+ import type { KnowledgeStore } from '../store.js';
2
+ import type { KnowledgeEdgeRecord, KnowledgeExtractionRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
3
+ import type { HomeGraphState } from './state.js';
4
+ export interface HomeGraphAutoLinkResult {
5
+ readonly edge: KnowledgeEdgeRecord;
6
+ readonly node: KnowledgeNodeRecord;
7
+ readonly relation: string;
8
+ readonly score: number;
9
+ readonly reasons: readonly string[];
10
+ }
11
+ export declare function autoLinkHomeGraphSource(input: {
12
+ readonly store: KnowledgeStore;
13
+ readonly spaceId: string;
14
+ readonly installationId: string;
15
+ readonly source: KnowledgeSourceRecord;
16
+ readonly extraction?: KnowledgeExtractionRecord;
17
+ readonly state: HomeGraphState;
18
+ }): Promise<HomeGraphAutoLinkResult | undefined>;
19
+ export declare function autoLinkHomeGraphSources(input: {
20
+ readonly store: KnowledgeStore;
21
+ readonly spaceId: string;
22
+ readonly installationId: string;
23
+ readonly sources: readonly KnowledgeSourceRecord[];
24
+ readonly extractionBySourceId: ReadonlyMap<string, KnowledgeExtractionRecord>;
25
+ readonly state: HomeGraphState;
26
+ }): Promise<readonly HomeGraphAutoLinkResult[]>;
27
+ //# sourceMappingURL=auto-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-link.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/auto-link.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAQrB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAOjD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,wBAAsB,uBAAuB,CAAC,KAAK,EAAE;IACnD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,UAAU,CAAC,EAAE,yBAAyB,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC,GAAG,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CA2B/C;AAED,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;IAC9E,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC,GAAG,OAAO,CAAC,SAAS,uBAAuB,EAAE,CAAC,CAiB9C"}
@@ -0,0 +1,236 @@
1
+ import { buildHomeGraphMetadata, edgeIsActive, isGeneratedPageSource, readRecord, uniqueStrings, } from './helpers.js';
2
+ const MIN_AUTO_LINK_SCORE = 90;
3
+ const MIN_SOURCE_TEXT_CHARS = 16;
4
+ const MAX_TEXT_FIELD_CHARS = 32_768;
5
+ const GENERIC_TOKENS = new Set(['device', 'home', 'assistant', 'smart', 'manual', 'owner', 'guide', 'user', 'the']);
6
+ export async function autoLinkHomeGraphSource(input) {
7
+ if (isGeneratedPageSource(input.source) || shouldSkipAutoLink(input.source))
8
+ return undefined;
9
+ if (hasActiveSourceLink(input.source.id, input.state.edges))
10
+ return undefined;
11
+ const candidates = scoreCandidates(input.source, input.extraction, input.state);
12
+ const best = candidates[0];
13
+ if (!best || best.score < MIN_AUTO_LINK_SCORE)
14
+ return undefined;
15
+ const next = candidates[1];
16
+ if (next && best.score - next.score < 20 && !best.reasons.some((reason) => reason.startsWith('exact-model:'))) {
17
+ return undefined;
18
+ }
19
+ const relation = inferRelation(input.source);
20
+ const edge = await input.store.upsertEdge({
21
+ fromKind: 'source',
22
+ fromId: input.source.id,
23
+ toKind: 'node',
24
+ toId: best.node.id,
25
+ relation,
26
+ weight: Math.min(5, Math.max(1, Math.round(best.score / 60))),
27
+ metadata: buildHomeGraphMetadata(input.spaceId, input.installationId, {
28
+ linkStatus: 'active',
29
+ linkMethod: 'homegraph-auto-link',
30
+ autoLinkedAt: Date.now(),
31
+ autoLinkScore: best.score,
32
+ autoLinkReasons: best.reasons,
33
+ }),
34
+ });
35
+ return { edge, node: best.node, relation, score: best.score, reasons: best.reasons };
36
+ }
37
+ export async function autoLinkHomeGraphSources(input) {
38
+ const linked = [];
39
+ for (const source of input.sources) {
40
+ const result = await autoLinkHomeGraphSource({
41
+ store: input.store,
42
+ spaceId: input.spaceId,
43
+ installationId: input.installationId,
44
+ source,
45
+ extraction: input.extractionBySourceId.get(source.id),
46
+ state: {
47
+ ...input.state,
48
+ edges: [...input.state.edges, ...linked.map((entry) => entry.edge)],
49
+ },
50
+ });
51
+ if (result)
52
+ linked.push(result);
53
+ }
54
+ return linked;
55
+ }
56
+ function scoreCandidates(source, extraction, state) {
57
+ const sourceText = sourceEvidenceText(source, extraction);
58
+ if (sourceText.length < MIN_SOURCE_TEXT_CHARS)
59
+ return [];
60
+ const lower = sourceText.toLowerCase();
61
+ const sourceTokens = new Set(tokenize(sourceText));
62
+ return state.nodes
63
+ .filter((node) => isAutoLinkCandidateNode(node))
64
+ .map((node) => {
65
+ const reasons = [];
66
+ let score = 0;
67
+ const identity = nodeIdentity(node, state);
68
+ for (const model of identity.models) {
69
+ if (model.length >= 4 && includesIdentity(lower, model)) {
70
+ score += model.length >= 8 ? 180 : 120;
71
+ reasons.push(`exact-model:${model}`);
72
+ }
73
+ }
74
+ for (const entityId of identity.entityIds) {
75
+ if (includesIdentity(lower, entityId)) {
76
+ score += 140;
77
+ reasons.push(`entity-id:${entityId}`);
78
+ }
79
+ }
80
+ for (const deviceId of identity.deviceIds) {
81
+ if (includesIdentity(lower, deviceId)) {
82
+ score += 120;
83
+ reasons.push(`device-id:${deviceId}`);
84
+ }
85
+ }
86
+ const titleTokens = tokenize(node.title).filter((token) => !isGenericToken(token));
87
+ const overlap = titleTokens.filter((token) => sourceTokens.has(token));
88
+ if (overlap.length > 0) {
89
+ score += overlap.reduce((sum, token) => sum + (token.length <= 3 ? 12 : 18), 0);
90
+ reasons.push(`title:${overlap.slice(0, 5).join(',')}`);
91
+ }
92
+ const manufacturerMatches = identity.manufacturers.filter((manufacturer) => includesIdentity(lower, manufacturer));
93
+ if (manufacturerMatches.length > 0) {
94
+ score += 28;
95
+ reasons.push(`manufacturer:${manufacturerMatches[0]}`);
96
+ }
97
+ const relatedEntityMatches = identity.relatedEntityTokens.filter((token) => sourceTokens.has(token));
98
+ if (relatedEntityMatches.length > 0) {
99
+ score += Math.min(50, relatedEntityMatches.length * 10);
100
+ reasons.push(`related-entity:${relatedEntityMatches.slice(0, 5).join(',')}`);
101
+ }
102
+ if (node.kind === 'ha_device' && isManualLikeSource(source))
103
+ score += 16;
104
+ if (node.kind === 'ha_integration' && isIntegrationDocumentationSource(source))
105
+ score += 40;
106
+ if (node.kind === 'ha_integration' && isManualLikeSource(source))
107
+ score -= 45;
108
+ return { node, score, reasons };
109
+ })
110
+ .filter((entry) => entry.score > 0)
111
+ .sort((left, right) => right.score - left.score || left.node.id.localeCompare(right.node.id));
112
+ }
113
+ function shouldSkipAutoLink(source) {
114
+ const kind = typeof source.metadata.homeGraphSourceKind === 'string' ? source.metadata.homeGraphSourceKind : '';
115
+ return kind === 'snapshot' || kind === 'generated-page';
116
+ }
117
+ function hasActiveSourceLink(sourceId, edges) {
118
+ return edges.some((edge) => edgeIsActive(edge) && ((edge.fromKind === 'source' && edge.fromId === sourceId && edge.toKind === 'node')
119
+ || (edge.fromKind === 'node' && edge.toKind === 'source' && edge.toId === sourceId)));
120
+ }
121
+ function isAutoLinkCandidateNode(node) {
122
+ return node.kind === 'ha_device'
123
+ || node.kind === 'ha_entity'
124
+ || node.kind === 'ha_integration'
125
+ || node.kind === 'ha_area'
126
+ || node.kind === 'ha_room';
127
+ }
128
+ function sourceEvidenceText(source, extraction) {
129
+ const structure = readRecord(extraction?.structure);
130
+ return uniqueStrings([
131
+ source.title,
132
+ source.summary,
133
+ source.description,
134
+ source.sourceUri,
135
+ source.canonicalUri,
136
+ source.tags.join(' '),
137
+ extraction?.title,
138
+ extraction?.summary,
139
+ extraction?.excerpt,
140
+ ...(extraction?.sections ?? []),
141
+ readString(structure.searchText),
142
+ ]).join('\n').slice(0, MAX_TEXT_FIELD_CHARS);
143
+ }
144
+ function nodeIdentity(node, state) {
145
+ const homeAssistant = readRecord(node.metadata.homeAssistant);
146
+ const relatedEntities = node.kind === 'ha_device'
147
+ ? relatedEntityNodes(node.id, state)
148
+ : [];
149
+ return {
150
+ models: uniqueStrings([
151
+ readString(node.metadata.model),
152
+ readString(node.metadata.modelId),
153
+ readString(node.metadata.model_id),
154
+ ]),
155
+ manufacturers: uniqueStrings([
156
+ readString(node.metadata.manufacturer),
157
+ readString(node.metadata.vendor),
158
+ ]),
159
+ entityIds: uniqueStrings([
160
+ readString(homeAssistant.entityId),
161
+ ...relatedEntities.map((entity) => readString(readRecord(entity.metadata.homeAssistant).entityId)),
162
+ ]),
163
+ deviceIds: uniqueStrings([
164
+ readString(homeAssistant.deviceId),
165
+ node.kind === 'ha_device' ? readString(homeAssistant.objectId) : undefined,
166
+ ]),
167
+ relatedEntityTokens: uniqueStrings(relatedEntities.flatMap((entity) => [
168
+ ...tokenize(entity.title),
169
+ ...tokenize(readString(readRecord(entity.metadata.homeAssistant).entityId) ?? ''),
170
+ readString(entity.metadata.domain),
171
+ readString(entity.metadata.platform),
172
+ ])),
173
+ };
174
+ }
175
+ function relatedEntityNodes(deviceNodeId, state) {
176
+ const entityIds = new Set(state.edges.filter((edge) => (edgeIsActive(edge)
177
+ && edge.fromKind === 'node'
178
+ && edge.toKind === 'node'
179
+ && edge.toId === deviceNodeId
180
+ && edge.relation === 'belongs_to_device')).map((edge) => edge.fromId));
181
+ return state.nodes.filter((node) => entityIds.has(node.id) && node.kind === 'ha_entity');
182
+ }
183
+ function inferRelation(source) {
184
+ const tags = source.tags.map((tag) => tag.toLowerCase());
185
+ const text = [source.sourceType, source.title, source.sourceUri, ...tags].join(' ').toLowerCase();
186
+ if (text.includes('receipt'))
187
+ return 'has_receipt';
188
+ if (text.includes('warranty'))
189
+ return 'has_warranty';
190
+ if (isManualLikeSource(source))
191
+ return 'has_manual';
192
+ return 'source_for';
193
+ }
194
+ function isManualLikeSource(source) {
195
+ const tags = source.tags.map((tag) => tag.toLowerCase());
196
+ const text = [source.sourceType, source.title, source.sourceUri, ...tags].join(' ').toLowerCase();
197
+ return source.sourceType === 'manual'
198
+ || tags.includes('manual')
199
+ || tags.includes('artifact')
200
+ || tags.includes('document')
201
+ || /\bmanual\b|\.pdf\b|owner.?s guide|user guide/.test(text);
202
+ }
203
+ function isIntegrationDocumentationSource(source) {
204
+ const tags = source.tags.map((tag) => tag.toLowerCase());
205
+ const kind = typeof source.metadata.homeGraphSourceKind === 'string'
206
+ ? source.metadata.homeGraphSourceKind.toLowerCase()
207
+ : '';
208
+ return tags.includes('integration') || tags.includes('documentation') || kind === 'documentation-candidate';
209
+ }
210
+ function tokenize(value) {
211
+ return uniqueStrings(value.toLowerCase().split(/[^a-z0-9_.:-]+/).filter((token) => token.length >= 2));
212
+ }
213
+ function isGenericToken(value) {
214
+ return GENERIC_TOKENS.has(value);
215
+ }
216
+ function includesIdentity(haystack, identity) {
217
+ const normalized = identity.trim().toLowerCase();
218
+ if (!normalized)
219
+ return false;
220
+ if (/^[a-z0-9_.:-]+$/.test(normalized)) {
221
+ return new RegExp(`(?:^|[^a-z0-9])${escapeRegExp(normalized)}(?:$|[^a-z0-9])`).test(haystack);
222
+ }
223
+ return haystack.includes(normalized);
224
+ }
225
+ function readString(value) {
226
+ if (typeof value === 'string') {
227
+ const trimmed = value.trim();
228
+ return trimmed.length > 0 ? trimmed : undefined;
229
+ }
230
+ if (typeof value === 'number' && Number.isFinite(value))
231
+ return String(value);
232
+ return undefined;
233
+ }
234
+ function escapeRegExp(value) {
235
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
236
+ }
@@ -10,6 +10,7 @@ interface HomeGraphPageContext {
10
10
  export declare function generateAutomaticHomeGraphPages(context: HomeGraphPageContext & {
11
11
  readonly input: HomeGraphSnapshotInput;
12
12
  }): Promise<HomeGraphGeneratedPagesSummary>;
13
+ export declare function refreshAutomaticHomeGraphPages(context: HomeGraphPageContext): Promise<HomeGraphGeneratedPagesSummary>;
13
14
  export declare function refreshHomeGraphDevicePassport(context: HomeGraphPageContext & {
14
15
  readonly input: HomeGraphProjectionInput;
15
16
  }): Promise<HomeGraphDevicePassportResult & {
@@ -1 +1 @@
1
- {"version":3,"file":"generated-pages.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/generated-pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA8BlD,OAAO,KAAK,EACV,6BAA6B,EAC7B,8BAA8B,EAC9B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GACzE,OAAO,CAAC,8BAA8B,CAAC,CAoEzC;AAED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,6BAA6B,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CA0EhF;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,yBAAyB,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAsC5E;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,yBAAyB,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB5E"}
1
+ {"version":3,"file":"generated-pages.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/generated-pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA8BlD,OAAO,KAAK,EACV,6BAA6B,EAC7B,8BAA8B,EAC9B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,GACzE,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AAED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AA2ED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,6BAA6B,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CA0EhF;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,yBAAyB,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAsC5E;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,oBAAoB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GAC3E,OAAO,CAAC,yBAAyB,GAAG;IAAE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAyB5E"}
@@ -3,13 +3,19 @@ import { HOME_GRAPH_CONNECTOR_ID, buildHomeGraphMetadata, edgeIsActive, homeGrap
3
3
  import { findHomeAssistantNode, missingDevicePassportFields, readHomeGraphState, renderHomeGraphState, safeHomeGraphFilename, sourcesLinkedToNode, } from './state.js';
4
4
  import { renderDevicePassportPage, renderPacketPage, renderRoomPage, } from './rendering.js';
5
5
  export async function generateAutomaticHomeGraphPages(context) {
6
- const options = context.input.pageAutomation ?? {};
6
+ return generateHomeGraphPagesForCurrentState(context, context.input.pageAutomation ?? {});
7
+ }
8
+ export async function refreshAutomaticHomeGraphPages(context) {
9
+ return generateHomeGraphPagesForCurrentState(context, {});
10
+ }
11
+ async function generateHomeGraphPagesForCurrentState(context, options) {
12
+ const effectiveOptions = options ?? {};
7
13
  const summary = createGeneratedPagesSummary();
8
- if (options.enabled === false)
14
+ if (effectiveOptions.enabled === false)
9
15
  return summary;
10
16
  const state = readHomeGraphState(context.store, context.spaceId);
11
- if (options.devicePassports !== false) {
12
- const devices = limitRecords(state.nodes.filter((node) => node.kind === 'ha_device' && node.status !== 'stale').sort(compareByTitle), options.maxDevicePassports);
17
+ if (effectiveOptions.devicePassports !== false) {
18
+ const devices = limitRecords(state.nodes.filter((node) => node.kind === 'ha_device' && node.status !== 'stale').sort(compareByTitle), effectiveOptions.maxDevicePassports);
13
19
  for (const device of devices) {
14
20
  const deviceId = readHomeAssistantObjectId(device, 'objectId', 'deviceId') ?? device.id;
15
21
  try {
@@ -36,10 +42,10 @@ export async function generateAutomaticHomeGraphPages(context) {
36
42
  }
37
43
  }
38
44
  }
39
- if (options.roomPages !== false) {
45
+ if (effectiveOptions.roomPages !== false) {
40
46
  const rooms = limitRecords(state.nodes
41
47
  .filter((node) => (node.kind === 'ha_area' || node.kind === 'ha_room') && node.status !== 'stale')
42
- .sort(compareByTitle), options.maxRoomPages);
48
+ .sort(compareByTitle), effectiveOptions.maxRoomPages);
43
49
  for (const room of rooms) {
44
50
  const areaId = readHomeAssistantObjectId(room, 'objectId', 'areaId') ?? room.id;
45
51
  try {
@@ -109,7 +115,7 @@ export async function refreshHomeGraphDevicePassport(context) {
109
115
  relation: 'source_for',
110
116
  metadata: buildHomeGraphMetadata(spaceId, installationId),
111
117
  });
112
- const markdown = renderDevicePassportPage({ spaceId, device, entities, sources, issues, missingFields });
118
+ const markdown = renderDevicePassportPage({ spaceId, device, entities, sources, extractions: state.extractions, issues, missingFields });
113
119
  const generated = await materializeGeneratedMarkdown({
114
120
  store,
115
121
  artifactStore,
@@ -147,7 +153,7 @@ export async function generateHomeGraphRoomPage(context) {
147
153
  const state = readHomeGraphState(store, spaceId);
148
154
  const areaId = input.areaId ?? input.roomId;
149
155
  const title = input.title ?? resolveRoomTitle(state.nodes, areaId) ?? 'Home Graph Room';
150
- const markdown = renderRoomPage({ ...state, title }, areaId);
156
+ const markdown = renderRoomPage({ ...state, title, extractions: state.extractions }, areaId);
151
157
  const filename = `${safeHomeGraphFilename(title)}.md`;
152
158
  const targetNode = areaId
153
159
  ? findHomeAssistantNode(state.nodes, 'ha_area', areaId) ?? findHomeAssistantNode(state.nodes, 'ha_room', areaId)
@@ -1,4 +1,4 @@
1
1
  export { HomeGraphService } from './service.js';
2
2
  export { HOME_GRAPH_NODE_KINDS, HOME_GRAPH_RELATIONS, } from './types.js';
3
- export type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphGeneratedPagesSummary, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphKnowledgeTarget, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphMapEdge, HomeGraphMapHaFilterInput, HomeGraphMapInput, HomeGraphMapNode, HomeGraphMapResult, HomeGraphNodeKind, HomeGraphObjectInput, HomeGraphObjectKind, HomeGraphPageAutomationOptions, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphReindexResult, HomeGraphRelation, HomeGraphReviewInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult, } from './types.js';
3
+ export type { HomeGraphAskInput, HomeGraphAskResult, HomeGraphDevicePassportResult, HomeGraphExport, HomeGraphGeneratedPagesSummary, HomeGraphIngestArtifactInput, HomeGraphIngestNoteInput, HomeGraphIngestResult, HomeGraphIngestUrlInput, HomeGraphKnowledgeTarget, HomeGraphLinkInput, HomeGraphLinkResult, HomeGraphMapEdge, HomeGraphMapHaFilterInput, HomeGraphMapInput, HomeGraphMapNode, HomeGraphMapResult, HomeGraphNodeKind, HomeGraphObjectInput, HomeGraphObjectKind, HomeGraphPageAutomationOptions, HomeGraphPageListResult, HomeGraphProjectionInput, HomeGraphProjectionResult, HomeGraphReindexResult, HomeGraphRelation, HomeGraphReviewInput, HomeGraphSnapshotInput, HomeGraphStatus, HomeGraphSyncResult, } from './types.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,6BAA6B,EAC7B,eAAe,EACf,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,8BAA8B,EAC9B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,6BAA6B,EAC7B,eAAe,EACf,8BAA8B,EAC9B,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,8BAA8B,EAC9B,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ArtifactStore } from '../../artifacts/index.js';
2
+ import type { KnowledgeSourceRecord } from '../types.js';
3
+ import type { HomeGraphPageListResult } from './types.js';
4
+ export declare function listHomeGraphPages(input: {
5
+ readonly artifactStore: ArtifactStore;
6
+ readonly spaceId: string;
7
+ readonly sources: readonly KnowledgeSourceRecord[];
8
+ readonly limit: number;
9
+ readonly includeMarkdown: boolean;
10
+ }): Promise<HomeGraphPageListResult>;
11
+ //# sourceMappingURL=pages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA0BnC"}
@@ -0,0 +1,44 @@
1
+ import { isGeneratedPageSource } from './helpers.js';
2
+ export async function listHomeGraphPages(input) {
3
+ const pages = [];
4
+ const sources = input.sources
5
+ .filter(isGeneratedPageSource)
6
+ .sort(compareGeneratedPages)
7
+ .slice(0, input.limit);
8
+ for (const source of sources) {
9
+ const artifact = typeof source.artifactId === 'string' ? input.artifactStore.get(source.artifactId) : undefined;
10
+ const markdown = input.includeMarkdown && artifact
11
+ ? await readMarkdown(input.artifactStore, artifact.id)
12
+ : undefined;
13
+ pages.push({
14
+ source,
15
+ ...(artifact ? {
16
+ artifact: {
17
+ id: artifact.id,
18
+ mimeType: artifact.mimeType,
19
+ filename: artifact.filename,
20
+ createdAt: artifact.createdAt,
21
+ metadata: artifact.metadata,
22
+ },
23
+ } : {}),
24
+ ...(markdown ? { markdown } : {}),
25
+ });
26
+ }
27
+ return { ok: true, spaceId: input.spaceId, pages };
28
+ }
29
+ function compareGeneratedPages(left, right) {
30
+ const leftKind = typeof left.metadata.projectionKind === 'string' ? left.metadata.projectionKind : '';
31
+ const rightKind = typeof right.metadata.projectionKind === 'string' ? right.metadata.projectionKind : '';
32
+ return leftKind.localeCompare(rightKind)
33
+ || (left.title ?? left.id).localeCompare(right.title ?? right.id)
34
+ || left.id.localeCompare(right.id);
35
+ }
36
+ async function readMarkdown(artifactStore, artifactId) {
37
+ try {
38
+ const { buffer } = await artifactStore.readContent(artifactId);
39
+ return buffer.toString('utf-8');
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
@@ -1,4 +1,4 @@
1
- import type { KnowledgeEdgeRecord, KnowledgeIssueRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
1
+ import type { KnowledgeEdgeRecord, KnowledgeExtractionRecord, KnowledgeIssueRecord, KnowledgeNodeRecord, KnowledgeSourceRecord } from '../types.js';
2
2
  import type { HomeGraphMapInput, HomeGraphMapResult } from './types.js';
3
3
  export interface HomeGraphRenderState {
4
4
  readonly spaceId: string;
@@ -7,6 +7,7 @@ export interface HomeGraphRenderState {
7
7
  readonly nodes: readonly KnowledgeNodeRecord[];
8
8
  readonly edges: readonly KnowledgeEdgeRecord[];
9
9
  readonly issues: readonly KnowledgeIssueRecord[];
10
+ readonly extractions?: readonly KnowledgeExtractionRecord[];
10
11
  }
11
12
  export declare function renderRoomPage(state: HomeGraphRenderState, areaId?: string): string;
12
13
  export declare function renderDevicePassportPage(input: {
@@ -14,6 +15,7 @@ export declare function renderDevicePassportPage(input: {
14
15
  readonly device: KnowledgeNodeRecord;
15
16
  readonly entities: readonly KnowledgeNodeRecord[];
16
17
  readonly sources: readonly KnowledgeSourceRecord[];
18
+ readonly extractions?: readonly KnowledgeExtractionRecord[];
17
19
  readonly issues: readonly KnowledgeIssueRecord[];
18
20
  readonly missingFields: readonly string[];
19
21
  }): string;
@@ -1 +1 @@
1
- {"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EAEpB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAIrB,OAAO,KAAK,EAA6B,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEnG,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;CAClD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAuCnF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,GAAG,MAAM,CAoBT;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC,GAAG,MAAM,CAkBd;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE,iBAAsB,GAAG,kBAAkB,CAcnH"}
1
+ {"version":3,"file":"rendering.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/knowledge/home-graph/rendering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EAEpB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAKrB,OAAO,KAAK,EAA6B,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEnG,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,QAAQ,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;CAC7D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAiDnF;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC5D,QAAQ,CAAC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,GAAG,MAAM,CA8BT;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC,GAAG,MAAM,CAkBd;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE,iBAAsB,GAAG,kBAAkB,CAcnH"}