alif-digest 1.0.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.
- package/.github/workflows/publish.yml +33 -0
- package/.husky/pre-commit +1 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +88 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/run.d.ts +4 -0
- package/dist/cli/commands/run.js +46 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/schedule.d.ts +1 -0
- package/dist/cli/commands/schedule.js +94 -0
- package/dist/cli/commands/schedule.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +29 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/config-manager.d.ts +14 -0
- package/dist/core/config-manager.js +65 -0
- package/dist/core/config-manager.js.map +1 -0
- package/dist/core/config-schema.d.ts +40 -0
- package/dist/core/config-schema.js +24 -0
- package/dist/core/config-schema.js.map +1 -0
- package/dist/core/default-keywords.d.ts +1 -0
- package/dist/core/default-keywords.js +10 -0
- package/dist/core/default-keywords.js.map +1 -0
- package/dist/core/filters/deduplicator.d.ts +10 -0
- package/dist/core/filters/deduplicator.js +34 -0
- package/dist/core/filters/deduplicator.js.map +1 -0
- package/dist/core/filters/keywords.d.ts +6 -0
- package/dist/core/filters/keywords.js +17 -0
- package/dist/core/filters/keywords.js.map +1 -0
- package/dist/core/orchestrator.d.ts +6 -0
- package/dist/core/orchestrator.js +44 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/pipeline.d.ts +15 -0
- package/dist/core/pipeline.js +140 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/scheduler.d.ts +9 -0
- package/dist/core/scheduler.js +64 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/scraper-types.d.ts +27 -0
- package/dist/core/scraper-types.js +3 -0
- package/dist/core/scraper-types.js.map +1 -0
- package/dist/core/scrapers/api-scraper.d.ts +4 -0
- package/dist/core/scrapers/api-scraper.js +46 -0
- package/dist/core/scrapers/api-scraper.js.map +1 -0
- package/dist/core/scrapers/arxiv-scraper.d.ts +4 -0
- package/dist/core/scrapers/arxiv-scraper.js +34 -0
- package/dist/core/scrapers/arxiv-scraper.js.map +1 -0
- package/dist/core/scrapers/json-scraper.d.ts +4 -0
- package/dist/core/scrapers/json-scraper.js +56 -0
- package/dist/core/scrapers/json-scraper.js.map +1 -0
- package/dist/core/scrapers/rss-scraper.d.ts +6 -0
- package/dist/core/scrapers/rss-scraper.js +32 -0
- package/dist/core/scrapers/rss-scraper.js.map +1 -0
- package/dist/core/scrapers/scrape-scraper.d.ts +4 -0
- package/dist/core/scrapers/scrape-scraper.js +49 -0
- package/dist/core/scrapers/scrape-scraper.js.map +1 -0
- package/dist/db/article-store.d.ts +22 -0
- package/dist/db/article-store.js +43 -0
- package/dist/db/article-store.js.map +1 -0
- package/dist/db/connection.d.ts +2 -0
- package/dist/db/connection.js +15 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/migrate.d.ts +2 -0
- package/dist/db/migrate.js +60 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schedule-store.d.ts +17 -0
- package/dist/db/schedule-store.js +23 -0
- package/dist/db/schedule-store.js.map +1 -0
- package/dist/db/source-health-store.d.ts +16 -0
- package/dist/db/source-health-store.js +31 -0
- package/dist/db/source-health-store.js.map +1 -0
- package/dist/providers/delivery/index.d.ts +18 -0
- package/dist/providers/delivery/index.js +2 -0
- package/dist/providers/delivery/index.js.map +1 -0
- package/dist/providers/delivery/slack.d.ts +6 -0
- package/dist/providers/delivery/slack.js +52 -0
- package/dist/providers/delivery/slack.js.map +1 -0
- package/dist/providers/delivery/webhook.d.ts +6 -0
- package/dist/providers/delivery/webhook.js +16 -0
- package/dist/providers/delivery/webhook.js.map +1 -0
- package/dist/providers/factory.d.ts +7 -0
- package/dist/providers/factory.js +33 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/llm/anthropic.d.ts +12 -0
- package/dist/providers/llm/anthropic.js +43 -0
- package/dist/providers/llm/anthropic.js.map +1 -0
- package/dist/providers/llm/index.d.ts +10 -0
- package/dist/providers/llm/index.js +2 -0
- package/dist/providers/llm/index.js.map +1 -0
- package/dist/providers/llm/ollama.d.ts +12 -0
- package/dist/providers/llm/ollama.js +42 -0
- package/dist/providers/llm/ollama.js.map +1 -0
- package/dist/providers/llm/openrouter.d.ts +13 -0
- package/dist/providers/llm/openrouter.js +53 -0
- package/dist/providers/llm/openrouter.js.map +1 -0
- package/dist/providers/llm/utils.d.ts +6 -0
- package/dist/providers/llm/utils.js +45 -0
- package/dist/providers/llm/utils.js.map +1 -0
- package/dist/resources/default-feeds.json +650 -0
- package/dist/resources/index.d.ts +2 -0
- package/dist/resources/index.js +3 -0
- package/dist/resources/index.js.map +1 -0
- package/eslint.config.mjs +29 -0
- package/package.json +66 -0
- package/src/cli/commands/init.ts +94 -0
- package/src/cli/commands/run.ts +52 -0
- package/src/cli/commands/schedule.ts +99 -0
- package/src/cli/index.ts +34 -0
- package/src/core/config-manager.ts +72 -0
- package/src/core/config-schema.ts +31 -0
- package/src/core/default-keywords.ts +9 -0
- package/src/core/filters/deduplicator.ts +39 -0
- package/src/core/filters/keywords.ts +18 -0
- package/src/core/orchestrator.ts +47 -0
- package/src/core/pipeline.ts +171 -0
- package/src/core/scheduler.ts +74 -0
- package/src/core/scraper-types.ts +30 -0
- package/src/core/scrapers/api-scraper.ts +45 -0
- package/src/core/scrapers/arxiv-scraper.ts +35 -0
- package/src/core/scrapers/json-scraper.ts +54 -0
- package/src/core/scrapers/rss-scraper.ts +34 -0
- package/src/core/scrapers/scrape-scraper.ts +50 -0
- package/src/db/article-store.ts +75 -0
- package/src/db/connection.ts +17 -0
- package/src/db/migrate.ts +68 -0
- package/src/db/schedule-store.ts +41 -0
- package/src/db/source-health-store.ts +42 -0
- package/src/providers/delivery/index.ts +19 -0
- package/src/providers/delivery/slack.ts +55 -0
- package/src/providers/delivery/webhook.ts +16 -0
- package/src/providers/factory.ts +37 -0
- package/src/providers/llm/anthropic.ts +48 -0
- package/src/providers/llm/index.ts +8 -0
- package/src/providers/llm/ollama.ts +44 -0
- package/src/providers/llm/openrouter.ts +56 -0
- package/src/providers/llm/utils.ts +54 -0
- package/src/resources/default-feeds.json +650 -0
- package/src/resources/index.ts +3 -0
- package/tests/config-manager.test.ts +70 -0
- package/tests/db-integration.test.ts +72 -0
- package/tests/filters.test.ts +53 -0
- package/tests/llm-provider.test.ts +115 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { parseLLMJson } from './utils.js';
|
|
3
|
+
export class OllamaProvider {
|
|
4
|
+
options;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async analyze(articles) {
|
|
9
|
+
if (articles.length === 0)
|
|
10
|
+
return [];
|
|
11
|
+
const prompt = `
|
|
12
|
+
Analyze the following AI-related news items. For each item, provide:
|
|
13
|
+
1. A concise, one-sentence summary (max 30 words).
|
|
14
|
+
2. A category (e.g., "Model Release", "Research", "Tool/SDK", "Policy", "Industry News", "Tutorial").
|
|
15
|
+
|
|
16
|
+
Return ONLY a JSON array of objects with keys "summary" and "category". Match the order of the input items.
|
|
17
|
+
|
|
18
|
+
Items:
|
|
19
|
+
${articles.map((a, idx) => `${idx + 1}. TITLE: ${a.title}\nCONTENT: ${a.content || 'None'}`).join('\n\n')}
|
|
20
|
+
`;
|
|
21
|
+
try {
|
|
22
|
+
const response = await axios.post(`${this.options.baseUrl}/api/generate`, {
|
|
23
|
+
model: this.options.model,
|
|
24
|
+
system: 'You are an AI signal analyst. Provide direct, objective summaries and categories. Do NOT include any reasoning, thinking process, or <think> tags. Always return a JSON array of objects.',
|
|
25
|
+
prompt: prompt,
|
|
26
|
+
stream: false,
|
|
27
|
+
format: 'json',
|
|
28
|
+
options: {
|
|
29
|
+
stop: ['<think>', '</think>', 'Reasoning:'],
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const ollamaData = response.data;
|
|
33
|
+
const finalResponse = ollamaData.response || ollamaData.thinking || '';
|
|
34
|
+
return parseLLMJson(finalResponse);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error(`[Ollama] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
38
|
+
return articles.map(() => ({ summary: null, category: 'Uncategorized' }));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../../src/providers/llm/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,OAAO,cAAc;IACL;IAApB,YAAoB,OAA2C;QAA3C,YAAO,GAAP,OAAO,CAAoC;IAAG,CAAC;IAEnE,KAAK,CAAC,OAAO,CAAC,QAA+C;QAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAErC,MAAM,MAAM,GAAG;;;;;;;;EAQjB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;CACxG,CAAC;QAEE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,eAAe,EAAE;gBACxE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;gBACzB,MAAM,EACJ,2LAA2L;gBAC7L,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,IAAI,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,YAAY,CAAC;iBAC5C;aACF,CAAC,CAAC;YAEH,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC;YACjC,MAAM,aAAa,GAAG,UAAU,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;YAEvE,OAAO,YAAY,CAAC,aAAa,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mBAAmB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC3F,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LLMProvider, AnalysisResult } from './index.js';
|
|
2
|
+
export declare class OpenRouterProvider implements LLMProvider {
|
|
3
|
+
private options;
|
|
4
|
+
private client;
|
|
5
|
+
constructor(options: {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
model: string;
|
|
8
|
+
});
|
|
9
|
+
analyze(articles: {
|
|
10
|
+
title: string;
|
|
11
|
+
content?: string;
|
|
12
|
+
}[]): Promise<AnalysisResult[]>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { parseLLMJson } from './utils.js';
|
|
3
|
+
export class OpenRouterProvider {
|
|
4
|
+
options;
|
|
5
|
+
client;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.client = new OpenAI({
|
|
9
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
|
10
|
+
apiKey: this.options.apiKey,
|
|
11
|
+
defaultHeaders: {
|
|
12
|
+
'HTTP-Referer': 'https://github.com/qarib/alif',
|
|
13
|
+
'X-Title': 'Alif CLI',
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async analyze(articles) {
|
|
18
|
+
if (articles.length === 0)
|
|
19
|
+
return [];
|
|
20
|
+
const prompt = `
|
|
21
|
+
Analyze the following AI-related news items. For each item, provide:
|
|
22
|
+
1. A concise, one-sentence summary (max 30 words).
|
|
23
|
+
2. A category (e.g., "Model Release", "Research", "Tool/SDK", "Policy", "Industry News", "Tutorial").
|
|
24
|
+
|
|
25
|
+
Return ONLY a JSON array of objects with keys "summary" and "category". Match the order of the input items.
|
|
26
|
+
|
|
27
|
+
Items:
|
|
28
|
+
${articles.map((a, idx) => `${idx + 1}. TITLE: ${a.title}\nCONTENT: ${a.content || 'None'}`).join('\n\n')}
|
|
29
|
+
`;
|
|
30
|
+
try {
|
|
31
|
+
const response = await this.client.chat.completions.create({
|
|
32
|
+
model: this.options.model,
|
|
33
|
+
messages: [
|
|
34
|
+
{
|
|
35
|
+
role: 'system',
|
|
36
|
+
content: 'You are an AI signal analyst. Provide direct, objective summaries and categories. Do NOT include any reasoning, thinking process, or <think> tags. Return valid JSON only.',
|
|
37
|
+
},
|
|
38
|
+
{ role: 'user', content: prompt },
|
|
39
|
+
],
|
|
40
|
+
response_format: { type: 'json_object' },
|
|
41
|
+
});
|
|
42
|
+
const content = response.choices[0].message.content;
|
|
43
|
+
if (!content)
|
|
44
|
+
throw new Error('Empty response from OpenRouter');
|
|
45
|
+
return parseLLMJson(content);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error(`[OpenRouter] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
49
|
+
return articles.map(() => ({ summary: null, category: 'Uncategorized' }));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=openrouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../../src/providers/llm/openrouter.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,OAAO,kBAAkB;IAGT;IAFZ,MAAM,CAAS;IAEvB,YAAoB,OAA0C;QAA1C,YAAO,GAAP,OAAO,CAAmC;QAC5D,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC;YACvB,OAAO,EAAE,8BAA8B;YACvC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;YAC3B,cAAc,EAAE;gBACd,cAAc,EAAE,+BAA+B;gBAC/C,SAAS,EAAE,UAAU;aACtB;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAA+C;QAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAErC,MAAM,MAAM,GAAG;;;;;;;;EAQjB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;CACxG,CAAC;QAEE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;gBACzD,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;gBACzB,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,QAAQ;wBACd,OAAO,EACL,4KAA4K;qBAC/K;oBACD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;iBAClC;gBACD,eAAe,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;aACzC,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;YACpD,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YAEhE,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,uBAAuB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC/F,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AnalysisResult } from './index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Robustly parses a JSON string from LLM output.
|
|
4
|
+
* Handles markdown code blocks and attempts to find the first array/object if surrounded by text.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseLLMJson(text: string): AnalysisResult[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Robustly parses a JSON string from LLM output.
|
|
3
|
+
* Handles markdown code blocks and attempts to find the first array/object if surrounded by text.
|
|
4
|
+
*/
|
|
5
|
+
export function parseLLMJson(text) {
|
|
6
|
+
if (!text || text.trim() === '') {
|
|
7
|
+
throw new Error('Empty response from LLM');
|
|
8
|
+
}
|
|
9
|
+
// Clean up markdown blocks if present
|
|
10
|
+
let cleanText = text.trim();
|
|
11
|
+
if (cleanText.includes('```')) {
|
|
12
|
+
const match = cleanText.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
13
|
+
if (match && match[1]) {
|
|
14
|
+
cleanText = match[1].trim();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Find the first instance of [ or {
|
|
18
|
+
const firstArray = cleanText.indexOf('[');
|
|
19
|
+
const firstObject = cleanText.indexOf('{');
|
|
20
|
+
let startIndex = -1;
|
|
21
|
+
if (firstArray !== -1 && (firstObject === -1 || firstArray < firstObject)) {
|
|
22
|
+
startIndex = firstArray;
|
|
23
|
+
}
|
|
24
|
+
else if (firstObject !== -1) {
|
|
25
|
+
startIndex = firstObject;
|
|
26
|
+
}
|
|
27
|
+
if (startIndex === -1) {
|
|
28
|
+
throw new Error('No JSON structure found in LLM response');
|
|
29
|
+
}
|
|
30
|
+
// Truncate to the last ] or }
|
|
31
|
+
const lastArray = cleanText.lastIndexOf(']');
|
|
32
|
+
const lastObject = cleanText.lastIndexOf('}');
|
|
33
|
+
const endIndex = Math.max(lastArray, lastObject);
|
|
34
|
+
if (endIndex === -1 || endIndex < startIndex) {
|
|
35
|
+
throw new Error('Incomplete JSON structure in LLM response');
|
|
36
|
+
}
|
|
37
|
+
const jsonStr = cleanText.substring(startIndex, endIndex + 1);
|
|
38
|
+
const parsed = JSON.parse(jsonStr);
|
|
39
|
+
const results = Array.isArray(parsed) ? parsed : [parsed];
|
|
40
|
+
return results.map((item) => ({
|
|
41
|
+
summary: item.summary || null,
|
|
42
|
+
category: item.category || 'Uncategorized',
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/providers/llm/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACrC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC/C,CAAC;IAED,sCAAsC;IACtC,IAAI,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACjE,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACpB,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChC,CAAC;IACL,CAAC;IAED,oCAAoC;IACpC,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAE3C,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC;IACpB,IAAI,UAAU,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,CAAC,CAAC,IAAI,UAAU,GAAG,WAAW,CAAC,EAAE,CAAC;QACxE,UAAU,GAAG,UAAU,CAAC;IAC5B,CAAC;SAAM,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QAC5B,UAAU,GAAG,WAAW,CAAC;IAC7B,CAAC;IAED,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC/D,CAAC;IAED,8BAA8B;IAC9B,MAAM,SAAS,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAEjD,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,QAAQ,GAAG,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,UAAU,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEnC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAE1D,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;QAC/B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI;QAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,eAAe;KAC7C,CAAC,CAAC,CAAC;AACR,CAAC"}
|