personal-ai 0.1.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.
- package/README.md +227 -0
- package/SKILL.md +310 -0
- package/dist/auth-Dtx8Wc3l.mjs +2 -0
- package/dist/calendar-BHcM4wfQ.mjs +91 -0
- package/dist/calendar-BHcM4wfQ.mjs.map +1 -0
- package/dist/entry.mjs +3891 -0
- package/dist/entry.mjs.map +1 -0
- package/dist/gmail-B9ja9sKN.mjs +92 -0
- package/dist/gmail-B9ja9sKN.mjs.map +1 -0
- package/dist/index.mjs +1761 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mac-C9SDXZGK.mjs +55 -0
- package/dist/mac-C9SDXZGK.mjs.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["console.spinner","console.spinner","console.spinner"],"sources":["../src/config/paths.ts","../src/config/schema.ts","../src/config/io.ts","../src/utils/slug.ts","../src/utils/frontmatter.ts","../src/raw/add.ts","../src/utils/console.ts","../src/scraper/index.ts","../src/utils/process.ts","../src/search/index.ts","../src/llm/client.ts","../src/prompts/triage.ts","../src/distill/triage.ts","../src/prompts/distill.ts","../src/distill/merge.ts","../src/distill/index.ts","../src/prompts/generate.ts","../src/generate/index.ts","../src/profile/compile.ts","../src/connectors/sanitize.ts","../src/connectors/mac/collectors.ts","../src/profile/index.ts"],"sourcesContent":["import os from \"node:os\";\nimport path from \"node:path\";\n\n/** Root data directory for pai */\nexport function getPaiHome(): string {\n return process.env.PAI_HOME ?? path.join(os.homedir(), \".pai\");\n}\n\nexport function getRawDir(): string {\n return path.join(getPaiHome(), \"raw\");\n}\n\nexport function getVaultDir(): string {\n return path.join(getPaiHome(), \"vault\");\n}\n\nexport function getSkillsDir(): string {\n return path.join(getPaiHome(), \"skills\", \"profiles\");\n}\n\nexport function getConfigDir(): string {\n return path.join(getPaiHome(), \"config\");\n}\n\nexport function getConfigPath(): string {\n return path.join(getConfigDir(), \"pai.json5\");\n}\n\nexport function getProfilesPath(): string {\n return path.join(getConfigDir(), \"profiles.json5\");\n}\n\nexport function getPreferencesPath(): string {\n return path.join(getConfigDir(), \"preferences.md\");\n}\n\n/** Compiled user profile — the core output of pai */\nexport function getProfilePath(): string {\n return path.join(getPaiHome(), \"profile.md\");\n}\n","import { z } from \"zod\";\n\nexport const QmdConfigSchema = z.object({\n rawCollection: z.string().default(\"raw\"),\n vaultCollection: z.string().default(\"vault\"),\n});\n\nexport const LlmConfigSchema = z.object({\n apiKeyEnv: z.string().default(\"OPENAI_API_KEY\"),\n baseUrl: z.string().optional(),\n cheapModel: z.string().default(\"gpt-4o-mini\"),\n expensiveModel: z.string().default(\"gpt-4o\"),\n});\n\nexport const ScraperConfigSchema = z.object({\n timeout: z.number().default(30_000),\n});\n\nexport const VaultConfigSchema = z.object({\n maxFileTokens: z.number().default(4000),\n warnFileTokens: z.number().default(3000),\n});\n\nexport const PaiConfigSchema = z\n .object({\n version: z.string().default(\"0.1\"),\n qmd: QmdConfigSchema.default(() => QmdConfigSchema.parse({})),\n llm: LlmConfigSchema.default(() => LlmConfigSchema.parse({})),\n scraper: ScraperConfigSchema.default(() => ScraperConfigSchema.parse({})),\n vault: VaultConfigSchema.default(() => VaultConfigSchema.parse({})),\n })\n .strict();\n\nexport type PaiConfig = z.infer<typeof PaiConfigSchema>;\n","import fs from \"node:fs/promises\";\nimport JSON5 from \"json5\";\nimport { PaiConfigSchema, type PaiConfig } from \"./schema.js\";\nimport { getConfigPath, getProfilesPath } from \"./paths.js\";\nimport type { ProfilesConfig } from \"../types.js\";\n\n/** Load and validate pai.json5 config */\nexport async function loadConfig(): Promise<PaiConfig> {\n const configPath = getConfigPath();\n try {\n const raw = await fs.readFile(configPath, \"utf-8\");\n const parsed = JSON5.parse(raw);\n return PaiConfigSchema.parse(parsed);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n // Return defaults if config file doesn't exist\n return PaiConfigSchema.parse({});\n }\n throw err;\n }\n}\n\n/** Load profiles.json5 */\nexport async function loadProfiles(): Promise<ProfilesConfig> {\n const profilesPath = getProfilesPath();\n try {\n const raw = await fs.readFile(profilesPath, \"utf-8\");\n return JSON5.parse(raw) as ProfilesConfig;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { profiles: {} };\n }\n throw err;\n }\n}\n\n/** Write config file (with JSON5 formatting) */\nexport async function saveConfig(\n filePath: string,\n content: string,\n): Promise<void> {\n await fs.writeFile(filePath, content, \"utf-8\");\n}\n","/** Generate a URL-safe slug from a title string */\nexport function generateSlug(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, \"\") // remove special chars\n .replace(/\\s+/g, \"-\") // spaces to hyphens\n .replace(/-+/g, \"-\") // collapse multiple hyphens\n .replace(/^-|-$/g, \"\") // trim leading/trailing hyphens\n .slice(0, 50); // max 50 chars\n}\n\n/** Generate a timestamped filename: YYYY-MM-DDTHH-MM-{slug}.md */\nexport function generateRawFilename(title: string): string {\n const now = new Date();\n const ts = now\n .toISOString()\n .replace(/:\\d{2}\\.\\d{3}Z$/, \"\")\n .replace(/:/g, \"-\");\n const slug = generateSlug(title) || \"untitled\";\n return `${ts}-${slug}.md`;\n}\n","import matter from \"gray-matter\";\nimport type { RawFrontmatter } from \"../types.js\";\n\n/** Parse a markdown file with frontmatter */\nexport function parseFrontmatter<T = Record<string, unknown>>(\n content: string,\n): { data: T; content: string } {\n const result = matter(content);\n return { data: result.data as T, content: result.content };\n}\n\n/** Stringify data + content into a frontmatter markdown string */\nexport function stringifyFrontmatter(\n data: Record<string, unknown>,\n content: string,\n): string {\n return matter.stringify(content, data);\n}\n\n/** Create a raw file with proper frontmatter */\nexport function createRawFile(\n frontmatter: RawFrontmatter,\n title: string,\n body: string,\n): string {\n const content = `\\n# ${title}\\n\\n${body}\\n`;\n return stringifyFrontmatter(frontmatter as unknown as Record<string, unknown>, content);\n}\n\n/** Update frontmatter fields in a raw file */\nexport function updateRawFrontmatter(\n fileContent: string,\n updates: Partial<RawFrontmatter>,\n): string {\n const { data, content } = parseFrontmatter<RawFrontmatter>(fileContent);\n const updated = { ...data, ...updates };\n return stringifyFrontmatter(updated as unknown as Record<string, unknown>, content);\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getRawDir } from \"../config/paths.js\";\nimport { generateRawFilename } from \"../utils/slug.js\";\nimport { createRawFile, parseFrontmatter } from \"../utils/frontmatter.js\";\nimport type { CollectorResult, RawFrontmatter } from \"../types.js\";\n\n/** Add plain text content to raw/local/ */\nexport async function addText(\n content: string,\n source: string = \"local\",\n): Promise<string> {\n const title = extractTitle(content);\n const filename = generateRawFilename(title);\n const dir = path.join(getRawDir(), \"local\");\n await fs.mkdir(dir, { recursive: true });\n\n const fm: RawFrontmatter = {\n source,\n timestamp: new Date().toISOString(),\n status: \"pending\",\n };\n\n const fileContent = createRawFile(fm, title, content);\n const filePath = path.join(dir, filename);\n await fs.writeFile(filePath, fileContent, \"utf-8\");\n return filePath;\n}\n\n/** Add a local file's content to raw/local/ */\nexport async function addFile(\n filePath: string,\n source: string = \"local\",\n): Promise<string> {\n const content = await fs.readFile(filePath, \"utf-8\");\n const basename = path.basename(filePath, path.extname(filePath));\n const filename = generateRawFilename(basename);\n const dir = path.join(getRawDir(), \"local\");\n await fs.mkdir(dir, { recursive: true });\n\n const fm: RawFrontmatter = {\n source,\n timestamp: new Date().toISOString(),\n status: \"pending\",\n };\n\n const fileContent = createRawFile(fm, basename, content);\n const outPath = path.join(dir, filename);\n await fs.writeFile(outPath, fileContent, \"utf-8\");\n return outPath;\n}\n\n/** Add URL-scraped content to raw/web/ */\nexport async function addUrl(\n url: string,\n title: string,\n markdown: string,\n): Promise<string> {\n const filename = generateRawFilename(title);\n const dir = path.join(getRawDir(), \"web\");\n await fs.mkdir(dir, { recursive: true });\n\n const fm: RawFrontmatter = {\n source: \"web\",\n timestamp: new Date().toISOString(),\n url,\n status: \"pending\",\n };\n\n const fileContent = createRawFile(fm, title, markdown);\n const filePath = path.join(dir, filename);\n await fs.writeFile(filePath, fileContent, \"utf-8\");\n return filePath;\n}\n\n/**\n * Add or update a connector-scanned entry to raw/connector/{name}/.\n * Uses scan_id in frontmatter for dedup:\n * - If no existing file with same scan_id → create new\n * - If existing file with same content body → skip (\"skipped\")\n * - If existing file with different content → overwrite (\"updated\")\n * Returns \"created\" | \"updated\" | \"skipped\".\n */\nexport async function addConnectorEntry(\n connectorName: string,\n entry: CollectorResult,\n): Promise<\"created\" | \"updated\" | \"skipped\"> {\n const dir = path.join(getRawDir(), \"connector\", connectorName);\n await fs.mkdir(dir, { recursive: true });\n\n const scanId = `${connectorName}/${entry.id}`;\n\n // Check for existing file with same scan_id\n const existingPath = await findByScanId(dir, scanId);\n\n const fm: RawFrontmatter = {\n source: `connector/${connectorName}`,\n timestamp: new Date().toISOString(),\n status: \"pending\",\n scan_id: scanId,\n };\n\n if (existingPath) {\n // Compare content body\n const oldRaw = await fs.readFile(existingPath, \"utf-8\");\n const { content: oldBody } = parseFrontmatter(oldRaw);\n const newBody = `\\n# ${entry.title}\\n\\n${entry.content}\\n`;\n\n if (oldBody.trim() === newBody.trim()) {\n return \"skipped\";\n }\n\n // Overwrite with updated content (preserve filename)\n const fileContent = createRawFile(fm, entry.title, entry.content);\n await fs.writeFile(existingPath, fileContent, \"utf-8\");\n return \"updated\";\n }\n\n // Create new entry\n const filename = generateRawFilename(entry.id);\n const fileContent = createRawFile(fm, entry.title, entry.content);\n const filePath = path.join(dir, filename);\n await fs.writeFile(filePath, fileContent, \"utf-8\");\n return \"created\";\n}\n\n/** Find a raw file in a directory whose frontmatter scan_id matches */\nasync function findByScanId(dir: string, scanId: string): Promise<string | null> {\n try {\n const entries = await fs.readdir(dir);\n for (const name of entries) {\n if (!name.endsWith(\".md\")) continue;\n const filePath = path.join(dir, name);\n const content = await fs.readFile(filePath, \"utf-8\");\n if (content.includes(`scan_id: ${scanId}`) || content.includes(`scan_id: '${scanId}'`)) {\n return filePath;\n }\n }\n } catch {\n // Directory doesn't exist or read error\n }\n return null;\n}\n\n/** List all pending raw files (status: pending) */\nexport async function listPending(): Promise<string[]> {\n const rawDir = getRawDir();\n const pending: string[] = [];\n\n const subdirs = [\"local\", \"web\", \"connector\"];\n for (const sub of subdirs) {\n const dir = path.join(rawDir, sub);\n try {\n const entries = await walkDir(dir);\n for (const entry of entries) {\n if (entry.endsWith(\".md\")) {\n const content = await fs.readFile(entry, \"utf-8\");\n if (content.includes(\"status: pending\")) {\n pending.push(entry);\n }\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n return pending;\n}\n\n/** List all raw files regardless of status */\nexport async function listAll(): Promise<string[]> {\n const rawDir = getRawDir();\n const files: string[] = [];\n\n const subdirs = [\"local\", \"web\", \"connector\"];\n for (const sub of subdirs) {\n const dir = path.join(rawDir, sub);\n try {\n const entries = await walkDir(dir);\n files.push(...entries.filter((e) => e.endsWith(\".md\")));\n } catch {\n // Directory might not exist\n }\n }\n return files;\n}\n\n/** Recursively walk a directory and return file paths */\nasync function walkDir(dir: string): Promise<string[]> {\n const results: string[] = [];\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...(await walkDir(fullPath)));\n } else {\n results.push(fullPath);\n }\n }\n } catch {\n // Ignore errors (dir doesn't exist, etc.)\n }\n return results;\n}\n\n/** Extract a title from content (first line or first N words) */\nfunction extractTitle(content: string): string {\n const firstLine = content.split(\"\\n\")[0]?.trim() ?? \"\";\n // Remove markdown heading syntax\n const cleaned = firstLine.replace(/^#+\\s*/, \"\");\n if (cleaned.length > 0 && cleaned.length <= 100) {\n return cleaned;\n }\n // Fall back to first 8 words\n return content.split(/\\s+/).slice(0, 8).join(\" \").slice(0, 100) || \"untitled\";\n}\n","import chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\n\nexport function log(message: string): void {\n // eslint-disable-next-line no-console\n console.log(message);\n}\n\nexport function info(message: string): void {\n // eslint-disable-next-line no-console\n console.log(chalk.blue(\"ℹ\"), message);\n}\n\nexport function success(message: string): void {\n // eslint-disable-next-line no-console\n console.log(chalk.green(\"✓\"), message);\n}\n\nexport function warn(message: string): void {\n // eslint-disable-next-line no-console\n console.warn(chalk.yellow(\"⚠\"), message);\n}\n\nexport function error(message: string): void {\n // eslint-disable-next-line no-console\n console.error(chalk.red(\"✗\"), message);\n}\n\nexport function spinner(text: string): Ora {\n return ora({ text, color: \"cyan\" }).start();\n}\n\nexport function dim(message: string): string {\n return chalk.dim(message);\n}\n\nexport function bold(message: string): string {\n return chalk.bold(message);\n}\n","import * as console from \"../utils/console.js\";\n\ninterface ScrapeResult {\n url: string;\n title: string;\n markdown: string;\n}\n\n/**\n * Scrape a URL and return title + markdown content.\n * Uses fetch + defuddle for content extraction.\n * Falls back to basic HTML extraction if defuddle is unavailable.\n */\nexport async function scrapeUrl(\n url: string,\n timeout: number = 30_000,\n): Promise<ScrapeResult> {\n // Fetch the HTML\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n\n let html: string;\n try {\n const response = await fetch(url, {\n signal: controller.signal,\n headers: {\n \"User-Agent\":\n \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\n Accept: \"text/html,application/xhtml+xml\",\n },\n });\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n html = await response.text();\n } finally {\n clearTimeout(timer);\n }\n\n // Try defuddle for content extraction\n try {\n // Dynamic import — gracefully fails if defuddle is not installed\n const defuddleMod = await import(\"defuddle/node\" as string);\n const Defuddle = defuddleMod.Defuddle ?? defuddleMod.default;\n if (Defuddle) {\n const result = new Defuddle(html, { url }).parse();\n const title = result.title || extractTitleFromHtml(html);\n const markdown = result.content\n ? htmlToSimpleMarkdown(result.content)\n : extractTextFromHtml(html);\n return { url, title, markdown };\n }\n } catch {\n console.warn(\"defuddle not available, using basic HTML extraction\");\n }\n\n // Fallback: basic HTML extraction\n const title = extractTitleFromHtml(html);\n const markdown = extractTextFromHtml(html);\n return { url, title, markdown };\n}\n\n/** Extract <title> from HTML */\nfunction extractTitleFromHtml(html: string): string {\n const match = html.match(/<title[^>]*>([^<]+)<\\/title>/i);\n return match?.[1]?.trim() ?? \"Untitled\";\n}\n\n/** Basic HTML to text extraction (fallback) */\nfunction extractTextFromHtml(html: string): string {\n return (\n html\n // Remove script and style blocks\n .replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n .replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n // Remove HTML tags\n .replace(/<[^>]+>/g, \" \")\n // Decode common entities\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/ /g, \" \")\n // Collapse whitespace\n .replace(/\\s+/g, \" \")\n .trim()\n .slice(0, 10000)\n );\n}\n\n/** Convert simple HTML to markdown */\nfunction htmlToSimpleMarkdown(html: string): string {\n return (\n html\n // Headings\n .replace(/<h1[^>]*>([\\s\\S]*?)<\\/h1>/gi, \"# $1\\n\\n\")\n .replace(/<h2[^>]*>([\\s\\S]*?)<\\/h2>/gi, \"## $1\\n\\n\")\n .replace(/<h3[^>]*>([\\s\\S]*?)<\\/h3>/gi, \"### $1\\n\\n\")\n // Paragraphs\n .replace(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi, \"$1\\n\\n\")\n // Line breaks\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n // Bold and italic\n .replace(/<strong[^>]*>([\\s\\S]*?)<\\/strong>/gi, \"**$1**\")\n .replace(/<em[^>]*>([\\s\\S]*?)<\\/em>/gi, \"*$1*\")\n // Links\n .replace(/<a[^>]*href=\"([^\"]*)\"[^>]*>([\\s\\S]*?)<\\/a>/gi, \"[$2]($1)\")\n // Lists\n .replace(/<li[^>]*>([\\s\\S]*?)<\\/li>/gi, \"- $1\\n\")\n // Code\n .replace(/<code[^>]*>([\\s\\S]*?)<\\/code>/gi, \"`$1`\")\n .replace(/<pre[^>]*>([\\s\\S]*?)<\\/pre>/gi, \"```\\n$1\\n```\\n\")\n // Remove remaining tags\n .replace(/<[^>]+>/g, \"\")\n // Decode entities\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/ /g, \" \")\n // Clean up whitespace\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim()\n );\n}\n","import { execFile } from \"node:child_process\";\n\n/** Execute QMD CLI command and return stdout */\nexport async function execQmd(args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n execFile(\"qmd\", args, { encoding: \"utf-8\", timeout: 60_000 }, (err, stdout, stderr) => {\n if (err) {\n const msg = stderr?.trim() || err.message;\n reject(new Error(`qmd ${args[0]} failed: ${msg}`));\n } else {\n resolve(stdout);\n }\n });\n });\n}\n\n/** Check if QMD is available on PATH */\nexport async function isQmdAvailable(): Promise<boolean> {\n try {\n await execQmd([\"status\"]);\n return true;\n } catch {\n return false;\n }\n}\n","import { execFile } from \"node:child_process\";\nimport path from \"node:path\";\nimport { execQmd, isQmdAvailable } from \"../utils/process.js\";\nimport { getVaultDir, getRawDir } from \"../config/paths.js\";\nimport type { SearchResult } from \"../types.js\";\n\nexport type SearchMode = \"query\" | \"search\" | \"vsearch\";\n\n/**\n * Hybrid search via QMD.\n * - \"query\" (default): BM25 + vector + expansion + reranking (~5s, best quality)\n * - \"search\": BM25 keyword + rg grep fallback (~50ms, fast)\n * - \"vsearch\": Vector similarity only (~2s, good for semantic)\n *\n * For \"search\" (fast) mode, QMD BM25 doesn't support CJK tokenization,\n * so we supplement with ripgrep to cover Chinese/Japanese/Korean.\n */\nexport async function search(\n query: string,\n collection: string = \"vault\",\n n: number = 5,\n mode: SearchMode = \"query\",\n): Promise<SearchResult[]> {\n if (!(await isQmdAvailable())) {\n throw new Error(\n \"QMD is not installed. Install with: npm install -g https://github.com/tobi/qmd\",\n );\n }\n\n const stdout = await execQmd([\n mode,\n query,\n \"--collection\",\n collection,\n \"--json\",\n \"-n\",\n String(n),\n ]);\n\n let results = parseSearchResults(stdout);\n\n // BM25 can't tokenize CJK — supplement with ripgrep for fast mode\n if (mode === \"search\" && results.length === 0 && hasCjk(query)) {\n results = await grepFallback(query, collection, n);\n }\n\n return results;\n}\n\n/** Parse QMD JSON search output into SearchResult[] */\nfunction parseSearchResults(stdout: string): SearchResult[] {\n try {\n const parsed = JSON.parse(stdout);\n if (!Array.isArray(parsed)) return [];\n return parsed.map((item: Record<string, unknown>) => ({\n file: (item.file as string) ?? \"\",\n title: (item.title as string) ?? undefined,\n snippet:\n (item.snippet as string) ??\n (item.content as string)?.slice(0, 200) ??\n \"\",\n score: item.score as number | undefined,\n }));\n } catch {\n return [];\n }\n}\n\n/** Check if a string contains CJK characters */\nfunction hasCjk(text: string): boolean {\n return /[\\u4e00-\\u9fff\\u3040-\\u30ff\\uac00-\\ud7af]/.test(text);\n}\n\n/**\n * grep fallback for CJK text that BM25 can't tokenize.\n * Runs: grep -rl \"query\" <dir> --include=\"*.md\"\n * Then reads matched files and extracts context.\n */\nasync function grepFallback(\n query: string,\n collection: string,\n n: number,\n): Promise<SearchResult[]> {\n const baseDir = collection === \"raw\" ? getRawDir() : getVaultDir();\n\n // grep -rl: recursive, list filenames only\n const stdout = await new Promise<string>((resolve) => {\n execFile(\n \"grep\",\n [\"-rl\", \"--include=*.md\", query, baseDir],\n { encoding: \"utf-8\", timeout: 5_000 },\n (_err, out) => {\n // grep exits 1 when no matches — that's fine\n resolve(out ?? \"\");\n },\n );\n });\n\n if (!stdout.trim()) return [];\n\n const files = stdout.trim().split(\"\\n\").slice(0, n);\n const results: SearchResult[] = [];\n\n for (const filePath of files) {\n if (!filePath) continue;\n // Get context line with grep -m1 (first match with surrounding text)\n const context = await new Promise<string>((resolve) => {\n execFile(\n \"grep\",\n [\"-m1\", \"-C1\", query, filePath],\n { encoding: \"utf-8\", timeout: 2_000 },\n (_err, out) => resolve(out ?? \"\"),\n );\n });\n\n const snippet = context.replace(/\\n/g, \" \").trim().slice(0, 160);\n const relative = path.relative(baseDir, filePath);\n // Extract title: first H1 line via grep\n const titleLine = await new Promise<string>((resolve) => {\n execFile(\n \"grep\",\n [\"-m1\", \"^# \", filePath],\n { encoding: \"utf-8\", timeout: 1_000 },\n (_err, out) => resolve(out?.replace(/^#\\s+/, \"\").trim() ?? \"\"),\n );\n });\n\n results.push({\n file: `qmd://${collection}/${relative}`,\n title: titleLine || path.basename(filePath, \".md\"),\n snippet,\n score: 1.0,\n });\n }\n\n return results;\n}\n\n/** Update QMD index: runs `qmd update` + `qmd embed` */\nexport async function updateIndex(): Promise<void> {\n await execQmd([\"update\"]);\n await execQmd([\"embed\"]);\n}\n","import fs from \"node:fs/promises\";\nimport OpenAI from \"openai\";\nimport { loadConfig } from \"../config/index.js\";\nimport { getPreferencesPath } from \"../config/paths.js\";\nimport type { PaiConfig } from \"../config/index.js\";\n\nlet cachedConfig: PaiConfig | null = null;\nlet cachedPreferences: string | null = null;\n\nasync function getConfig(): Promise<PaiConfig> {\n if (!cachedConfig) {\n cachedConfig = await loadConfig();\n }\n return cachedConfig;\n}\n\n/** Load user preferences.md (cached per process) */\nasync function getPreferences(): Promise<string> {\n if (cachedPreferences !== null) return cachedPreferences;\n try {\n cachedPreferences = await fs.readFile(getPreferencesPath(), \"utf-8\");\n } catch {\n cachedPreferences = \"\";\n }\n return cachedPreferences;\n}\n\n/** Create an OpenAI-compatible client from config */\nexport async function createLlmClient(): Promise<OpenAI> {\n const config = await getConfig();\n const apiKey = process.env[config.llm.apiKeyEnv];\n if (!apiKey) {\n throw new Error(\n `Missing API key: set ${config.llm.apiKeyEnv} environment variable`,\n );\n }\n return new OpenAI({\n apiKey,\n baseURL: config.llm.baseUrl || undefined,\n });\n}\n\n/**\n * Single LLM call with system + user messages.\n * Automatically prepends user preferences to the system prompt.\n * Retries once on failure.\n */\nexport async function llmCall(\n prompt: string,\n system: string,\n model?: string,\n): Promise<string> {\n const config = await getConfig();\n const client = await createLlmClient();\n const modelName = model ?? config.llm.cheapModel;\n\n // Inject user preferences into system prompt\n const prefs = await getPreferences();\n const fullSystem = prefs\n ? `${system}\\n\\n---USER PREFERENCES (always respect these)---\\n${prefs}`\n : system;\n\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n const response = await client.chat.completions.create({\n model: modelName,\n messages: [\n { role: \"system\", content: fullSystem },\n { role: \"user\", content: prompt },\n ],\n temperature: 0.3,\n });\n return response.choices[0]?.message?.content ?? \"\";\n } catch (err) {\n if (attempt === 0) {\n // Retry once\n await new Promise((r) => setTimeout(r, 1000));\n continue;\n }\n throw err;\n }\n }\n return \"\"; // unreachable\n}\n\n/** Reset cached config and preferences (for testing) */\nexport function resetConfigCache(): void {\n cachedConfig = null;\n cachedPreferences = null;\n}\n","/** Build system prompt for the triage step */\nexport function triageSystemPrompt(): string {\n return `You are a personal knowledge management assistant.\nYour job is to evaluate raw input and extract ALL distinct pieces of personal context worth preserving.\n\nCRITICAL: One raw input often contains MULTIPLE types of data. You MUST split them into separate entries routed to different vault files.\n\nTwo kinds of valuable content:\n\n1) EXPERIENTIAL: concrete lessons, preferences, tips, or experiences.\n2) SYSTEM CONTEXT: data from system/connector scans — identity, environment, habits, preferences.\n\nRouting targets:\n- vault/context/identity.md — name, email, role, languages, locale, timezone\n- vault/context/active-projects.md — repos, cloud infra, SSH hosts\n- vault/preferences/tools.md — tech stack, IDEs, runtimes, package managers\n- vault/preferences/workflow.md — shell habits, git config, aliases, dev workflow\n- vault/life/interests.md — interests, domains of focus, bookmarks, browsing patterns\n- vault/life/lifestyle.md — calendar, apps, music, media, daily routines\n- vault/coding/*.md — coding lessons (use existing or suggest new)\n- vault/work/*.md — work-related lessons and context\n\nDo NOT mark as valuable: generic news, ads, or content with no personal signal.\n\nRespond ONLY with valid JSON (no markdown fences):\n{\n \"valuable\": true/false,\n \"entries\": [\n { \"targetFile\": \"vault/context/identity.md\", \"extract\": \"name, email, role, languages\" },\n { \"targetFile\": \"vault/preferences/tools.md\", \"extract\": \"IDE, runtimes, package managers\" }\n ],\n \"reason\": \"brief explanation\"\n}\n\nRules:\n- entries array can have 1-6 items — split aggressively by topic\n- Each entry.extract is a SHORT directive telling the distill step WHAT to pull for that target\n- If not valuable, entries should be empty []\n- Use existing vault files from the list when available`;\n}\n\n/** Build user prompt for triage */\nexport function triageUserPrompt(\n rawContent: string,\n vaultFiles: string[],\n): string {\n const fileList =\n vaultFiles.length > 0\n ? `\\nExisting vault files:\\n${vaultFiles.map((f) => `- ${f}`).join(\"\\n\")}`\n : \"\\nNo existing vault files yet.\";\n\n return `Evaluate this raw input and decide if it's worth distilling:\n\n---RAW CONTENT---\n${rawContent}\n---END RAW CONTENT---\n${fileList}\n\nRoute to context/, preferences/, life/, coding/, or work/ as appropriate. Respond with JSON only.`;\n}\n","import { llmCall } from \"../llm/index.js\";\nimport { triageSystemPrompt, triageUserPrompt } from \"../prompts/triage.js\";\nimport type { TriageResult, TriageEntry } from \"../types.js\";\n\n/** Run triage on a raw file to determine if it's worth distilling */\nexport async function triageRawFile(\n rawContent: string,\n vaultFiles: string[],\n): Promise<TriageResult> {\n const system = triageSystemPrompt();\n const prompt = triageUserPrompt(rawContent, vaultFiles);\n\n const response = await llmCall(prompt, system);\n\n try {\n // Strip markdown code fences if present\n const cleaned = response.replace(/```json?\\n?/g, \"\").replace(/```/g, \"\").trim();\n const raw = JSON.parse(cleaned) as Record<string, unknown>;\n\n // Parse entries array (new 1:N format)\n let entries: TriageEntry[] = [];\n if (Array.isArray(raw.entries)) {\n entries = (raw.entries as Record<string, unknown>[])\n .filter((e) => typeof e.targetFile === \"string\" && typeof e.extract === \"string\")\n .map((e) => ({\n targetFile: e.targetFile as string,\n extract: e.extract as string,\n }));\n }\n // Backward compat: if LLM returns old { targetFile } format, convert\n if (entries.length === 0 && typeof raw.targetFile === \"string\" && raw.targetFile) {\n entries = [{ targetFile: raw.targetFile, extract: \"all relevant content\" }];\n }\n\n return {\n valuable: Boolean(raw.valuable),\n entries,\n reason: (raw.reason as string) ?? \"\",\n };\n } catch {\n // If LLM returns invalid JSON, treat as not valuable\n return {\n valuable: false,\n entries: [],\n reason: `Failed to parse triage response: ${response.slice(0, 100)}`,\n };\n }\n}\n","/** Build system prompt for the distill & merge step */\nexport function distillSystemPrompt(): string {\n return `You are a personal knowledge distillation assistant.\nYour job is to extract and merge valuable personal context from raw input into a vault document.\n\nTwo modes:\n\nA) EXPERIENTIAL content (lessons, tips, experiences):\n- Extract ONLY conclusions and actionable knowledge — never copy raw text verbatim\n- If a similar experience already exists, increment its verification count\n- Each bullet: (source, date | ref: raw/path)\n\nB) SYSTEM CONTEXT content (identity, environment, habits, bookmarks, etc.):\n- Do NOT dump raw lists verbatim (e.g. every package path or every bookmark folder)\n- Synthesize into a compact, HIGH-DENSITY profile:\n - Identity: name, role, languages, locale, timezone — one line each\n - Tools: summarize tech stack concisely (e.g. \"Full-stack: Node/Python/Go/Rust, IDEs: Cursor+Android Studio\")\n - Workflow: summarize habits (e.g. \"Heavy git user (4762 commands), frequent claude CLI, command-line focused\")\n - Life: summarize calendar themes, main apps, browsing focus — with concrete data\n - Interests: summarize bookmark categories AND top domains as interest tags (e.g. \"AI/LLM, GIS, iOS, Python, Web3\")\n - Projects: summarize active areas with names (e.g. \"PINAI (PIN-APP-IOS, PIN-AGENT-WEB), consulting, agent-market\")\n\nCRITICAL RULES:\n- NEVER write \"未提供\", \"未指定\", \"Not specified\", \"Unknown\" — if data is not in the raw input, simply OMIT that field\n- Only include sections and fields that have ACTUAL data from the raw input\n- Do NOT create empty placeholder sections. If an H2 section would be empty, skip it entirely\n- Each vault file handles ONE topic — only include relevant data from the raw input\n- Keep information density HIGH: pack maximum facts per line\n- Maintain H2 (##) section structure\n- Keep each H2 section between 200-800 tokens\n- Preserve all existing vault content — only add or update, never remove\n- Output the COMPLETE updated vault file content (not just the changes)\n- End each new bullet with source ref: (source, date | ref: raw/path/to/file.md)`;\n}\n\n/** Build user prompt for distill & merge */\nexport function distillUserPrompt(\n rawContent: string,\n rawFilePath: string,\n existingVaultContent: string | null,\n extractDirective?: string,\n): string {\n const vaultSection = existingVaultContent\n ? `---EXISTING VAULT DOCUMENT---\\n${existingVaultContent}\\n---END VAULT DOCUMENT---`\n : \"This is a NEW vault document. Create it with appropriate H1 title and H2 sections.\";\n\n const extractHint = extractDirective\n ? `\\nFOCUS: Only extract data related to: ${extractDirective}. Ignore unrelated content in the raw input.`\n : \"\";\n\n return `Distill the following raw input and merge into the vault document:\n\n---RAW CONTENT (from: ${rawFilePath})---\n${rawContent}\n---END RAW CONTENT---\n\n${vaultSection}\n${extractHint}\nOutput the COMPLETE updated vault file content.\nRules: synthesize into high-density profile. NEVER write \"未提供\"/\"Not specified\"/\"Unknown\" — omit fields with no data instead. Only include sections with actual data.`;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { llmCall } from \"../llm/index.js\";\nimport { distillSystemPrompt, distillUserPrompt } from \"../prompts/distill.js\";\nimport { getVaultDir } from \"../config/paths.js\";\n\n/**\n * Distill raw content and merge into a vault file.\n * Creates the vault file if it doesn't exist.\n * @param extractDirective - Optional hint telling LLM what to extract from raw content for this target\n */\nexport async function distillAndMerge(\n rawContent: string,\n rawFilePath: string,\n targetFile: string,\n extractDirective?: string,\n): Promise<string> {\n const vaultDir = getVaultDir();\n // targetFile is like \"vault/coding/python-fastapi.md\"\n // Strip leading \"vault/\" if present\n const relativePath = targetFile.replace(/^vault\\//, \"\");\n const fullPath = path.join(vaultDir, relativePath);\n\n // Read existing vault content (if any)\n let existingContent: string | null = null;\n try {\n existingContent = await fs.readFile(fullPath, \"utf-8\");\n } catch {\n // File doesn't exist — will create new\n }\n\n const system = distillSystemPrompt();\n const prompt = distillUserPrompt(rawContent, rawFilePath, existingContent, extractDirective);\n\n const updatedContent = await llmCall(prompt, system);\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(fullPath), { recursive: true });\n\n // Write the updated vault file\n await fs.writeFile(fullPath, updatedContent.trim() + \"\\n\", \"utf-8\");\n\n return fullPath;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { listPending } from \"../raw/index.js\";\nimport { triageRawFile } from \"./triage.js\";\nimport { distillAndMerge } from \"./merge.js\";\nimport { getVaultDir } from \"../config/paths.js\";\nimport { parseFrontmatter, updateRawFrontmatter } from \"../utils/frontmatter.js\";\nimport * as console from \"../utils/console.js\";\nimport type { RawFrontmatter } from \"../types.js\";\n\ninterface DistillResult {\n processed: number;\n valuable: number;\n discarded: number;\n errors: string[];\n}\n\n/** List all existing vault files (relative paths like \"coding/python.md\") */\nasync function listVaultFiles(): Promise<string[]> {\n const vaultDir = getVaultDir();\n const files: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.name.endsWith(\".md\")) {\n files.push(path.relative(vaultDir, fullPath));\n }\n }\n } catch {\n // Directory might not exist\n }\n }\n\n await walk(vaultDir);\n return files;\n}\n\n/** Run the full distill pipeline on all pending raw files */\nexport async function distillPipeline(\n options: { dryRun?: boolean; singleFile?: string } = {},\n): Promise<DistillResult> {\n const result: DistillResult = {\n processed: 0,\n valuable: 0,\n discarded: 0,\n errors: [],\n };\n\n // Get pending files\n let pendingFiles: string[];\n if (options.singleFile) {\n pendingFiles = [options.singleFile];\n } else {\n pendingFiles = await listPending();\n }\n\n if (pendingFiles.length === 0) {\n console.info(\"No pending raw files to process.\");\n return result;\n }\n\n console.info(`Found ${pendingFiles.length} pending file(s) to process.`);\n\n // Get existing vault files for triage context\n const vaultFiles = await listVaultFiles();\n const vaultFilesList = vaultFiles.map((f) => `vault/${f}`);\n\n for (const filePath of pendingFiles) {\n const spin = console.spinner(`Processing ${path.basename(filePath)}...`);\n\n try {\n // Read raw file\n const rawFileContent = await fs.readFile(filePath, \"utf-8\");\n const { content } = parseFrontmatter<RawFrontmatter>(rawFileContent);\n\n // Step 1: Triage — returns 1:N entries\n const triage = await triageRawFile(content, vaultFilesList);\n result.processed++;\n\n if (!triage.valuable || triage.entries.length === 0) {\n spin.succeed(`Discarded: ${path.basename(filePath)} — ${triage.reason}`);\n result.discarded++;\n\n if (!options.dryRun) {\n const updated = updateRawFrontmatter(rawFileContent, {\n status: \"discarded\",\n });\n await fs.writeFile(filePath, updated, \"utf-8\");\n }\n continue;\n }\n\n // Step 2: Distill & Merge — one call per entry\n const targets = triage.entries.map((e) => e.targetFile);\n\n if (options.dryRun) {\n spin.succeed(\n `[DRY RUN] Would distill ${path.basename(filePath)} → ${targets.join(\", \")}`,\n );\n result.valuable++;\n continue;\n }\n\n const mergedPaths: string[] = [];\n for (const entry of triage.entries) {\n const targetFile = entry.targetFile || \"vault/context/misc.md\";\n const vaultPath = await distillAndMerge(content, filePath, targetFile, entry.extract);\n mergedPaths.push(vaultPath);\n\n // Track newly created vault files for subsequent triage\n if (!vaultFilesList.includes(targetFile)) {\n vaultFilesList.push(targetFile);\n }\n }\n\n result.valuable++;\n\n // Update raw file frontmatter — record all targets\n const distilledTo = mergedPaths\n .map((p) => path.relative(path.dirname(filePath).replace(/\\/raw\\/.*/, \"/raw\"), p))\n .join(\", \");\n const updated = updateRawFrontmatter(rawFileContent, {\n status: \"processed\",\n distilled_to: distilledTo,\n });\n await fs.writeFile(filePath, updated, \"utf-8\");\n\n spin.succeed(\n `Distilled ${path.basename(filePath)} → ${targets.join(\", \")} (${triage.entries.length} entries)`,\n );\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n spin.fail(`Error processing ${path.basename(filePath)}: ${msg}`);\n result.errors.push(`${filePath}: ${msg}`);\n }\n }\n\n return result;\n}\n","/** Build system prompt for SKILL.md generation */\nexport function generateSystemPrompt(): string {\n return `You are generating a concise SKILL.md personal context file for an AI agent.\nThis file helps the agent understand the user's background, preferences, and experiences.\n\nRules:\n- Be extremely concise — every line must carry HIGH information density\n- Pack maximum concrete facts per bullet (names, numbers, specific tools)\n- Prioritize experiences with higher verification counts when present\n- Use bullet points for each piece of context\n- Follow the exact section structure provided\n- Do NOT include generic advice — only user-specific context\n- NEVER write filler like \"未提供\", \"Not specified\" or vague statements\n- Write in the same language as the source content\n\nWhen vault content includes identity/tools/workflow/interests data:\n- \"Who I Am\": real name, email, languages with specifics, locale, timezone, tech identity\n- \"Preferences\": specific tools by name, workflow patterns with data (e.g. \"git: 4762 commands\"), communication style\n- \"Hard-won Lessons\": only include if genuine lessons exist; otherwise omit or make it 1-2 lines\n- \"Current Work\": specific project names, focus areas, active repos — be concrete`;\n}\n\n/** Build user prompt for SKILL.md generation */\nexport function generateUserPrompt(\n profileName: string,\n vaultContents: string,\n maxLines: number,\n): string {\n return `Generate a SKILL.md profile called \"${profileName}\" from the following vault content.\nMaximum ${maxLines} lines total.\n\nRequired sections:\n# Personal Context — ${profileName}\n\n## Who I Am\n## Preferences\n## Hard-won Lessons\n## Current Work\n\n---VAULT CONTENT---\n${vaultContents}\n---END VAULT CONTENT---\n\nGenerate the SKILL.md now. Stay within ${maxLines} lines.\nIMPORTANT: include ALL concrete facts from the vault — names, numbers, tools, project names, bookmark categories, browsing domains. Do not summarize away specific data. Never write filler or \"未提供\".`;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { minimatch } from \"minimatch\";\nimport { loadProfiles } from \"../config/io.js\";\nimport { getVaultDir, getSkillsDir } from \"../config/paths.js\";\nimport { llmCall } from \"../llm/index.js\";\nimport {\n generateSystemPrompt,\n generateUserPrompt,\n} from \"../prompts/generate.js\";\nimport * as console from \"../utils/console.js\";\n\n/** Recursively list all markdown files in a directory */\nasync function walkMd(dir: string): Promise<string[]> {\n const results: string[] = [];\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...(await walkMd(full)));\n } else if (entry.name.endsWith(\".md\")) {\n results.push(full);\n }\n }\n } catch {\n // Ignore errors\n }\n return results;\n}\n\n/** Collect vault file contents matching scope patterns */\nasync function collectVaultContent(scope: string[]): Promise<string> {\n const vaultDir = getVaultDir();\n const allFiles = await walkMd(vaultDir);\n const matched: string[] = [];\n\n for (const file of allFiles) {\n const relative = \"vault/\" + path.relative(vaultDir, file);\n // Check if this file matches any scope pattern\n for (const pattern of scope) {\n if (minimatch(relative, pattern)) {\n matched.push(file);\n break;\n }\n }\n }\n\n // Read and concatenate all matched files\n const contents: string[] = [];\n for (const file of matched) {\n try {\n const content = await fs.readFile(file, \"utf-8\");\n contents.push(`--- ${path.relative(vaultDir, file)} ---\\n${content}`);\n } catch {\n // Skip unreadable files\n }\n }\n\n return contents.join(\"\\n\\n\");\n}\n\n/** Generate a single SKILL.md profile */\nexport async function generateProfile(profileName: string): Promise<string> {\n const profiles = await loadProfiles();\n const profileDef = profiles.profiles[profileName];\n\n if (!profileDef) {\n throw new Error(`Profile \"${profileName}\" not found in profiles.json5`);\n }\n\n const vaultContent = await collectVaultContent(profileDef.scope);\n\n if (!vaultContent.trim()) {\n throw new Error(\n `No vault content found for profile \"${profileName}\". Run 'pai distill' first.`,\n );\n }\n\n const system = generateSystemPrompt();\n const prompt = generateUserPrompt(\n profileName,\n vaultContent,\n profileDef.maxLines,\n );\n\n const skillMd = await llmCall(prompt, system);\n\n // Write to skills/profiles/\n const skillsDir = getSkillsDir();\n await fs.mkdir(skillsDir, { recursive: true });\n const outPath = path.join(skillsDir, `${profileName}.md`);\n await fs.writeFile(outPath, skillMd.trim() + \"\\n\", \"utf-8\");\n\n return outPath;\n}\n\n/** Generate all profiles defined in profiles.json5 */\nexport async function generateAll(): Promise<string[]> {\n const profiles = await loadProfiles();\n const profileNames = Object.keys(profiles.profiles);\n\n if (profileNames.length === 0) {\n console.warn(\"No profiles defined in profiles.json5\");\n return [];\n }\n\n const results: string[] = [];\n for (const name of profileNames) {\n const spin = console.spinner(`Generating profile: ${name}...`);\n try {\n const outPath = await generateProfile(name);\n spin.succeed(`Generated ${name} → ${outPath}`);\n results.push(outPath);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n spin.fail(`Failed to generate ${name}: ${msg}`);\n }\n }\n\n return results;\n}\n","/**\n * Profile compiler — transforms raw CollectorResult[] into a structured profile.md.\n * Zero LLM dependency: pure template + data formatting.\n */\n\nimport type { CollectorResult } from \"../types.js\";\n\n/** Mapping from collector IDs to profile sections */\ninterface SectionDef {\n heading: string;\n /** Collector IDs that feed this section (in order) */\n collectors: string[];\n}\n\nconst PROFILE_SECTIONS: SectionDef[] = [\n {\n heading: \"Identity\",\n collectors: [\"identity-profile\", \"github-profile\"],\n },\n {\n heading: \"Environment & Tools\",\n collectors: [\"dev-environment\", \"dev-preferences\"],\n },\n {\n heading: \"Work Style & Habits\",\n collectors: [\"shell-habits\", \"coding-rules\"],\n },\n {\n heading: \"Active Projects & Recent Focus\",\n collectors: [\"active-projects\", \"recent-focus\"],\n },\n {\n heading: \"Digital Footprint\",\n collectors: [\"browser-bookmarks\", \"browser-domains\", \"productivity-setup\"],\n },\n {\n heading: \"Registry & Cloud Accounts\",\n collectors: [\"social-profiles\"],\n },\n {\n heading: \"Context\",\n collectors: [\"calendar-context\", \"file-organization\"],\n },\n];\n\n/**\n * Compile CollectorResult[] into a structured profile markdown string.\n * No LLM involved — pure formatting.\n */\nexport function compileProfile(results: CollectorResult[]): string {\n const index = new Map<string, CollectorResult>();\n for (const r of results) {\n index.set(r.id, r);\n }\n\n const sections: string[] = [\"# Personal Profile\", \"\"];\n\n for (const section of PROFILE_SECTIONS) {\n const parts: string[] = [];\n\n for (const cid of section.collectors) {\n const result = index.get(cid);\n if (result?.content?.trim()) {\n parts.push(result.content.trim());\n }\n }\n\n if (parts.length === 0) continue;\n\n sections.push(`## ${section.heading}`, \"\");\n sections.push(parts.join(\"\\n\\n\"));\n sections.push(\"\");\n }\n\n // Append any collectors not covered by the predefined sections\n const coveredIds = new Set(PROFILE_SECTIONS.flatMap((s) => s.collectors));\n const extras: string[] = [];\n\n for (const r of results) {\n if (!coveredIds.has(r.id) && r.content?.trim()) {\n extras.push(`## ${r.title}`, \"\", r.content.trim(), \"\");\n }\n }\n\n if (extras.length > 0) {\n sections.push(...extras);\n }\n\n // Footer\n sections.push(\"---\");\n sections.push(`Last updated: ${new Date().toISOString()}`);\n sections.push(\"\");\n\n return sections.join(\"\\n\");\n}\n\n/**\n * Generate a compact profile summary suitable for agent injection.\n * Strips verbose sub-sections and keeps high-signal data only.\n */\nexport function compileCompactProfile(results: CollectorResult[]): string {\n const index = new Map<string, CollectorResult>();\n for (const r of results) {\n index.set(r.id, r);\n }\n\n const parts: string[] = [\"# Personal Profile (Compact)\", \"\"];\n\n // Identity — always include in full\n const identity = index.get(\"identity-profile\");\n if (identity?.content?.trim()) {\n parts.push(identity.content.trim(), \"\");\n }\n\n // Dev environment — keep runtime versions and package managers\n const devEnv = index.get(\"dev-environment\");\n if (devEnv?.content?.trim()) {\n parts.push(devEnv.content.trim(), \"\");\n }\n\n // Active projects — keep repo list\n const projects = index.get(\"active-projects\");\n if (projects?.content?.trim()) {\n parts.push(projects.content.trim(), \"\");\n }\n\n // Shell habits — keep top commands only\n const habits = index.get(\"shell-habits\");\n if (habits?.content?.trim()) {\n // Take only the first 15 lines (header + top commands)\n const lines = habits.content.trim().split(\"\\n\");\n parts.push(lines.slice(0, 17).join(\"\\n\"), \"\");\n }\n\n parts.push(\"---\");\n parts.push(`Last updated: ${new Date().toISOString()}`);\n parts.push(\"\");\n\n return parts.join(\"\\n\");\n}\n","/**\n * Security sanitization utilities for connector-scanned content.\n * Strips secrets, credentials, and sensitive paths before writing to raw layer.\n */\n\n/** Pattern matching shell export lines containing secrets */\nconst SECRET_EXPORT_RE =\n /^(\\s*export\\s+)\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|PASSWD)\\w*\\s*=.*/i;\n\n/** Pattern matching lines that look like inline secret assignments (no export) */\nconst SECRET_ASSIGN_RE =\n /^\\s*\\w*(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|PASSWD)\\w*\\s*=\\s*[\"']?\\S+/i;\n\n/**\n * Strip secret export/assignment lines from shell config content.\n * Preserves comments and non-secret lines intact.\n */\nexport function stripSecretExports(content: string): string {\n return content\n .split(\"\\n\")\n .map((line) => {\n if (SECRET_EXPORT_RE.test(line) || SECRET_ASSIGN_RE.test(line)) {\n return \"# [REDACTED by pai — secret removed]\";\n }\n return line;\n })\n .join(\"\\n\");\n}\n\n/**\n * Strip [credential] sections from git config content.\n * Removes the section header and all lines until the next section.\n */\nexport function stripGitCredentials(content: string): string {\n const lines = content.split(\"\\n\");\n const result: string[] = [];\n let inCredentialSection = false;\n\n for (const line of lines) {\n // Detect section headers like [credential] or [credential \"...\"]\n if (/^\\s*\\[credential/i.test(line)) {\n inCredentialSection = true;\n result.push(\"# [REDACTED by pai — credential section removed]\");\n continue;\n }\n // New section starts → exit credential section\n if (inCredentialSection && /^\\s*\\[/.test(line)) {\n inCredentialSection = false;\n }\n if (!inCredentialSection) {\n result.push(line);\n }\n }\n\n return result.join(\"\\n\");\n}\n\n/**\n * Strip IdentityFile lines from SSH config.\n * Keeps Host, HostName, User, Port — removes key paths.\n */\nexport function stripSshIdentityFiles(content: string): string {\n return content\n .split(\"\\n\")\n .map((line) => {\n if (/^\\s*IdentityFile\\s/i.test(line)) {\n return \" # [REDACTED by pai — IdentityFile removed]\";\n }\n return line;\n })\n .join(\"\\n\");\n}\n","/**\n * Mac system context collectors.\n * Each collector gathers a specific category of personalized context\n * from the local macOS environment and returns a CollectorResult.\n */\n\nimport { execFile } from \"node:child_process\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport type { CollectorResult } from \"../../types.js\";\nimport {\n stripSecretExports,\n stripGitCredentials,\n stripSshIdentityFiles,\n} from \"../sanitize.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Run a shell command and return stdout (empty string on failure) */\nasync function exec(cmd: string, args: string[]): Promise<string> {\n return new Promise((resolve) => {\n execFile(cmd, args, { encoding: \"utf-8\", timeout: 15_000 }, (err, stdout) => {\n resolve(err ? \"\" : stdout.trim());\n });\n });\n}\n\n/** Read a file, return empty string on failure */\nasync function readSafe(filePath: string): Promise<string> {\n try {\n return await fs.readFile(filePath, \"utf-8\");\n } catch {\n return \"\";\n }\n}\n\n/** Check if path exists */\nasync function exists(p: string): Promise<boolean> {\n try {\n await fs.access(p);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Run osascript and return output */\nasync function osascript(script: string): Promise<string> {\n return exec(\"osascript\", [\"-e\", script]);\n}\n\n/** Run defaults read and return output */\nasync function defaultsRead(...args: string[]): Promise<string> {\n return exec(\"defaults\", [\"read\", ...args]);\n}\n\nconst HOME = os.homedir();\n\n// ---------------------------------------------------------------------------\n// 1. Identity Profile\n// ---------------------------------------------------------------------------\n\nexport async function collectIdentityProfile(): Promise<CollectorResult> {\n const lines: string[] = [\"## User Identity\"];\n\n // macOS username\n const username = await exec(\"id\", [\"-un\"]);\n if (username) lines.push(`- macOS Username: ${username}`);\n\n // Real name from directory service\n const realName = await exec(\"dscl\", [\".\", \"-read\", `/Users/${username}`, \"RealName\"]);\n const nameMatch = realName.split(\"\\n\").find((l) => l.trim() && !l.includes(\"RealName\"));\n if (nameMatch?.trim()) lines.push(`- Real Name (local): ${nameMatch.trim()}`);\n\n // Apple ID / iCloud\n const mobileMe = await defaultsRead(\"MobileMeAccounts\");\n const appleIdMatch = mobileMe.match(/AccountID\\s*=\\s*\"?([^\";\\n]+)/);\n const displayNameMatch = mobileMe.match(/DisplayName\\s*=\\s*\"?([^\";\\n]+)/);\n if (displayNameMatch) lines.push(`- Apple ID Display Name: ${displayNameMatch[1].trim()}`);\n if (appleIdMatch) lines.push(`- Apple ID: ${appleIdMatch[1].trim()}`);\n\n // Computer name\n const computerName = await exec(\"scutil\", [\"--get\", \"ComputerName\"]);\n if (computerName) lines.push(`- Computer Name: ${computerName}`);\n\n // Git identity\n const gitName = await exec(\"git\", [\"config\", \"--global\", \"user.name\"]);\n const gitEmail = await exec(\"git\", [\"config\", \"--global\", \"user.email\"]);\n if (gitName) lines.push(`- Git Name: ${gitName}`);\n if (gitEmail) lines.push(`- Git Email: ${gitEmail}`);\n\n // Locale & Language\n lines.push(\"\", \"## Locale & Language\");\n const langs = await defaultsRead(\"NSGlobalDomain\", \"AppleLanguages\");\n const langList = langs.match(/\"([^\"]+)\"/g)?.map((s) => s.replace(/\"/g, \"\")) ?? [];\n if (langList.length > 0) lines.push(`- Languages: ${langList.join(\", \")}`);\n\n const locale = await defaultsRead(\"NSGlobalDomain\", \"AppleLocale\");\n if (locale) lines.push(`- Locale: ${locale}`);\n\n // Input methods\n const inputSources = await defaultsRead(\"com.apple.HIToolbox\", \"AppleSelectedInputSources\");\n const inputMethods = inputSources.match(/\"Input Mode\"\\s*=\\s*\"([^\"]+)\"/g) ?? [];\n const bundleIds = inputSources.match(/\"Bundle ID\"\\s*=\\s*\"([^\"]+)\"/g) ?? [];\n const inputs = [...inputMethods, ...bundleIds]\n .map((s) => s.replace(/.*\"([^\"]+)\"$/, \"$1\"))\n .filter((s) => !s.includes(\"PressAndHold\"));\n if (inputs.length > 0) lines.push(`- Input Methods: ${inputs.join(\", \")}`);\n\n // System appearance\n const appearance = await defaultsRead(\"-g\", \"AppleInterfaceStyle\");\n lines.push(`- System Appearance: ${appearance || \"Light\"}`);\n\n // Timezone\n const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n lines.push(`- Timezone: ${tz}`);\n\n return {\n id: \"identity-profile\",\n title: \"Mac User Identity Profile\",\n content: lines.join(\"\\n\"),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Calendar Context\n// ---------------------------------------------------------------------------\n\nexport async function collectCalendarContext(): Promise<CollectorResult> {\n let calNames: string[] = [];\n const raw = await osascript(\n 'tell application \"Calendar\" to get name of every calendar',\n );\n if (raw) {\n calNames = raw.split(\", \").map((s) => s.trim()).filter(Boolean);\n }\n\n const lines = [\n \"## Calendar Subscriptions\",\n \"\",\n `Total calendars: ${calNames.length}`,\n \"\",\n ...calNames.map((name) => `- ${name}`),\n ];\n\n return {\n id: \"calendar-context\",\n title: \"Calendar Context\",\n content: calNames.length > 0\n ? lines.join(\"\\n\")\n : \"No calendar data accessible (Calendar app may not be running).\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. File Organization\n// ---------------------------------------------------------------------------\n\nexport async function collectFileOrganization(): Promise<CollectorResult> {\n const lines: string[] = [];\n\n // Documents top-level folders\n const docsDir = path.join(HOME, \"Documents\");\n try {\n const entries = await fs.readdir(docsDir, { withFileTypes: true });\n const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);\n lines.push(\"## Documents Folders\", \"\", ...dirs.map((d) => `- ${d}`));\n } catch {\n lines.push(\"## Documents Folders\", \"\", \"(not accessible)\");\n }\n\n // Desktop items\n const desktopDir = path.join(HOME, \"Desktop\");\n try {\n const entries = await fs.readdir(desktopDir);\n lines.push(\"\", \"## Desktop Items\", \"\", ...entries.slice(0, 20).map((e) => `- ${e}`));\n if (entries.length > 20) lines.push(`- ... and ${entries.length - 20} more`);\n } catch {\n lines.push(\"\", \"## Desktop Items\", \"\", \"(not accessible)\");\n }\n\n // Recent downloads (file names only)\n const dlDir = path.join(HOME, \"Downloads\");\n try {\n const entries = await fs.readdir(dlDir);\n // Sort by name only (stat would be slow), take first 15\n lines.push(\n \"\",\n \"## Recent Downloads (latest 15 by name)\",\n \"\",\n ...entries.slice(0, 15).map((e) => `- ${e}`),\n );\n } catch {\n lines.push(\"\", \"## Recent Downloads\", \"\", \"(not accessible)\");\n }\n\n return {\n id: \"file-organization\",\n title: \"File Organization Structure\",\n content: lines.join(\"\\n\"),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Dev Environment\n// ---------------------------------------------------------------------------\n\nexport async function collectDevEnvironment(): Promise<CollectorResult> {\n const lines: string[] = [\"## Runtime Versions\"];\n\n // Language runtimes\n const runtimes: [string, string, string[]][] = [\n [\"Node.js\", \"node\", [\"--version\"]],\n [\"Python\", \"python3\", [\"--version\"]],\n [\"Go\", \"go\", [\"version\"]],\n [\"Rust\", \"rustc\", [\"--version\"]],\n [\"Java\", \"java\", [\"--version\"]],\n ];\n\n for (const [name, cmd, args] of runtimes) {\n const ver = await exec(cmd, args);\n if (ver) {\n // Extract just the version string\n const first = ver.split(\"\\n\")[0] ?? ver;\n lines.push(`- ${name}: ${first}`);\n }\n }\n\n // Package managers\n lines.push(\"\", \"## Package Managers\");\n const pkgMgrs = [\"brew\", \"pnpm\", \"npm\", \"pip3\", \"cargo\"];\n for (const pm of pkgMgrs) {\n const which = await exec(\"which\", [pm]);\n if (which) lines.push(`- ${pm}: ${which}`);\n }\n\n // Docker\n const docker = await exec(\"docker\", [\"--version\"]);\n if (docker) lines.push(\"\", \"## Docker\", \"\", `- ${docker}`);\n\n // Shell info\n lines.push(\"\", \"## Shell\");\n const shell = process.env.SHELL ?? \"\";\n lines.push(`- Shell: ${shell}`);\n const zshrc = await readSafe(path.join(HOME, \".zshrc\"));\n const themeMatch = zshrc.match(/^ZSH_THEME=\"?([^\"\\n]+)/m);\n const pluginsMatch = zshrc.match(/^plugins=\\(([^)]+)\\)/m);\n if (themeMatch) lines.push(`- Oh-My-Zsh Theme: ${themeMatch[1]}`);\n if (pluginsMatch) lines.push(`- Oh-My-Zsh Plugins: ${pluginsMatch[1].trim()}`);\n\n // Global npm packages\n const npmGlobal = await exec(\"npm\", [\"list\", \"-g\", \"--depth=0\"]);\n if (npmGlobal) {\n const pkgs = npmGlobal\n .split(\"\\n\")\n .filter((l) => l.startsWith(\"├\") || l.startsWith(\"└\"))\n .map((l) => l.replace(/^[├└─┬│\\s]+/, \"\").trim())\n .filter(Boolean);\n if (pkgs.length > 0) {\n lines.push(\"\", \"## Global npm Packages\", \"\", ...pkgs.map((p) => `- ${p}`));\n }\n }\n\n // Homebrew casks (installed apps via brew)\n const casks = await exec(\"brew\", [\"list\", \"--cask\"]);\n if (casks) {\n const caskList = casks.split(\"\\n\").filter(Boolean);\n lines.push(\"\", \"## Homebrew Casks\", \"\", ...caskList.map((c) => `- ${c}`));\n }\n\n return {\n id: \"dev-environment\",\n title: \"Development Environment\",\n content: lines.join(\"\\n\"),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Dev Preferences\n// ---------------------------------------------------------------------------\n\nexport async function collectDevPreferences(): Promise<CollectorResult> {\n const lines: string[] = [];\n\n // Shell aliases (sanitized)\n const zshrc = await readSafe(path.join(HOME, \".zshrc\"));\n if (zshrc) {\n const sanitized = stripSecretExports(zshrc);\n const aliases = sanitized\n .split(\"\\n\")\n .filter((l) => l.match(/^\\s*alias\\s/));\n if (aliases.length > 0) {\n lines.push(\"## Shell Aliases\", \"\", ...aliases);\n }\n\n // PATH additions\n const pathLines = sanitized\n .split(\"\\n\")\n .filter((l) => l.match(/^\\s*export\\s+PATH/) && !l.includes(\"REDACTED\"));\n if (pathLines.length > 0) {\n lines.push(\"\", \"## PATH Additions\", \"\", ...pathLines);\n }\n }\n\n // Git config (sanitized)\n const gitconfigPath = path.join(HOME, \".gitconfig\");\n const gitconfig = await readSafe(gitconfigPath);\n if (gitconfig) {\n const sanitized = stripGitCredentials(gitconfig);\n lines.push(\"\", \"## Git Config\", \"\", \"```\", sanitized.trim(), \"```\");\n }\n\n // Git aliases\n const gitAliases = await exec(\"git\", [\"config\", \"--global\", \"--get-regexp\", \"alias\"]);\n if (gitAliases) {\n lines.push(\"\", \"## Git Aliases\", \"\", ...gitAliases.split(\"\\n\").map((l) => `- ${l}`));\n }\n\n // Default branch\n const defaultBranch = await exec(\"git\", [\"config\", \"--global\", \"init.defaultBranch\"]);\n if (defaultBranch) lines.push(\"\", `## Default Git Branch: ${defaultBranch}`);\n\n // Cursor extensions\n const extDir = path.join(HOME, \".cursor\", \"extensions\");\n try {\n const entries = await fs.readdir(extDir);\n const exts = entries.filter((e) => !e.startsWith(\".\") && e !== \"extensions.json\");\n if (exts.length > 0) {\n lines.push(\"\", \"## Cursor Extensions\", \"\", ...exts.map((e) => `- ${e}`));\n }\n } catch {\n // No Cursor extensions dir\n }\n\n return {\n id: \"dev-preferences\",\n title: \"Development Preferences\",\n content: lines.join(\"\\n\"),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Shell Habits\n// ---------------------------------------------------------------------------\n\nexport async function collectShellHabits(): Promise<CollectorResult> {\n const histPath = path.join(HOME, \".zsh_history\");\n const raw = await readSafe(histPath);\n\n if (!raw) {\n return {\n id: \"shell-habits\",\n title: \"Shell Command Habits\",\n content: \"No zsh history found.\",\n };\n }\n\n const rawLines = raw.split(\"\\n\");\n const totalEntries = rawLines.length;\n\n // Parse commands: zsh history format is `: timestamp:0;command`\n const freq = new Map<string, number>();\n for (const line of rawLines) {\n // Strip zsh extended history prefix\n const cmd = line.replace(/^:\\s*\\d+:\\d+;/, \"\").trim();\n const firstWord = cmd.split(/\\s+/)[0];\n if (firstWord && firstWord.length > 0 && firstWord.length < 50) {\n freq.set(firstWord, (freq.get(firstWord) ?? 0) + 1);\n }\n }\n\n const top30 = [...freq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 30);\n\n const lines = [\n `Total history entries: ${totalEntries}`,\n \"\",\n \"## Most Used Commands (Top 30)\",\n \"\",\n ...top30.map(([cmd, count]) => `- ${cmd}: ${count} times`),\n ];\n\n return {\n id: \"shell-habits\",\n title: \"Shell Command Habits\",\n content: lines.join(\"\\n\"),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 7. Coding Rules\n// ---------------------------------------------------------------------------\n\nexport async function collectCodingRules(): Promise<CollectorResult> {\n const sections: string[] = [];\n\n // Global CLAUDE.md files\n const claudePaths = [\n path.join(HOME, \"CLAUDE.md\"),\n path.join(HOME, \".claude\", \"CLAUDE.md\"),\n ];\n\n for (const p of claudePaths) {\n const content = await readSafe(p);\n if (content) {\n const relPath = p.replace(HOME, \"~\");\n sections.push(`## ${relPath}`, \"\", content.trim(), \"\");\n }\n }\n\n // Claude custom commands\n const cmdDir = path.join(HOME, \".claude\", \"commands\");\n try {\n const entries = await fs.readdir(cmdDir);\n const mdFiles = entries.filter((e) => e.endsWith(\".md\"));\n if (mdFiles.length > 0) {\n sections.push(\"## Claude Custom Commands\", \"\");\n for (const f of mdFiles) {\n const content = await readSafe(path.join(cmdDir, f));\n if (content) {\n sections.push(`### ${f}`, \"\", content.trim(), \"\");\n }\n }\n }\n } catch {\n // No commands dir\n }\n\n // Scan active project repos for CLAUDE.md / AGENTS.md / .cursorrules\n const activeRepos = await findRecentGitRepos(30);\n const projectRules: string[] = [];\n\n for (const repo of activeRepos.slice(0, 10)) {\n // Only scan top 10 most recent\n for (const ruleFile of [\"CLAUDE.md\", \"AGENTS.md\", \".cursorrules\"]) {\n const rulePath = path.join(repo, ruleFile);\n const content = await readSafe(rulePath);\n if (content && content.length > 10) {\n const repoName = path.basename(repo);\n projectRules.push(\n `### ${repoName}/${ruleFile}`,\n \"\",\n content.trim().slice(0, 2000), // Cap at 2000 chars per file\n \"\",\n );\n }\n }\n }\n\n if (projectRules.length > 0) {\n sections.push(\"## Project-Level Rules\", \"\", ...projectRules);\n }\n\n // Cursor rules directory\n const cursorRulesDir = path.join(HOME, \".cursor\", \"rules\");\n try {\n const entries = await fs.readdir(cursorRulesDir);\n const mdFiles = entries.filter((e) => e.endsWith(\".md\") || e.endsWith(\".mdc\"));\n if (mdFiles.length > 0) {\n sections.push(\"## Global Cursor Rules\", \"\");\n for (const f of mdFiles) {\n const content = await readSafe(path.join(cursorRulesDir, f));\n if (content) {\n sections.push(`### ${f}`, \"\", content.trim().slice(0, 1000), \"\");\n }\n }\n }\n } catch {\n // No cursor rules dir\n }\n\n return {\n id: \"coding-rules\",\n title: \"Coding Rules and AI Agent Config\",\n content: sections.length > 0\n ? sections.join(\"\\n\")\n : \"No coding rules found.\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// 8. Active Projects\n// ---------------------------------------------------------------------------\n\nexport async function collectActiveProjects(): Promise<CollectorResult> {\n const lines: string[] = [];\n\n // Recent git repos\n const repos = await findRecentGitRepos(30);\n if (repos.length > 0) {\n lines.push(\"## Active Git Repositories (last 30 days)\", \"\");\n for (const repo of repos) {\n const name = repo.replace(HOME, \"~\");\n lines.push(`- ${name}`);\n }\n }\n\n // SSH config hosts\n const sshConfig = await readSafe(path.join(HOME, \".ssh\", \"config\"));\n if (sshConfig) {\n const sanitized = stripSshIdentityFiles(sshConfig);\n const hosts = sanitized\n .split(\"\\n\")\n .filter((l) => /^\\s*Host\\s/i.test(l) && !l.includes(\"*\"))\n .map((l) => l.replace(/^\\s*Host\\s+/i, \"\").trim());\n if (hosts.length > 0) {\n lines.push(\"\", \"## SSH Hosts\", \"\", ...hosts.map((h) => `- ${h}`));\n }\n }\n\n // Cloud storage\n const cloudDir = path.join(HOME, \"Library\", \"CloudStorage\");\n try {\n const entries = await fs.readdir(cloudDir);\n if (entries.length > 0) {\n lines.push(\"\", \"## Cloud Storage\", \"\", ...entries.map((e) => `- ${e}`));\n }\n } catch {\n // No cloud storage\n }\n\n return {\n id: \"active-projects\",\n title: \"Active Projects and Infrastructure\",\n content: lines.join(\"\\n\"),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 9. Productivity Setup\n// ---------------------------------------------------------------------------\n\nexport async function collectProductivitySetup(): Promise<CollectorResult> {\n const lines: string[] = [];\n\n // Installed applications\n try {\n const entries = await fs.readdir(\"/Applications\");\n const apps = entries\n .filter((e) => e.endsWith(\".app\"))\n .map((e) => e.replace(\".app\", \"\"));\n lines.push(\"## Installed Applications\", \"\", ...apps.map((a) => `- ${a}`));\n } catch {\n lines.push(\"## Installed Applications\", \"\", \"(not accessible)\");\n }\n\n // Dock persistent apps\n const dockApps = await defaultsRead(\"com.apple.dock\", \"persistent-apps\");\n if (dockApps) {\n const labels = [...dockApps.matchAll(/\"file-label\"\\s*=\\s*\"?([^\";\\n}]+)/g)]\n .map((m) => m[1].trim())\n .filter(Boolean);\n if (labels.length > 0) {\n lines.push(\"\", \"## Dock Apps (pinned)\", \"\", ...labels.map((a) => `- ${a}`));\n }\n }\n\n // Default browser\n const handlers = await defaultsRead(\n \"com.apple.LaunchServices/com.apple.launchservices.secure\",\n \"LSHandlers\",\n );\n const browserMatch = handlers.match(/LSHandlerRoleAll\\s*=\\s*\"([^\"]+)\"/);\n if (browserMatch) {\n const bundleId = browserMatch[1];\n const browserName = bundleId.includes(\"edge\")\n ? \"Microsoft Edge\"\n : bundleId.includes(\"chrome\")\n ? \"Google Chrome\"\n : bundleId.includes(\"firefox\")\n ? \"Firefox\"\n : bundleId.includes(\"safari\")\n ? \"Safari\"\n : bundleId;\n lines.push(\"\", `## Default Browser: ${browserName}`);\n }\n\n // Screen resolution\n const displayInfo = await exec(\"system_profiler\", [\"SPDisplaysDataType\"]);\n const resolutions = [...displayInfo.matchAll(/Resolution:\\s*(.+)/g)].map((m) => m[1].trim());\n if (resolutions.length > 0) {\n lines.push(\"\", \"## Screen Resolution\", \"\", ...resolutions.map((r) => `- ${r}`));\n }\n\n return {\n id: \"productivity-setup\",\n title: \"Productivity Setup\",\n content: lines.join(\"\\n\"),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 10. Browser Bookmarks\n// ---------------------------------------------------------------------------\n\nexport async function collectBrowserBookmarks(): Promise<CollectorResult> {\n const bmPath = path.join(\n HOME,\n \"Library\",\n \"Application Support\",\n \"Microsoft Edge\",\n \"Default\",\n \"Bookmarks\",\n );\n\n const raw = await readSafe(bmPath);\n if (!raw) {\n return {\n id: \"browser-bookmarks\",\n title: \"Browser Bookmark Structure\",\n content: \"No Edge bookmarks found.\",\n };\n }\n\n try {\n const data = JSON.parse(raw);\n const folders: { name: string; count: number; depth: number }[] = [];\n let totalUrls = 0;\n\n function walk(\n node: Record<string, unknown>,\n depth: number,\n ): void {\n if (node.type === \"folder\") {\n const children = (node.children ?? []) as Record<string, unknown>[];\n const name = (node.name as string) ?? \"\";\n if (children.length > 0) {\n folders.push({ name, count: children.length, depth });\n }\n for (const child of children) {\n walk(child, depth + 1);\n }\n } else if (node.type === \"url\") {\n totalUrls++;\n }\n }\n\n const roots = data.roots as Record<string, unknown>;\n for (const root of Object.values(roots)) {\n if (root && typeof root === \"object\" && \"children\" in (root as Record<string, unknown>)) {\n walk(root as Record<string, unknown>, 0);\n }\n }\n\n // Sort by count descending\n folders.sort((a, b) => b.count - a.count);\n\n const lines = [\n `Total bookmarks: ${totalUrls}`,\n `Total folders: ${folders.length}`,\n \"\",\n \"## Top Bookmark Folders (by item count)\",\n \"\",\n ...folders.slice(0, 40).map((f) => `- ${\" \".repeat(Math.min(f.depth, 2))}${f.name}: ${f.count} items`),\n ];\n\n return {\n id: \"browser-bookmarks\",\n title: \"Browser Bookmark Structure\",\n content: lines.join(\"\\n\"),\n };\n } catch {\n return {\n id: \"browser-bookmarks\",\n title: \"Browser Bookmark Structure\",\n content: \"Failed to parse Edge bookmarks.\",\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// 11. Browser Domains\n// ---------------------------------------------------------------------------\n\nexport async function collectBrowserDomains(): Promise<CollectorResult> {\n const histPath = path.join(\n HOME,\n \"Library\",\n \"Application Support\",\n \"Microsoft Edge\",\n \"Default\",\n \"History\",\n );\n\n if (!(await exists(histPath))) {\n return {\n id: \"browser-domains\",\n title: \"Browser Top Domains (30 days)\",\n content: \"No Edge history found.\",\n };\n }\n\n // Copy the locked DB to a temp file for reading\n const tmpDb = path.join(os.tmpdir(), `pai-edge-history-${Date.now()}.db`);\n try {\n await fs.copyFile(histPath, tmpDb);\n\n const query = `\n SELECT\n REPLACE(REPLACE(SUBSTR(url, 1, INSTR(SUBSTR(url, 9), '/') + 8), 'https://', ''), 'http://', '') as domain,\n COUNT(*) as visits\n FROM urls\n WHERE last_visit_time > (strftime('%s', 'now', '-30 days') + 11644473600) * 1000000\n GROUP BY domain\n ORDER BY visits DESC\n LIMIT 30;\n `.trim();\n\n const output = await exec(\"sqlite3\", [tmpDb, query]);\n\n if (!output) {\n return {\n id: \"browser-domains\",\n title: \"Browser Top Domains (30 days)\",\n content: \"Could not query Edge history (DB may be locked).\",\n };\n }\n\n const lines = [\n \"## Top 30 Domains (last 30 days)\",\n \"\",\n ...output.split(\"\\n\").map((line) => {\n const [domain, visits] = line.split(\"|\");\n return `- ${domain}: ${visits} visits`;\n }),\n ];\n\n return {\n id: \"browser-domains\",\n title: \"Browser Top Domains (30 days)\",\n content: lines.join(\"\\n\"),\n };\n } finally {\n try {\n await fs.unlink(tmpDb);\n } catch {\n // Cleanup failure is ok\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// 12. GitHub Profile\n// ---------------------------------------------------------------------------\n\nexport async function collectGitHubProfile(): Promise<CollectorResult> {\n const lines: string[] = [];\n\n // Check if gh CLI is available\n const ghVersion = await exec(\"gh\", [\"--version\"]);\n if (!ghVersion) {\n return {\n id: \"github-profile\",\n title: \"GitHub Profile\",\n content: \"GitHub CLI (gh) not installed.\",\n };\n }\n\n // Check auth status\n const authStatus = await exec(\"gh\", [\"auth\", \"status\"]);\n if (!authStatus) {\n return {\n id: \"github-profile\",\n title: \"GitHub Profile\",\n content: \"GitHub CLI not authenticated. Run `gh auth login` to enable.\",\n };\n }\n\n // Get user profile\n const userJson = await exec(\"gh\", [\n \"api\", \"user\",\n \"--jq\", '[.login, .name, .bio, .company, .location, .blog, .public_repos, .followers, .following, .created_at] | @tsv',\n ]);\n\n if (userJson) {\n const parts = userJson.split(\"\\t\");\n const [login, name, bio, company, location, blog, publicRepos, followers, following, createdAt] = parts;\n lines.push(\"## GitHub Profile\", \"\");\n if (login) lines.push(`- Username: ${login}`);\n if (name) lines.push(`- Name: ${name}`);\n if (bio) lines.push(`- Bio: ${bio}`);\n if (company) lines.push(`- Company: ${company}`);\n if (location) lines.push(`- Location: ${location}`);\n if (blog) lines.push(`- Website: ${blog}`);\n if (publicRepos) lines.push(`- Public Repos: ${publicRepos}`);\n if (followers || following) lines.push(`- Followers/Following: ${followers}/${following}`);\n if (createdAt) lines.push(`- Member since: ${createdAt}`);\n }\n\n // Get recent repos (owned, sorted by push date)\n const reposJson = await exec(\"gh\", [\n \"repo\", \"list\", \"--limit\", \"10\", \"--sort\", \"updated\", \"--json\",\n \"name,description,primaryLanguage,pushedAt,isPrivate\",\n \"--jq\", '.[] | [.name, .description, (.primaryLanguage.name // \"\"), .pushedAt, .isPrivate] | @tsv',\n ]);\n\n if (reposJson) {\n const repos = reposJson.split(\"\\n\").filter(Boolean);\n if (repos.length > 0) {\n lines.push(\"\", \"## Recent GitHub Repos (by push date)\", \"\");\n for (const repo of repos) {\n const [name, desc, lang, _pushed, isPrivate] = repo.split(\"\\t\");\n const visibility = isPrivate === \"true\" ? \"private\" : \"public\";\n const langTag = lang ? ` [${lang}]` : \"\";\n const descTag = desc ? ` — ${desc}` : \"\";\n lines.push(`- ${name}${langTag} (${visibility})${descTag}`);\n }\n }\n }\n\n // Get starred repos (recent 20 for interest signals)\n const starsJson = await exec(\"gh\", [\n \"api\", \"user/starred?per_page=20&sort=created&direction=desc\",\n \"--jq\", '.[].full_name',\n ]);\n\n if (starsJson) {\n const stars = starsJson.split(\"\\n\").filter(Boolean);\n if (stars.length > 0) {\n lines.push(\"\", \"## Recently Starred Repos (interest signals)\", \"\");\n lines.push(stars.map((s) => `- ${s}`).join(\"\\n\"));\n }\n }\n\n return {\n id: \"github-profile\",\n title: \"GitHub Profile & Activity\",\n content: lines.length > 0 ? lines.join(\"\\n\") : \"No GitHub data accessible.\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// 13. Recent Focus (inferred from git commits + shell history)\n// ---------------------------------------------------------------------------\n\nexport async function collectRecentFocus(): Promise<CollectorResult> {\n const lines: string[] = [];\n\n // Analyze recent commit messages across active repos for topic signals\n const repos = await findRecentGitRepos(14); // Last 2 weeks\n const commitTopics = new Map<string, number>();\n const recentMessages: string[] = [];\n\n for (const repo of repos.slice(0, 8)) {\n // Get recent commit subjects from this repo\n const log = await exec(\"git\", [\n \"-C\", repo,\n \"log\", \"--oneline\", \"--since=14 days ago\",\n \"--format=%s\", \"-n\", \"20\",\n ]);\n if (log) {\n for (const msg of log.split(\"\\n\").filter(Boolean)) {\n recentMessages.push(msg);\n // Extract first word (often a conventional commit type)\n const type = msg.match(/^(feat|fix|refactor|test|docs|chore|perf|ci|build|style)/i);\n if (type) {\n const key = type[1].toLowerCase();\n commitTopics.set(key, (commitTopics.get(key) ?? 0) + 1);\n }\n }\n }\n }\n\n if (commitTopics.size > 0) {\n const sorted = [...commitTopics.entries()].sort((a, b) => b[1] - a[1]);\n lines.push(\"## Recent Commit Activity (last 2 weeks)\", \"\");\n lines.push(`Total commits analyzed: ${recentMessages.length}`);\n lines.push(\"\", \"Commit types:\");\n for (const [type, count] of sorted) {\n lines.push(`- ${type}: ${count}`);\n }\n }\n\n // Extract recent commit subjects for topic inference\n if (recentMessages.length > 0) {\n lines.push(\"\", \"## Recent Commit Messages (sample)\", \"\");\n // Take a diverse sample (first 15 unique-ish messages)\n const unique = [...new Set(recentMessages)].slice(0, 15);\n for (const msg of unique) {\n lines.push(`- ${msg}`);\n }\n }\n\n // Recent shell commands that indicate current work context\n const histPath = path.join(HOME, \".zsh_history\");\n const histRaw = await readSafe(histPath);\n if (histRaw) {\n const histLines = histRaw.split(\"\\n\");\n // Take last 200 history entries and extract project-related patterns\n const recent = histLines.slice(-200);\n const cdPaths = new Map<string, number>();\n const tools = new Map<string, number>();\n\n for (const line of recent) {\n const cmd = line.replace(/^:\\s*\\d+:\\d+;/, \"\").trim();\n // Track cd destinations\n const cdMatch = cmd.match(/^cd\\s+(.+)/);\n if (cdMatch) {\n const dest = cdMatch[1].trim().replace(/^~/, HOME);\n cdPaths.set(dest, (cdPaths.get(dest) ?? 0) + 1);\n }\n // Track tool usage\n const toolMatch = cmd.match(/^(docker|kubectl|terraform|aws|gcloud|firebase|vercel|netlify|npm|pnpm|yarn|bun|cargo|go|python|pip|uv)\\b/);\n if (toolMatch) {\n tools.set(toolMatch[1], (tools.get(toolMatch[1]) ?? 0) + 1);\n }\n }\n\n if (cdPaths.size > 0) {\n const topDirs = [...cdPaths.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8);\n lines.push(\"\", \"## Recent Working Directories\", \"\");\n for (const [dir, count] of topDirs) {\n lines.push(`- ${dir.replace(HOME, \"~\")}: ${count}x`);\n }\n }\n\n if (tools.size > 0) {\n const topTools = [...tools.entries()].sort((a, b) => b[1] - a[1]);\n lines.push(\"\", \"## Recently Used Tools (from history)\", \"\");\n for (const [tool, count] of topTools) {\n lines.push(`- ${tool}: ${count}x`);\n }\n }\n }\n\n return {\n id: \"recent-focus\",\n title: \"Recent Focus & Activity\",\n content: lines.length > 0\n ? lines.join(\"\\n\")\n : \"No recent activity data found.\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// 14. Social & Registry Profiles\n// ---------------------------------------------------------------------------\n\nexport async function collectSocialProfiles(): Promise<CollectorResult> {\n const lines: string[] = [];\n\n // npm registry identity\n const npmrc = await readSafe(path.join(HOME, \".npmrc\"));\n if (npmrc) {\n // Extract registry URL (but not tokens)\n const registryMatch = npmrc.match(/^registry\\s*=\\s*(.+)/m);\n if (registryMatch) lines.push(`- npm registry: ${registryMatch[1].trim()}`);\n // Check for scope configs\n const scopes = npmrc.match(/^@[\\w-]+:registry/gm);\n if (scopes) {\n for (const s of scopes) {\n lines.push(`- npm scope: ${s.split(\":\")[0]}`);\n }\n }\n }\n\n // npm whoami\n const npmUser = await exec(\"npm\", [\"whoami\"]);\n if (npmUser) lines.push(`- npm username: ${npmUser}`);\n\n // PyPI config\n const pypirc = await readSafe(path.join(HOME, \".pypirc\"));\n if (pypirc) {\n const repoMatch = pypirc.match(/repository\\s*=\\s*(.+)/);\n if (repoMatch) lines.push(`- PyPI repository: ${repoMatch[1].trim()}`);\n const usernameMatch = pypirc.match(/username\\s*=\\s*(.+)/);\n if (usernameMatch) lines.push(`- PyPI username: ${usernameMatch[1].trim()}`);\n }\n\n // Docker Hub\n const dockerConfig = await readSafe(path.join(HOME, \".docker\", \"config.json\"));\n if (dockerConfig) {\n try {\n const cfg = JSON.parse(dockerConfig);\n const auths = Object.keys(cfg.auths ?? {});\n if (auths.length > 0) {\n lines.push(`- Docker registries: ${auths.join(\", \")}`);\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n // Cargo / crates.io\n const cargoConfig = await readSafe(path.join(HOME, \".cargo\", \"config.toml\"));\n if (cargoConfig) {\n const registries = cargoConfig.match(/\\[registries\\.(\\w+)\\]/g);\n if (registries) {\n lines.push(`- Cargo registries: ${registries.map((r) => r.replace(/\\[registries\\.|\\]/g, \"\")).join(\", \")}`);\n }\n }\n\n // Cloud provider CLIs\n const awsIdentity = await exec(\"aws\", [\"sts\", \"get-caller-identity\", \"--query\", \"Account\", \"--output\", \"text\"]);\n if (awsIdentity) lines.push(`- AWS Account: ${awsIdentity}`);\n\n const gcpProject = await exec(\"gcloud\", [\"config\", \"get-value\", \"project\"]);\n if (gcpProject && !gcpProject.includes(\"unset\")) lines.push(`- GCP Project: ${gcpProject}`);\n\n const vercelUser = await exec(\"vercel\", [\"whoami\"]);\n if (vercelUser) lines.push(`- Vercel: ${vercelUser}`);\n\n return {\n id: \"social-profiles\",\n title: \"Registry & Cloud Profiles\",\n content: lines.length > 0\n ? [\"## Registry & Cloud Accounts\", \"\", ...lines].join(\"\\n\")\n : \"No registry or cloud profiles detected.\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared utilities\n// ---------------------------------------------------------------------------\n\n/** Find git repos under ~/Documents modified in the last N days */\nasync function findRecentGitRepos(days: number): Promise<string[]> {\n const output = await exec(\"find\", [\n path.join(HOME, \"Documents\"),\n \"-maxdepth\", \"4\",\n \"-name\", \".git\",\n \"-type\", \"d\",\n \"-mtime\", `-${days}`,\n ]);\n\n if (!output) return [];\n return output\n .split(\"\\n\")\n .filter(Boolean)\n .map((p) => p.replace(/\\/.git$/, \"\"));\n}\n\n// ---------------------------------------------------------------------------\n// Export all collectors\n// ---------------------------------------------------------------------------\n\n/** All available Mac collectors in execution order */\nexport const ALL_COLLECTORS = [\n collectIdentityProfile,\n collectCalendarContext,\n collectFileOrganization,\n collectDevEnvironment,\n collectDevPreferences,\n collectShellHabits,\n collectCodingRules,\n collectActiveProjects,\n collectProductivitySetup,\n collectBrowserBookmarks,\n collectBrowserDomains,\n collectGitHubProfile,\n collectRecentFocus,\n collectSocialProfiles,\n] as const;\n","/**\n * Profile module — compile, load, and rebuild user profiles.\n */\n\nimport fs from \"node:fs/promises\";\nimport { getProfilePath } from \"../config/paths.js\";\nimport { addConnectorEntry } from \"../raw/add.js\";\nimport * as console from \"../utils/console.js\";\nimport { compileProfile, compileCompactProfile } from \"./compile.js\";\nimport type { CollectorResult } from \"../types.js\";\nimport { ALL_COLLECTORS } from \"../connectors/mac/collectors.js\";\n\nexport { compileProfile, compileCompactProfile } from \"./compile.js\";\n\n/** Load the current profile from disk. Returns null if not found. */\nexport async function loadProfile(): Promise<string | null> {\n try {\n return await fs.readFile(getProfilePath(), \"utf-8\");\n } catch {\n return null;\n }\n}\n\n/** Save profile markdown to disk. */\nexport async function saveProfile(content: string): Promise<string> {\n const profilePath = getProfilePath();\n await fs.writeFile(profilePath, content, \"utf-8\");\n return profilePath;\n}\n\n/**\n * Full rebuild: scan local machine → compile profile → write to disk.\n * Also writes raw files for the scan data (for vault/distill pipeline compatibility).\n */\nexport async function rebuildProfile(opts?: {\n /** If true, skip writing raw files (profile-only mode) */\n skipRaw?: boolean;\n /** If true, show progress spinners */\n verbose?: boolean;\n}): Promise<{ profilePath: string; results: CollectorResult[] }> {\n const verbose = opts?.verbose ?? true;\n\n // 1. Run all collectors concurrently\n const spin = verbose ? console.spinner(\"Scanning local machine...\") : null;\n\n const settled = await Promise.allSettled(\n ALL_COLLECTORS.map((collector) => collector()),\n );\n\n const results: CollectorResult[] = [];\n const failed: string[] = [];\n\n for (let i = 0; i < settled.length; i++) {\n const s = settled[i];\n if (s.status === \"fulfilled\") {\n results.push(s.value);\n } else {\n const name = ALL_COLLECTORS[i].name;\n failed.push(name);\n if (verbose) console.warn(`Collector ${name} failed: ${String(s.reason)}`);\n }\n }\n\n spin?.succeed(`Scanned ${results.length} sources (${failed.length} failed)`);\n\n // 2. Optionally write raw files (for backward compat with vault/distill)\n if (!opts?.skipRaw) {\n for (const entry of results) {\n try {\n await addConnectorEntry(\"mac\", entry);\n } catch {\n // Non-critical — profile compilation doesn't depend on raw writes\n }\n }\n }\n\n // 3. Compile and save profile\n const profileContent = compileProfile(results);\n const profilePath = await saveProfile(profileContent);\n\n return { profilePath, results };\n}\n"],"mappings":";;;;;;;;;;;;;;AAIA,SAAgB,aAAqB;AACnC,QAAO,QAAQ,IAAI,YAAY,KAAK,KAAK,GAAG,SAAS,EAAE,OAAO;;AAGhE,SAAgB,YAAoB;AAClC,QAAO,KAAK,KAAK,YAAY,EAAE,MAAM;;AAGvC,SAAgB,cAAsB;AACpC,QAAO,KAAK,KAAK,YAAY,EAAE,QAAQ;;AAGzC,SAAgB,eAAuB;AACrC,QAAO,KAAK,KAAK,YAAY,EAAE,UAAU,WAAW;;AAGtD,SAAgB,eAAuB;AACrC,QAAO,KAAK,KAAK,YAAY,EAAE,SAAS;;AAG1C,SAAgB,gBAAwB;AACtC,QAAO,KAAK,KAAK,cAAc,EAAE,YAAY;;AAG/C,SAAgB,kBAA0B;AACxC,QAAO,KAAK,KAAK,cAAc,EAAE,iBAAiB;;AAGpD,SAAgB,qBAA6B;AAC3C,QAAO,KAAK,KAAK,cAAc,EAAE,iBAAiB;;;AAIpD,SAAgB,iBAAyB;AACvC,QAAO,KAAK,KAAK,YAAY,EAAE,aAAa;;;;;ACpC9C,MAAa,kBAAkB,EAAE,OAAO;CACtC,eAAe,EAAE,QAAQ,CAAC,QAAQ,MAAM;CACxC,iBAAiB,EAAE,QAAQ,CAAC,QAAQ,QAAQ;CAC7C,CAAC;AAEF,MAAa,kBAAkB,EAAE,OAAO;CACtC,WAAW,EAAE,QAAQ,CAAC,QAAQ,iBAAiB;CAC/C,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,YAAY,EAAE,QAAQ,CAAC,QAAQ,cAAc;CAC7C,gBAAgB,EAAE,QAAQ,CAAC,QAAQ,SAAS;CAC7C,CAAC;AAEF,MAAa,sBAAsB,EAAE,OAAO,EAC1C,SAAS,EAAE,QAAQ,CAAC,QAAQ,IAAO,EACpC,CAAC;AAEF,MAAa,oBAAoB,EAAE,OAAO;CACxC,eAAe,EAAE,QAAQ,CAAC,QAAQ,IAAK;CACvC,gBAAgB,EAAE,QAAQ,CAAC,QAAQ,IAAK;CACzC,CAAC;AAEF,MAAa,kBAAkB,EAC5B,OAAO;CACN,SAAS,EAAE,QAAQ,CAAC,QAAQ,MAAM;CAClC,KAAK,gBAAgB,cAAc,gBAAgB,MAAM,EAAE,CAAC,CAAC;CAC7D,KAAK,gBAAgB,cAAc,gBAAgB,MAAM,EAAE,CAAC,CAAC;CAC7D,SAAS,oBAAoB,cAAc,oBAAoB,MAAM,EAAE,CAAC,CAAC;CACzE,OAAO,kBAAkB,cAAc,kBAAkB,MAAM,EAAE,CAAC,CAAC;CACpE,CAAC,CACD,QAAQ;;;;;ACxBX,eAAsB,aAAiC;CACrD,MAAM,aAAa,eAAe;AAClC,KAAI;EACF,MAAM,MAAM,MAAM,GAAG,SAAS,YAAY,QAAQ;EAClD,MAAM,SAAS,MAAM,MAAM,IAAI;AAC/B,SAAO,gBAAgB,MAAM,OAAO;UAC7B,KAAc;AACrB,MAAK,IAA8B,SAAS,SAE1C,QAAO,gBAAgB,MAAM,EAAE,CAAC;AAElC,QAAM;;;;AAKV,eAAsB,eAAwC;CAC5D,MAAM,eAAe,iBAAiB;AACtC,KAAI;EACF,MAAM,MAAM,MAAM,GAAG,SAAS,cAAc,QAAQ;AACpD,SAAO,MAAM,MAAM,IAAI;UAChB,KAAc;AACrB,MAAK,IAA8B,SAAS,SAC1C,QAAO,EAAE,UAAU,EAAE,EAAE;AAEzB,QAAM;;;;;;;AC/BV,SAAgB,aAAa,OAAuB;AAClD,QAAO,MACJ,aAAa,CACb,QAAQ,iBAAiB,GAAG,CAC5B,QAAQ,QAAQ,IAAI,CACpB,QAAQ,OAAO,IAAI,CACnB,QAAQ,UAAU,GAAG,CACrB,MAAM,GAAG,GAAG;;;AAIjB,SAAgB,oBAAoB,OAAuB;AAOzD,QAAO,oBANK,IAAI,MAAM,EAEnB,aAAa,CACb,QAAQ,mBAAmB,GAAG,CAC9B,QAAQ,MAAM,IAAI,CAER,GADA,aAAa,MAAM,IAAI,WACf;;;;;;ACfvB,SAAgB,iBACd,SAC8B;CAC9B,MAAM,SAAS,OAAO,QAAQ;AAC9B,QAAO;EAAE,MAAM,OAAO;EAAW,SAAS,OAAO;EAAS;;;AAI5D,SAAgB,qBACd,MACA,SACQ;AACR,QAAO,OAAO,UAAU,SAAS,KAAK;;;AAIxC,SAAgB,cACd,aACA,OACA,MACQ;AAER,QAAO,qBAAqB,aADZ,OAAO,MAAM,MAAM,KAAK,IAC+C;;;AAIzF,SAAgB,qBACd,aACA,SACQ;CACR,MAAM,EAAE,MAAM,YAAY,iBAAiC,YAAY;AAEvE,QAAO,qBADS;EAAE,GAAG;EAAM,GAAG;EAAS,EACoC,QAAQ;;;;;;AC5BrF,eAAsB,QACpB,SACA,SAAiB,SACA;CACjB,MAAM,QAAQ,aAAa,QAAQ;CACnC,MAAM,WAAW,oBAAoB,MAAM;CAC3C,MAAM,MAAM,KAAK,KAAK,WAAW,EAAE,QAAQ;AAC3C,OAAM,GAAG,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;CAQxC,MAAM,cAAc,cANO;EACzB;EACA,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,QAAQ;EACT,EAEqC,OAAO,QAAQ;CACrD,MAAM,WAAW,KAAK,KAAK,KAAK,SAAS;AACzC,OAAM,GAAG,UAAU,UAAU,aAAa,QAAQ;AAClD,QAAO;;;AAIT,eAAsB,QACpB,UACA,SAAiB,SACA;CACjB,MAAM,UAAU,MAAM,GAAG,SAAS,UAAU,QAAQ;CACpD,MAAM,WAAW,KAAK,SAAS,UAAU,KAAK,QAAQ,SAAS,CAAC;CAChE,MAAM,WAAW,oBAAoB,SAAS;CAC9C,MAAM,MAAM,KAAK,KAAK,WAAW,EAAE,QAAQ;AAC3C,OAAM,GAAG,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;CAQxC,MAAM,cAAc,cANO;EACzB;EACA,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,QAAQ;EACT,EAEqC,UAAU,QAAQ;CACxD,MAAM,UAAU,KAAK,KAAK,KAAK,SAAS;AACxC,OAAM,GAAG,UAAU,SAAS,aAAa,QAAQ;AACjD,QAAO;;;AAIT,eAAsB,OACpB,KACA,OACA,UACiB;CACjB,MAAM,WAAW,oBAAoB,MAAM;CAC3C,MAAM,MAAM,KAAK,KAAK,WAAW,EAAE,MAAM;AACzC,OAAM,GAAG,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;CASxC,MAAM,cAAc,cAPO;EACzB,QAAQ;EACR,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;EACA,QAAQ;EACT,EAEqC,OAAO,SAAS;CACtD,MAAM,WAAW,KAAK,KAAK,KAAK,SAAS;AACzC,OAAM,GAAG,UAAU,UAAU,aAAa,QAAQ;AAClD,QAAO;;;;;;;;;;AAWT,eAAsB,kBACpB,eACA,OAC4C;CAC5C,MAAM,MAAM,KAAK,KAAK,WAAW,EAAE,aAAa,cAAc;AAC9D,OAAM,GAAG,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;CAExC,MAAM,SAAS,GAAG,cAAc,GAAG,MAAM;CAGzC,MAAM,eAAe,MAAM,aAAa,KAAK,OAAO;CAEpD,MAAM,KAAqB;EACzB,QAAQ,aAAa;EACrB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,QAAQ;EACR,SAAS;EACV;AAED,KAAI,cAAc;EAGhB,MAAM,EAAE,SAAS,YAAY,iBADd,MAAM,GAAG,SAAS,cAAc,QAAQ,CACF;EACrD,MAAM,UAAU,OAAO,MAAM,MAAM,MAAM,MAAM,QAAQ;AAEvD,MAAI,QAAQ,MAAM,KAAK,QAAQ,MAAM,CACnC,QAAO;EAIT,MAAM,cAAc,cAAc,IAAI,MAAM,OAAO,MAAM,QAAQ;AACjE,QAAM,GAAG,UAAU,cAAc,aAAa,QAAQ;AACtD,SAAO;;CAIT,MAAM,WAAW,oBAAoB,MAAM,GAAG;CAC9C,MAAM,cAAc,cAAc,IAAI,MAAM,OAAO,MAAM,QAAQ;CACjE,MAAM,WAAW,KAAK,KAAK,KAAK,SAAS;AACzC,OAAM,GAAG,UAAU,UAAU,aAAa,QAAQ;AAClD,QAAO;;;AAIT,eAAe,aAAa,KAAa,QAAwC;AAC/E,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,QAAQ,IAAI;AACrC,OAAK,MAAM,QAAQ,SAAS;AAC1B,OAAI,CAAC,KAAK,SAAS,MAAM,CAAE;GAC3B,MAAM,WAAW,KAAK,KAAK,KAAK,KAAK;GACrC,MAAM,UAAU,MAAM,GAAG,SAAS,UAAU,QAAQ;AACpD,OAAI,QAAQ,SAAS,YAAY,SAAS,IAAI,QAAQ,SAAS,aAAa,OAAO,GAAG,CACpF,QAAO;;SAGL;AAGR,QAAO;;;AAIT,eAAsB,cAAiC;CACrD,MAAM,SAAS,WAAW;CAC1B,MAAM,UAAoB,EAAE;AAG5B,MAAK,MAAM,OADK;EAAC;EAAS;EAAO;EAAY,EAClB;EACzB,MAAM,MAAM,KAAK,KAAK,QAAQ,IAAI;AAClC,MAAI;GACF,MAAM,UAAU,MAAM,QAAQ,IAAI;AAClC,QAAK,MAAM,SAAS,QAClB,KAAI,MAAM,SAAS,MAAM,EAEvB;SADgB,MAAM,GAAG,SAAS,OAAO,QAAQ,EACrC,SAAS,kBAAkB,CACrC,SAAQ,KAAK,MAAM;;UAInB;;AAIV,QAAO;;;AAsBT,eAAe,QAAQ,KAAgC;CACrD,MAAM,UAAoB,EAAE;AAC5B,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;AAC9D,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK;AAC3C,OAAI,MAAM,aAAa,CACrB,SAAQ,KAAK,GAAI,MAAM,QAAQ,SAAS,CAAE;OAE1C,SAAQ,KAAK,SAAS;;SAGpB;AAGR,QAAO;;;AAIT,SAAS,aAAa,SAAyB;CAG7C,MAAM,WAFY,QAAQ,MAAM,KAAK,CAAC,IAAI,MAAM,IAAI,IAE1B,QAAQ,UAAU,GAAG;AAC/C,KAAI,QAAQ,SAAS,KAAK,QAAQ,UAAU,IAC1C,QAAO;AAGT,QAAO,QAAQ,MAAM,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,MAAM,GAAG,IAAI,IAAI;;;;;AC/MrE,SAAgB,KAAK,SAAuB;AAE1C,SAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,QAAQ;;AAQvC,SAAgB,KAAK,SAAuB;AAE1C,SAAQ,KAAK,MAAM,OAAO,IAAI,EAAE,QAAQ;;AAQ1C,SAAgB,QAAQ,MAAmB;AACzC,QAAO,IAAI;EAAE;EAAM,OAAO;EAAQ,CAAC,CAAC,OAAO;;;;;;;;;;AChB7C,eAAsB,UACpB,KACA,UAAkB,KACK;CAEvB,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,QAAQ;CAE3D,IAAI;AACJ,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,KAAK;GAChC,QAAQ,WAAW;GACnB,SAAS;IACP,cACE;IACF,QAAQ;IACT;GACF,CAAC;AACF,MAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,QAAQ,SAAS,OAAO,IAAI,SAAS,aAAa;AAEpE,SAAO,MAAM,SAAS,MAAM;WACpB;AACR,eAAa,MAAM;;AAIrB,KAAI;EAEF,MAAM,cAAc,MAAM,OAAO;EACjC,MAAM,WAAW,YAAY,YAAY,YAAY;AACrD,MAAI,UAAU;GACZ,MAAM,SAAS,IAAI,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC,OAAO;AAKlD,UAAO;IAAE;IAAK,OAJA,OAAO,SAAS,qBAAqB,KAAK;IAInC,UAHJ,OAAO,UACpB,qBAAqB,OAAO,QAAQ,GACpC,oBAAoB,KAAK;IACE;;SAE3B;AACN,OAAa,sDAAsD;;AAMrE,QAAO;EAAE;EAAK,OAFA,qBAAqB,KAAK;EAEnB,UADJ,oBAAoB,KAAK;EACX;;;AAIjC,SAAS,qBAAqB,MAAsB;AAElD,QADc,KAAK,MAAM,gCAAgC,GAC1C,IAAI,MAAM,IAAI;;;AAI/B,SAAS,oBAAoB,MAAsB;AACjD,QACE,KAEG,QAAQ,+BAA+B,GAAG,CAC1C,QAAQ,6BAA6B,GAAG,CAExC,QAAQ,YAAY,IAAI,CAExB,QAAQ,UAAU,IAAI,CACtB,QAAQ,SAAS,IAAI,CACrB,QAAQ,SAAS,IAAI,CACrB,QAAQ,WAAW,KAAI,CACvB,QAAQ,UAAU,IAAI,CACtB,QAAQ,WAAW,IAAI,CAEvB,QAAQ,QAAQ,IAAI,CACpB,MAAM,CACN,MAAM,GAAG,IAAM;;;AAKtB,SAAS,qBAAqB,MAAsB;AAClD,QACE,KAEG,QAAQ,+BAA+B,WAAW,CAClD,QAAQ,+BAA+B,YAAY,CACnD,QAAQ,+BAA+B,aAAa,CAEpD,QAAQ,6BAA6B,SAAS,CAE9C,QAAQ,gBAAgB,KAAK,CAE7B,QAAQ,uCAAuC,SAAS,CACxD,QAAQ,+BAA+B,OAAO,CAE9C,QAAQ,gDAAgD,WAAW,CAEnE,QAAQ,+BAA+B,SAAS,CAEhD,QAAQ,mCAAmC,OAAO,CAClD,QAAQ,iCAAiC,iBAAiB,CAE1D,QAAQ,YAAY,GAAG,CAEvB,QAAQ,UAAU,IAAI,CACtB,QAAQ,SAAS,IAAI,CACrB,QAAQ,SAAS,IAAI,CACrB,QAAQ,WAAW,KAAI,CACvB,QAAQ,UAAU,IAAI,CACtB,QAAQ,WAAW,IAAI,CAEvB,QAAQ,WAAW,OAAO,CAC1B,MAAM;;;;;;ACzHb,eAAsB,QAAQ,MAAiC;AAC7D,QAAO,IAAI,SAAS,SAAS,WAAW;AACtC,WAAS,OAAO,MAAM;GAAE,UAAU;GAAS,SAAS;GAAQ,GAAG,KAAK,QAAQ,WAAW;AACrF,OAAI,KAAK;IACP,MAAM,MAAM,QAAQ,MAAM,IAAI,IAAI;AAClC,2BAAO,IAAI,MAAM,OAAO,KAAK,GAAG,WAAW,MAAM,CAAC;SAElD,SAAQ,OAAO;IAEjB;GACF;;;AAIJ,eAAsB,iBAAmC;AACvD,KAAI;AACF,QAAM,QAAQ,CAAC,SAAS,CAAC;AACzB,SAAO;SACD;AACN,SAAO;;;;;;;;;;;;;;;ACLX,eAAsB,OACpB,OACA,aAAqB,SACrB,IAAY,GACZ,OAAmB,SACM;AACzB,KAAI,CAAE,MAAM,gBAAgB,CAC1B,OAAM,IAAI,MACR,iFACD;CAaH,IAAI,UAAU,mBAVC,MAAM,QAAQ;EAC3B;EACA;EACA;EACA;EACA;EACA;EACA,OAAO,EAAE;EACV,CAAC,CAEsC;AAGxC,KAAI,SAAS,YAAY,QAAQ,WAAW,KAAK,OAAO,MAAM,CAC5D,WAAU,MAAM,aAAa,OAAO,YAAY,EAAE;AAGpD,QAAO;;;AAIT,SAAS,mBAAmB,QAAgC;AAC1D,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,OAAO;AACjC,MAAI,CAAC,MAAM,QAAQ,OAAO,CAAE,QAAO,EAAE;AACrC,SAAO,OAAO,KAAK,UAAmC;GACpD,MAAO,KAAK,QAAmB;GAC/B,OAAQ,KAAK,SAAoB;GACjC,SACG,KAAK,WACL,KAAK,SAAoB,MAAM,GAAG,IAAI,IACvC;GACF,OAAO,KAAK;GACb,EAAE;SACG;AACN,SAAO,EAAE;;;;AAKb,SAAS,OAAO,MAAuB;AACrC,QAAO,4CAA4C,KAAK,KAAK;;;;;;;AAQ/D,eAAe,aACb,OACA,YACA,GACyB;CACzB,MAAM,UAAU,eAAe,QAAQ,WAAW,GAAG,aAAa;CAGlE,MAAM,SAAS,MAAM,IAAI,SAAiB,YAAY;AACpD,WACE,QACA;GAAC;GAAO;GAAkB;GAAO;GAAQ,EACzC;GAAE,UAAU;GAAS,SAAS;GAAO,GACpC,MAAM,QAAQ;AAEb,WAAQ,OAAO,GAAG;IAErB;GACD;AAEF,KAAI,CAAC,OAAO,MAAM,CAAE,QAAO,EAAE;CAE7B,MAAM,QAAQ,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,MAAM,GAAG,EAAE;CACnD,MAAM,UAA0B,EAAE;AAElC,MAAK,MAAM,YAAY,OAAO;AAC5B,MAAI,CAAC,SAAU;EAWf,MAAM,WATU,MAAM,IAAI,SAAiB,YAAY;AACrD,YACE,QACA;IAAC;IAAO;IAAO;IAAO;IAAS,EAC/B;IAAE,UAAU;IAAS,SAAS;IAAO,GACpC,MAAM,QAAQ,QAAQ,OAAO,GAAG,CAClC;IACD,EAEsB,QAAQ,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI;EAChE,MAAM,WAAW,KAAK,SAAS,SAAS,SAAS;EAEjD,MAAM,YAAY,MAAM,IAAI,SAAiB,YAAY;AACvD,YACE,QACA;IAAC;IAAO;IAAO;IAAS,EACxB;IAAE,UAAU;IAAS,SAAS;IAAO,GACpC,MAAM,QAAQ,QAAQ,KAAK,QAAQ,SAAS,GAAG,CAAC,MAAM,IAAI,GAAG,CAC/D;IACD;AAEF,UAAQ,KAAK;GACX,MAAM,SAAS,WAAW,GAAG;GAC7B,OAAO,aAAa,KAAK,SAAS,UAAU,MAAM;GAClD;GACA,OAAO;GACR,CAAC;;AAGJ,QAAO;;;AAIT,eAAsB,cAA6B;AACjD,OAAM,QAAQ,CAAC,SAAS,CAAC;AACzB,OAAM,QAAQ,CAAC,QAAQ,CAAC;;;;;ACvI1B,IAAI,eAAiC;AACrC,IAAI,oBAAmC;AAEvC,eAAe,YAAgC;AAC7C,KAAI,CAAC,aACH,gBAAe,MAAM,YAAY;AAEnC,QAAO;;;AAIT,eAAe,iBAAkC;AAC/C,KAAI,sBAAsB,KAAM,QAAO;AACvC,KAAI;AACF,sBAAoB,MAAM,GAAG,SAAS,oBAAoB,EAAE,QAAQ;SAC9D;AACN,sBAAoB;;AAEtB,QAAO;;;AAIT,eAAsB,kBAAmC;CACvD,MAAM,SAAS,MAAM,WAAW;CAChC,MAAM,SAAS,QAAQ,IAAI,OAAO,IAAI;AACtC,KAAI,CAAC,OACH,OAAM,IAAI,MACR,wBAAwB,OAAO,IAAI,UAAU,uBAC9C;AAEH,QAAO,IAAI,OAAO;EAChB;EACA,SAAS,OAAO,IAAI,WAAW;EAChC,CAAC;;;;;;;AAQJ,eAAsB,QACpB,QACA,QACA,OACiB;CACjB,MAAM,SAAS,MAAM,WAAW;CAChC,MAAM,SAAS,MAAM,iBAAiB;CACtC,MAAM,YAAY,SAAS,OAAO,IAAI;CAGtC,MAAM,QAAQ,MAAM,gBAAgB;CACpC,MAAM,aAAa,QACf,GAAG,OAAO,qDAAqD,UAC/D;AAEJ,MAAK,IAAI,UAAU,GAAG,UAAU,GAAG,UACjC,KAAI;AASF,UARiB,MAAM,OAAO,KAAK,YAAY,OAAO;GACpD,OAAO;GACP,UAAU,CACR;IAAE,MAAM;IAAU,SAAS;IAAY,EACvC;IAAE,MAAM;IAAQ,SAAS;IAAQ,CAClC;GACD,aAAa;GACd,CAAC,EACc,QAAQ,IAAI,SAAS,WAAW;UACzC,KAAK;AACZ,MAAI,YAAY,GAAG;AAEjB,SAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAK,CAAC;AAC7C;;AAEF,QAAM;;AAGV,QAAO;;;;;;ACjFT,SAAgB,qBAA6B;AAC3C,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCT,SAAgB,iBACd,YACA,YACQ;AAMR,QAAO;;;EAGP,WAAW;;EAPT,WAAW,SAAS,IAChB,4BAA4B,WAAW,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,KAAK,KACtE,iCAOG;;;;;;;;ACnDX,eAAsB,cACpB,YACA,YACuB;CACvB,MAAM,SAAS,oBAAoB;CAGnC,MAAM,WAAW,MAAM,QAFR,iBAAiB,YAAY,WAAW,EAEhB,OAAO;AAE9C,KAAI;EAEF,MAAM,UAAU,SAAS,QAAQ,gBAAgB,GAAG,CAAC,QAAQ,QAAQ,GAAG,CAAC,MAAM;EAC/E,MAAM,MAAM,KAAK,MAAM,QAAQ;EAG/B,IAAI,UAAyB,EAAE;AAC/B,MAAI,MAAM,QAAQ,IAAI,QAAQ,CAC5B,WAAW,IAAI,QACZ,QAAQ,MAAM,OAAO,EAAE,eAAe,YAAY,OAAO,EAAE,YAAY,SAAS,CAChF,KAAK,OAAO;GACX,YAAY,EAAE;GACd,SAAS,EAAE;GACZ,EAAE;AAGP,MAAI,QAAQ,WAAW,KAAK,OAAO,IAAI,eAAe,YAAY,IAAI,WACpE,WAAU,CAAC;GAAE,YAAY,IAAI;GAAY,SAAS;GAAwB,CAAC;AAG7E,SAAO;GACL,UAAU,QAAQ,IAAI,SAAS;GAC/B;GACA,QAAS,IAAI,UAAqB;GACnC;SACK;AAEN,SAAO;GACL,UAAU;GACV,SAAS,EAAE;GACX,QAAQ,oCAAoC,SAAS,MAAM,GAAG,IAAI;GACnE;;;;;;;AC5CL,SAAgB,sBAA8B;AAC5C,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCT,SAAgB,kBACd,YACA,aACA,sBACA,kBACQ;AASR,QAAO;;wBAEe,YAAY;EAClC,WAAW;;;EAXU,uBACjB,kCAAkC,qBAAqB,8BACvD,qFAYS;EAVO,mBAChB,0CAA0C,iBAAiB,gDAC3D,GASQ;;;;;;;;;;;;AC9Cd,eAAsB,gBACpB,YACA,aACA,YACA,kBACiB;CACjB,MAAM,WAAW,aAAa;CAG9B,MAAM,eAAe,WAAW,QAAQ,YAAY,GAAG;CACvD,MAAM,WAAW,KAAK,KAAK,UAAU,aAAa;CAGlD,IAAI,kBAAiC;AACrC,KAAI;AACF,oBAAkB,MAAM,GAAG,SAAS,UAAU,QAAQ;SAChD;CAIR,MAAM,SAAS,qBAAqB;CAGpC,MAAM,iBAAiB,MAAM,QAFd,kBAAkB,YAAY,aAAa,iBAAiB,iBAAiB,EAE/C,OAAO;AAGpD,OAAM,GAAG,MAAM,KAAK,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AAG3D,OAAM,GAAG,UAAU,UAAU,eAAe,MAAM,GAAG,MAAM,QAAQ;AAEnE,QAAO;;;;;;ACxBT,eAAe,iBAAoC;CACjD,MAAM,WAAW,aAAa;CAC9B,MAAM,QAAkB,EAAE;CAE1B,eAAe,KAAK,KAA4B;AAC9C,MAAI;GACF,MAAM,UAAU,MAAM,GAAG,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;AAC9D,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK;AAC3C,QAAI,MAAM,aAAa,CACrB,OAAM,KAAK,SAAS;aACX,MAAM,KAAK,SAAS,MAAM,CACnC,OAAM,KAAK,KAAK,SAAS,UAAU,SAAS,CAAC;;UAG3C;;AAKV,OAAM,KAAK,SAAS;AACpB,QAAO;;;AAIT,eAAsB,gBACpB,UAAqD,EAAE,EAC/B;CACxB,MAAM,SAAwB;EAC5B,WAAW;EACX,UAAU;EACV,WAAW;EACX,QAAQ,EAAE;EACX;CAGD,IAAI;AACJ,KAAI,QAAQ,WACV,gBAAe,CAAC,QAAQ,WAAW;KAEnC,gBAAe,MAAM,aAAa;AAGpC,KAAI,aAAa,WAAW,GAAG;AAC7B,OAAa,mCAAmC;AAChD,SAAO;;AAGT,MAAa,SAAS,aAAa,OAAO,8BAA8B;CAIxE,MAAM,kBADa,MAAM,gBAAgB,EACP,KAAK,MAAM,SAAS,IAAI;AAE1D,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,OAAOA,QAAgB,cAAc,KAAK,SAAS,SAAS,CAAC,KAAK;AAExE,MAAI;GAEF,MAAM,iBAAiB,MAAM,GAAG,SAAS,UAAU,QAAQ;GAC3D,MAAM,EAAE,YAAY,iBAAiC,eAAe;GAGpE,MAAM,SAAS,MAAM,cAAc,SAAS,eAAe;AAC3D,UAAO;AAEP,OAAI,CAAC,OAAO,YAAY,OAAO,QAAQ,WAAW,GAAG;AACnD,SAAK,QAAQ,cAAc,KAAK,SAAS,SAAS,CAAC,KAAK,OAAO,SAAS;AACxE,WAAO;AAEP,QAAI,CAAC,QAAQ,QAAQ;KACnB,MAAM,UAAU,qBAAqB,gBAAgB,EACnD,QAAQ,aACT,CAAC;AACF,WAAM,GAAG,UAAU,UAAU,SAAS,QAAQ;;AAEhD;;GAIF,MAAM,UAAU,OAAO,QAAQ,KAAK,MAAM,EAAE,WAAW;AAEvD,OAAI,QAAQ,QAAQ;AAClB,SAAK,QACH,2BAA2B,KAAK,SAAS,SAAS,CAAC,KAAK,QAAQ,KAAK,KAAK,GAC3E;AACD,WAAO;AACP;;GAGF,MAAM,cAAwB,EAAE;AAChC,QAAK,MAAM,SAAS,OAAO,SAAS;IAClC,MAAM,aAAa,MAAM,cAAc;IACvC,MAAM,YAAY,MAAM,gBAAgB,SAAS,UAAU,YAAY,MAAM,QAAQ;AACrF,gBAAY,KAAK,UAAU;AAG3B,QAAI,CAAC,eAAe,SAAS,WAAW,CACtC,gBAAe,KAAK,WAAW;;AAInC,UAAO;GAMP,MAAM,UAAU,qBAAqB,gBAAgB;IACnD,QAAQ;IACR,cALkB,YACjB,KAAK,MAAM,KAAK,SAAS,KAAK,QAAQ,SAAS,CAAC,QAAQ,aAAa,OAAO,EAAE,EAAE,CAAC,CACjF,KAAK,KAAK;IAIZ,CAAC;AACF,SAAM,GAAG,UAAU,UAAU,SAAS,QAAQ;AAE9C,QAAK,QACH,aAAa,KAAK,SAAS,SAAS,CAAC,KAAK,QAAQ,KAAK,KAAK,CAAC,IAAI,OAAO,QAAQ,OAAO,WACxF;WACM,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,QAAK,KAAK,oBAAoB,KAAK,SAAS,SAAS,CAAC,IAAI,MAAM;AAChE,UAAO,OAAO,KAAK,GAAG,SAAS,IAAI,MAAM;;;AAI7C,QAAO;;;;;;AC7IT,SAAgB,uBAA+B;AAC7C,QAAO;;;;;;;;;;;;;;;;;;;;AAqBT,SAAgB,mBACd,aACA,eACA,UACQ;AACR,QAAO,uCAAuC,YAAY;UAClD,SAAS;;;uBAGI,YAAY;;;;;;;;EAQjC,cAAc;;;yCAGyB,SAAS;;;;;;;AC9BlD,eAAe,OAAO,KAAgC;CACpD,MAAM,UAAoB,EAAE;AAC5B,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;AAC9D,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,OAAO,KAAK,KAAK,KAAK,MAAM,KAAK;AACvC,OAAI,MAAM,aAAa,CACrB,SAAQ,KAAK,GAAI,MAAM,OAAO,KAAK,CAAE;YAC5B,MAAM,KAAK,SAAS,MAAM,CACnC,SAAQ,KAAK,KAAK;;SAGhB;AAGR,QAAO;;;AAIT,eAAe,oBAAoB,OAAkC;CACnE,MAAM,WAAW,aAAa;CAC9B,MAAM,WAAW,MAAM,OAAO,SAAS;CACvC,MAAM,UAAoB,EAAE;AAE5B,MAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,WAAW,WAAW,KAAK,SAAS,UAAU,KAAK;AAEzD,OAAK,MAAM,WAAW,MACpB,KAAI,UAAU,UAAU,QAAQ,EAAE;AAChC,WAAQ,KAAK,KAAK;AAClB;;;CAMN,MAAM,WAAqB,EAAE;AAC7B,MAAK,MAAM,QAAQ,QACjB,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,SAAS,MAAM,QAAQ;AAChD,WAAS,KAAK,OAAO,KAAK,SAAS,UAAU,KAAK,CAAC,QAAQ,UAAU;SAC/D;AAKV,QAAO,SAAS,KAAK,OAAO;;;AAI9B,eAAsB,gBAAgB,aAAsC;CAE1E,MAAM,cADW,MAAM,cAAc,EACT,SAAS;AAErC,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,YAAY,YAAY,+BAA+B;CAGzE,MAAM,eAAe,MAAM,oBAAoB,WAAW,MAAM;AAEhE,KAAI,CAAC,aAAa,MAAM,CACtB,OAAM,IAAI,MACR,uCAAuC,YAAY,6BACpD;CAGH,MAAM,SAAS,sBAAsB;CAOrC,MAAM,UAAU,MAAM,QANP,mBACb,aACA,cACA,WAAW,SACZ,EAEqC,OAAO;CAG7C,MAAM,YAAY,cAAc;AAChC,OAAM,GAAG,MAAM,WAAW,EAAE,WAAW,MAAM,CAAC;CAC9C,MAAM,UAAU,KAAK,KAAK,WAAW,GAAG,YAAY,KAAK;AACzD,OAAM,GAAG,UAAU,SAAS,QAAQ,MAAM,GAAG,MAAM,QAAQ;AAE3D,QAAO;;;AAIT,eAAsB,cAAiC;CACrD,MAAM,WAAW,MAAM,cAAc;CACrC,MAAM,eAAe,OAAO,KAAK,SAAS,SAAS;AAEnD,KAAI,aAAa,WAAW,GAAG;AAC7B,OAAa,wCAAwC;AACrD,SAAO,EAAE;;CAGX,MAAM,UAAoB,EAAE;AAC5B,MAAK,MAAM,QAAQ,cAAc;EAC/B,MAAM,OAAOC,QAAgB,uBAAuB,KAAK,KAAK;AAC9D,MAAI;GACF,MAAM,UAAU,MAAM,gBAAgB,KAAK;AAC3C,QAAK,QAAQ,aAAa,KAAK,KAAK,UAAU;AAC9C,WAAQ,KAAK,QAAQ;WACd,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,QAAK,KAAK,sBAAsB,KAAK,IAAI,MAAM;;;AAInD,QAAO;;;;;AC1GT,MAAM,mBAAiC;CACrC;EACE,SAAS;EACT,YAAY,CAAC,oBAAoB,iBAAiB;EACnD;CACD;EACE,SAAS;EACT,YAAY,CAAC,mBAAmB,kBAAkB;EACnD;CACD;EACE,SAAS;EACT,YAAY,CAAC,gBAAgB,eAAe;EAC7C;CACD;EACE,SAAS;EACT,YAAY,CAAC,mBAAmB,eAAe;EAChD;CACD;EACE,SAAS;EACT,YAAY;GAAC;GAAqB;GAAmB;GAAqB;EAC3E;CACD;EACE,SAAS;EACT,YAAY,CAAC,kBAAkB;EAChC;CACD;EACE,SAAS;EACT,YAAY,CAAC,oBAAoB,oBAAoB;EACtD;CACF;;;;;AAMD,SAAgB,eAAe,SAAoC;CACjE,MAAM,wBAAQ,IAAI,KAA8B;AAChD,MAAK,MAAM,KAAK,QACd,OAAM,IAAI,EAAE,IAAI,EAAE;CAGpB,MAAM,WAAqB,CAAC,sBAAsB,GAAG;AAErD,MAAK,MAAM,WAAW,kBAAkB;EACtC,MAAM,QAAkB,EAAE;AAE1B,OAAK,MAAM,OAAO,QAAQ,YAAY;GACpC,MAAM,SAAS,MAAM,IAAI,IAAI;AAC7B,OAAI,QAAQ,SAAS,MAAM,CACzB,OAAM,KAAK,OAAO,QAAQ,MAAM,CAAC;;AAIrC,MAAI,MAAM,WAAW,EAAG;AAExB,WAAS,KAAK,MAAM,QAAQ,WAAW,GAAG;AAC1C,WAAS,KAAK,MAAM,KAAK,OAAO,CAAC;AACjC,WAAS,KAAK,GAAG;;CAInB,MAAM,aAAa,IAAI,IAAI,iBAAiB,SAAS,MAAM,EAAE,WAAW,CAAC;CACzE,MAAM,SAAmB,EAAE;AAE3B,MAAK,MAAM,KAAK,QACd,KAAI,CAAC,WAAW,IAAI,EAAE,GAAG,IAAI,EAAE,SAAS,MAAM,CAC5C,QAAO,KAAK,MAAM,EAAE,SAAS,IAAI,EAAE,QAAQ,MAAM,EAAE,GAAG;AAI1D,KAAI,OAAO,SAAS,EAClB,UAAS,KAAK,GAAG,OAAO;AAI1B,UAAS,KAAK,MAAM;AACpB,UAAS,KAAK,kCAAiB,IAAI,MAAM,EAAC,aAAa,GAAG;AAC1D,UAAS,KAAK,GAAG;AAEjB,QAAO,SAAS,KAAK,KAAK;;;;;;;;;;ACvF5B,MAAM,mBACJ;;AAGF,MAAM,mBACJ;;;;;AAMF,SAAgB,mBAAmB,SAAyB;AAC1D,QAAO,QACJ,MAAM,KAAK,CACX,KAAK,SAAS;AACb,MAAI,iBAAiB,KAAK,KAAK,IAAI,iBAAiB,KAAK,KAAK,CAC5D,QAAO;AAET,SAAO;GACP,CACD,KAAK,KAAK;;;;;;AAOf,SAAgB,oBAAoB,SAAyB;CAC3D,MAAM,QAAQ,QAAQ,MAAM,KAAK;CACjC,MAAM,SAAmB,EAAE;CAC3B,IAAI,sBAAsB;AAE1B,MAAK,MAAM,QAAQ,OAAO;AAExB,MAAI,oBAAoB,KAAK,KAAK,EAAE;AAClC,yBAAsB;AACtB,UAAO,KAAK,mDAAmD;AAC/D;;AAGF,MAAI,uBAAuB,SAAS,KAAK,KAAK,CAC5C,uBAAsB;AAExB,MAAI,CAAC,oBACH,QAAO,KAAK,KAAK;;AAIrB,QAAO,OAAO,KAAK,KAAK;;;;;;AAO1B,SAAgB,sBAAsB,SAAyB;AAC7D,QAAO,QACJ,MAAM,KAAK,CACX,KAAK,SAAS;AACb,MAAI,sBAAsB,KAAK,KAAK,CAClC,QAAO;AAET,SAAO;GACP,CACD,KAAK,KAAK;;;;;;;;;;;AChDf,eAAe,KAAK,KAAa,MAAiC;AAChE,QAAO,IAAI,SAAS,YAAY;AAC9B,WAAS,KAAK,MAAM;GAAE,UAAU;GAAS,SAAS;GAAQ,GAAG,KAAK,WAAW;AAC3E,WAAQ,MAAM,KAAK,OAAO,MAAM,CAAC;IACjC;GACF;;;AAIJ,eAAe,SAAS,UAAmC;AACzD,KAAI;AACF,SAAO,MAAM,GAAG,SAAS,UAAU,QAAQ;SACrC;AACN,SAAO;;;;AAKX,eAAe,OAAO,GAA6B;AACjD,KAAI;AACF,QAAM,GAAG,OAAO,EAAE;AAClB,SAAO;SACD;AACN,SAAO;;;;AAKX,eAAe,UAAU,QAAiC;AACxD,QAAO,KAAK,aAAa,CAAC,MAAM,OAAO,CAAC;;;AAI1C,eAAe,aAAa,GAAG,MAAiC;AAC9D,QAAO,KAAK,YAAY,CAAC,QAAQ,GAAG,KAAK,CAAC;;AAG5C,MAAM,OAAO,GAAG,SAAS;AAMzB,eAAsB,yBAAmD;CACvE,MAAM,QAAkB,CAAC,mBAAmB;CAG5C,MAAM,WAAW,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC;AAC1C,KAAI,SAAU,OAAM,KAAK,qBAAqB,WAAW;CAIzD,MAAM,aADW,MAAM,KAAK,QAAQ;EAAC;EAAK;EAAS,UAAU;EAAY;EAAW,CAAC,EAC1D,MAAM,KAAK,CAAC,MAAM,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,SAAS,WAAW,CAAC;AACvF,KAAI,WAAW,MAAM,CAAE,OAAM,KAAK,wBAAwB,UAAU,MAAM,GAAG;CAG7E,MAAM,WAAW,MAAM,aAAa,mBAAmB;CACvD,MAAM,eAAe,SAAS,MAAM,+BAA+B;CACnE,MAAM,mBAAmB,SAAS,MAAM,iCAAiC;AACzE,KAAI,iBAAkB,OAAM,KAAK,4BAA4B,iBAAiB,GAAG,MAAM,GAAG;AAC1F,KAAI,aAAc,OAAM,KAAK,eAAe,aAAa,GAAG,MAAM,GAAG;CAGrE,MAAM,eAAe,MAAM,KAAK,UAAU,CAAC,SAAS,eAAe,CAAC;AACpE,KAAI,aAAc,OAAM,KAAK,oBAAoB,eAAe;CAGhE,MAAM,UAAU,MAAM,KAAK,OAAO;EAAC;EAAU;EAAY;EAAY,CAAC;CACtE,MAAM,WAAW,MAAM,KAAK,OAAO;EAAC;EAAU;EAAY;EAAa,CAAC;AACxE,KAAI,QAAS,OAAM,KAAK,eAAe,UAAU;AACjD,KAAI,SAAU,OAAM,KAAK,gBAAgB,WAAW;AAGpD,OAAM,KAAK,IAAI,uBAAuB;CAEtC,MAAM,YADQ,MAAM,aAAa,kBAAkB,iBAAiB,EAC7C,MAAM,aAAa,EAAE,KAAK,MAAM,EAAE,QAAQ,MAAM,GAAG,CAAC,IAAI,EAAE;AACjF,KAAI,SAAS,SAAS,EAAG,OAAM,KAAK,gBAAgB,SAAS,KAAK,KAAK,GAAG;CAE1E,MAAM,SAAS,MAAM,aAAa,kBAAkB,cAAc;AAClE,KAAI,OAAQ,OAAM,KAAK,aAAa,SAAS;CAG7C,MAAM,eAAe,MAAM,aAAa,uBAAuB,4BAA4B;CAC3F,MAAM,eAAe,aAAa,MAAM,gCAAgC,IAAI,EAAE;CAC9E,MAAM,YAAY,aAAa,MAAM,+BAA+B,IAAI,EAAE;CAC1E,MAAM,SAAS,CAAC,GAAG,cAAc,GAAG,UAAU,CAC3C,KAAK,MAAM,EAAE,QAAQ,gBAAgB,KAAK,CAAC,CAC3C,QAAQ,MAAM,CAAC,EAAE,SAAS,eAAe,CAAC;AAC7C,KAAI,OAAO,SAAS,EAAG,OAAM,KAAK,oBAAoB,OAAO,KAAK,KAAK,GAAG;CAG1E,MAAM,aAAa,MAAM,aAAa,MAAM,sBAAsB;AAClE,OAAM,KAAK,wBAAwB,cAAc,UAAU;CAG3D,MAAM,KAAK,KAAK,gBAAgB,CAAC,iBAAiB,CAAC;AACnD,OAAM,KAAK,eAAe,KAAK;AAE/B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,KAAK,KAAK;EAC1B;;AAOH,eAAsB,yBAAmD;CACvE,IAAI,WAAqB,EAAE;CAC3B,MAAM,MAAM,MAAM,UAChB,8DACD;AACD,KAAI,IACF,YAAW,IAAI,MAAM,KAAK,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ;CAGjE,MAAM,QAAQ;EACZ;EACA;EACA,oBAAoB,SAAS;EAC7B;EACA,GAAG,SAAS,KAAK,SAAS,KAAK,OAAO;EACvC;AAED,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,SAAS,SAAS,IACvB,MAAM,KAAK,KAAK,GAChB;EACL;;AAOH,eAAsB,0BAAoD;CACxE,MAAM,QAAkB,EAAE;CAG1B,MAAM,UAAU,KAAK,KAAK,MAAM,YAAY;AAC5C,KAAI;EAEF,MAAM,QADU,MAAM,GAAG,QAAQ,SAAS,EAAE,eAAe,MAAM,CAAC,EAC7C,QAAQ,MAAM,EAAE,aAAa,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK;AACtE,QAAM,KAAK,wBAAwB,IAAI,GAAG,KAAK,KAAK,MAAM,KAAK,IAAI,CAAC;SAC9D;AACN,QAAM,KAAK,wBAAwB,IAAI,mBAAmB;;CAI5D,MAAM,aAAa,KAAK,KAAK,MAAM,UAAU;AAC7C,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,QAAQ,WAAW;AAC5C,QAAM,KAAK,IAAI,oBAAoB,IAAI,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC,KAAK,MAAM,KAAK,IAAI,CAAC;AACpF,MAAI,QAAQ,SAAS,GAAI,OAAM,KAAK,aAAa,QAAQ,SAAS,GAAG,OAAO;SACtE;AACN,QAAM,KAAK,IAAI,oBAAoB,IAAI,mBAAmB;;CAI5D,MAAM,QAAQ,KAAK,KAAK,MAAM,YAAY;AAC1C,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,QAAQ,MAAM;AAEvC,QAAM,KACJ,IACA,2CACA,IACA,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC,KAAK,MAAM,KAAK,IAAI,CAC7C;SACK;AACN,QAAM,KAAK,IAAI,uBAAuB,IAAI,mBAAmB;;AAG/D,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,KAAK,KAAK;EAC1B;;AAOH,eAAsB,wBAAkD;CACtE,MAAM,QAAkB,CAAC,sBAAsB;AAW/C,MAAK,MAAM,CAAC,MAAM,KAAK,SARwB;EAC7C;GAAC;GAAW;GAAQ,CAAC,YAAY;GAAC;EAClC;GAAC;GAAU;GAAW,CAAC,YAAY;GAAC;EACpC;GAAC;GAAM;GAAM,CAAC,UAAU;GAAC;EACzB;GAAC;GAAQ;GAAS,CAAC,YAAY;GAAC;EAChC;GAAC;GAAQ;GAAQ,CAAC,YAAY;GAAC;EAChC,EAEyC;EACxC,MAAM,MAAM,MAAM,KAAK,KAAK,KAAK;AACjC,MAAI,KAAK;GAEP,MAAM,QAAQ,IAAI,MAAM,KAAK,CAAC,MAAM;AACpC,SAAM,KAAK,KAAK,KAAK,IAAI,QAAQ;;;AAKrC,OAAM,KAAK,IAAI,sBAAsB;AAErC,MAAK,MAAM,MADK;EAAC;EAAQ;EAAQ;EAAO;EAAQ;EAAQ,EAC9B;EACxB,MAAM,QAAQ,MAAM,KAAK,SAAS,CAAC,GAAG,CAAC;AACvC,MAAI,MAAO,OAAM,KAAK,KAAK,GAAG,IAAI,QAAQ;;CAI5C,MAAM,SAAS,MAAM,KAAK,UAAU,CAAC,YAAY,CAAC;AAClD,KAAI,OAAQ,OAAM,KAAK,IAAI,aAAa,IAAI,KAAK,SAAS;AAG1D,OAAM,KAAK,IAAI,WAAW;CAC1B,MAAM,QAAQ,QAAQ,IAAI,SAAS;AACnC,OAAM,KAAK,YAAY,QAAQ;CAC/B,MAAM,QAAQ,MAAM,SAAS,KAAK,KAAK,MAAM,SAAS,CAAC;CACvD,MAAM,aAAa,MAAM,MAAM,0BAA0B;CACzD,MAAM,eAAe,MAAM,MAAM,wBAAwB;AACzD,KAAI,WAAY,OAAM,KAAK,sBAAsB,WAAW,KAAK;AACjE,KAAI,aAAc,OAAM,KAAK,wBAAwB,aAAa,GAAG,MAAM,GAAG;CAG9E,MAAM,YAAY,MAAM,KAAK,OAAO;EAAC;EAAQ;EAAM;EAAY,CAAC;AAChE,KAAI,WAAW;EACb,MAAM,OAAO,UACV,MAAM,KAAK,CACX,QAAQ,MAAM,EAAE,WAAW,IAAI,IAAI,EAAE,WAAW,IAAI,CAAC,CACrD,KAAK,MAAM,EAAE,QAAQ,eAAe,GAAG,CAAC,MAAM,CAAC,CAC/C,OAAO,QAAQ;AAClB,MAAI,KAAK,SAAS,EAChB,OAAM,KAAK,IAAI,0BAA0B,IAAI,GAAG,KAAK,KAAK,MAAM,KAAK,IAAI,CAAC;;CAK9E,MAAM,QAAQ,MAAM,KAAK,QAAQ,CAAC,QAAQ,SAAS,CAAC;AACpD,KAAI,OAAO;EACT,MAAM,WAAW,MAAM,MAAM,KAAK,CAAC,OAAO,QAAQ;AAClD,QAAM,KAAK,IAAI,qBAAqB,IAAI,GAAG,SAAS,KAAK,MAAM,KAAK,IAAI,CAAC;;AAG3E,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,KAAK,KAAK;EAC1B;;AAOH,eAAsB,wBAAkD;CACtE,MAAM,QAAkB,EAAE;CAG1B,MAAM,QAAQ,MAAM,SAAS,KAAK,KAAK,MAAM,SAAS,CAAC;AACvD,KAAI,OAAO;EACT,MAAM,YAAY,mBAAmB,MAAM;EAC3C,MAAM,UAAU,UACb,MAAM,KAAK,CACX,QAAQ,MAAM,EAAE,MAAM,cAAc,CAAC;AACxC,MAAI,QAAQ,SAAS,EACnB,OAAM,KAAK,oBAAoB,IAAI,GAAG,QAAQ;EAIhD,MAAM,YAAY,UACf,MAAM,KAAK,CACX,QAAQ,MAAM,EAAE,MAAM,oBAAoB,IAAI,CAAC,EAAE,SAAS,WAAW,CAAC;AACzE,MAAI,UAAU,SAAS,EACrB,OAAM,KAAK,IAAI,qBAAqB,IAAI,GAAG,UAAU;;CAMzD,MAAM,YAAY,MAAM,SADF,KAAK,KAAK,MAAM,aAAa,CACJ;AAC/C,KAAI,WAAW;EACb,MAAM,YAAY,oBAAoB,UAAU;AAChD,QAAM,KAAK,IAAI,iBAAiB,IAAI,OAAO,UAAU,MAAM,EAAE,MAAM;;CAIrE,MAAM,aAAa,MAAM,KAAK,OAAO;EAAC;EAAU;EAAY;EAAgB;EAAQ,CAAC;AACrF,KAAI,WACF,OAAM,KAAK,IAAI,kBAAkB,IAAI,GAAG,WAAW,MAAM,KAAK,CAAC,KAAK,MAAM,KAAK,IAAI,CAAC;CAItF,MAAM,gBAAgB,MAAM,KAAK,OAAO;EAAC;EAAU;EAAY;EAAqB,CAAC;AACrF,KAAI,cAAe,OAAM,KAAK,IAAI,0BAA0B,gBAAgB;CAG5E,MAAM,SAAS,KAAK,KAAK,MAAM,WAAW,aAAa;AACvD,KAAI;EAEF,MAAM,QADU,MAAM,GAAG,QAAQ,OAAO,EACnB,QAAQ,MAAM,CAAC,EAAE,WAAW,IAAI,IAAI,MAAM,kBAAkB;AACjF,MAAI,KAAK,SAAS,EAChB,OAAM,KAAK,IAAI,wBAAwB,IAAI,GAAG,KAAK,KAAK,MAAM,KAAK,IAAI,CAAC;SAEpE;AAIR,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,KAAK,KAAK;EAC1B;;AAOH,eAAsB,qBAA+C;CAEnE,MAAM,MAAM,MAAM,SADD,KAAK,KAAK,MAAM,eAAe,CACZ;AAEpC,KAAI,CAAC,IACH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS;EACV;CAGH,MAAM,WAAW,IAAI,MAAM,KAAK;CAChC,MAAM,eAAe,SAAS;CAG9B,MAAM,uBAAO,IAAI,KAAqB;AACtC,MAAK,MAAM,QAAQ,UAAU;EAG3B,MAAM,YADM,KAAK,QAAQ,iBAAiB,GAAG,CAAC,MAAM,CAC9B,MAAM,MAAM,CAAC;AACnC,MAAI,aAAa,UAAU,SAAS,KAAK,UAAU,SAAS,GAC1D,MAAK,IAAI,YAAY,KAAK,IAAI,UAAU,IAAI,KAAK,EAAE;;CAIvD,MAAM,QAAQ,CAAC,GAAG,KAAK,SAAS,CAAC,CAC9B,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAC3B,MAAM,GAAG,GAAG;AAUf,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAXY;GACZ,0BAA0B;GAC1B;GACA;GACA;GACA,GAAG,MAAM,KAAK,CAAC,KAAK,WAAW,KAAK,IAAI,IAAI,MAAM,QAAQ;GAC3D,CAKgB,KAAK,KAAK;EAC1B;;AAOH,eAAsB,qBAA+C;CACnE,MAAM,WAAqB,EAAE;CAG7B,MAAM,cAAc,CAClB,KAAK,KAAK,MAAM,YAAY,EAC5B,KAAK,KAAK,MAAM,WAAW,YAAY,CACxC;AAED,MAAK,MAAM,KAAK,aAAa;EAC3B,MAAM,UAAU,MAAM,SAAS,EAAE;AACjC,MAAI,SAAS;GACX,MAAM,UAAU,EAAE,QAAQ,MAAM,IAAI;AACpC,YAAS,KAAK,MAAM,WAAW,IAAI,QAAQ,MAAM,EAAE,GAAG;;;CAK1D,MAAM,SAAS,KAAK,KAAK,MAAM,WAAW,WAAW;AACrD,KAAI;EAEF,MAAM,WADU,MAAM,GAAG,QAAQ,OAAO,EAChB,QAAQ,MAAM,EAAE,SAAS,MAAM,CAAC;AACxD,MAAI,QAAQ,SAAS,GAAG;AACtB,YAAS,KAAK,6BAA6B,GAAG;AAC9C,QAAK,MAAM,KAAK,SAAS;IACvB,MAAM,UAAU,MAAM,SAAS,KAAK,KAAK,QAAQ,EAAE,CAAC;AACpD,QAAI,QACF,UAAS,KAAK,OAAO,KAAK,IAAI,QAAQ,MAAM,EAAE,GAAG;;;SAIjD;CAKR,MAAM,cAAc,MAAM,mBAAmB,GAAG;CAChD,MAAM,eAAyB,EAAE;AAEjC,MAAK,MAAM,QAAQ,YAAY,MAAM,GAAG,GAAG,CAEzC,MAAK,MAAM,YAAY;EAAC;EAAa;EAAa;EAAe,EAAE;EAEjE,MAAM,UAAU,MAAM,SADL,KAAK,KAAK,MAAM,SAAS,CACF;AACxC,MAAI,WAAW,QAAQ,SAAS,IAAI;GAClC,MAAM,WAAW,KAAK,SAAS,KAAK;AACpC,gBAAa,KACX,OAAO,SAAS,GAAG,YACnB,IACA,QAAQ,MAAM,CAAC,MAAM,GAAG,IAAK,EAC7B,GACD;;;AAKP,KAAI,aAAa,SAAS,EACxB,UAAS,KAAK,0BAA0B,IAAI,GAAG,aAAa;CAI9D,MAAM,iBAAiB,KAAK,KAAK,MAAM,WAAW,QAAQ;AAC1D,KAAI;EAEF,MAAM,WADU,MAAM,GAAG,QAAQ,eAAe,EACxB,QAAQ,MAAM,EAAE,SAAS,MAAM,IAAI,EAAE,SAAS,OAAO,CAAC;AAC9E,MAAI,QAAQ,SAAS,GAAG;AACtB,YAAS,KAAK,0BAA0B,GAAG;AAC3C,QAAK,MAAM,KAAK,SAAS;IACvB,MAAM,UAAU,MAAM,SAAS,KAAK,KAAK,gBAAgB,EAAE,CAAC;AAC5D,QAAI,QACF,UAAS,KAAK,OAAO,KAAK,IAAI,QAAQ,MAAM,CAAC,MAAM,GAAG,IAAK,EAAE,GAAG;;;SAIhE;AAIR,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,SAAS,SAAS,IACvB,SAAS,KAAK,KAAK,GACnB;EACL;;AAOH,eAAsB,wBAAkD;CACtE,MAAM,QAAkB,EAAE;CAG1B,MAAM,QAAQ,MAAM,mBAAmB,GAAG;AAC1C,KAAI,MAAM,SAAS,GAAG;AACpB,QAAM,KAAK,6CAA6C,GAAG;AAC3D,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,OAAO,KAAK,QAAQ,MAAM,IAAI;AACpC,SAAM,KAAK,KAAK,OAAO;;;CAK3B,MAAM,YAAY,MAAM,SAAS,KAAK,KAAK,MAAM,QAAQ,SAAS,CAAC;AACnE,KAAI,WAAW;EAEb,MAAM,QADY,sBAAsB,UAAU,CAE/C,MAAM,KAAK,CACX,QAAQ,MAAM,cAAc,KAAK,EAAE,IAAI,CAAC,EAAE,SAAS,IAAI,CAAC,CACxD,KAAK,MAAM,EAAE,QAAQ,gBAAgB,GAAG,CAAC,MAAM,CAAC;AACnD,MAAI,MAAM,SAAS,EACjB,OAAM,KAAK,IAAI,gBAAgB,IAAI,GAAG,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;;CAKrE,MAAM,WAAW,KAAK,KAAK,MAAM,WAAW,eAAe;AAC3D,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,QAAQ,SAAS;AAC1C,MAAI,QAAQ,SAAS,EACnB,OAAM,KAAK,IAAI,oBAAoB,IAAI,GAAG,QAAQ,KAAK,MAAM,KAAK,IAAI,CAAC;SAEnE;AAIR,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,KAAK,KAAK;EAC1B;;AAOH,eAAsB,2BAAqD;CACzE,MAAM,QAAkB,EAAE;AAG1B,KAAI;EAEF,MAAM,QADU,MAAM,GAAG,QAAQ,gBAAgB,EAE9C,QAAQ,MAAM,EAAE,SAAS,OAAO,CAAC,CACjC,KAAK,MAAM,EAAE,QAAQ,QAAQ,GAAG,CAAC;AACpC,QAAM,KAAK,6BAA6B,IAAI,GAAG,KAAK,KAAK,MAAM,KAAK,IAAI,CAAC;SACnE;AACN,QAAM,KAAK,6BAA6B,IAAI,mBAAmB;;CAIjE,MAAM,WAAW,MAAM,aAAa,kBAAkB,kBAAkB;AACxE,KAAI,UAAU;EACZ,MAAM,SAAS,CAAC,GAAG,SAAS,SAAS,oCAAoC,CAAC,CACvE,KAAK,MAAM,EAAE,GAAG,MAAM,CAAC,CACvB,OAAO,QAAQ;AAClB,MAAI,OAAO,SAAS,EAClB,OAAM,KAAK,IAAI,yBAAyB,IAAI,GAAG,OAAO,KAAK,MAAM,KAAK,IAAI,CAAC;;CAS/E,MAAM,gBAJW,MAAM,aACrB,4DACA,aACD,EAC6B,MAAM,mCAAmC;AACvE,KAAI,cAAc;EAChB,MAAM,WAAW,aAAa;EAC9B,MAAM,cAAc,SAAS,SAAS,OAAO,GACzC,mBACA,SAAS,SAAS,SAAS,GACzB,kBACA,SAAS,SAAS,UAAU,GAC1B,YACA,SAAS,SAAS,SAAS,GACzB,WACA;AACV,QAAM,KAAK,IAAI,uBAAuB,cAAc;;CAKtD,MAAM,cAAc,CAAC,IADD,MAAM,KAAK,mBAAmB,CAAC,qBAAqB,CAAC,EACrC,SAAS,sBAAsB,CAAC,CAAC,KAAK,MAAM,EAAE,GAAG,MAAM,CAAC;AAC5F,KAAI,YAAY,SAAS,EACvB,OAAM,KAAK,IAAI,wBAAwB,IAAI,GAAG,YAAY,KAAK,MAAM,KAAK,IAAI,CAAC;AAGjF,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,KAAK,KAAK;EAC1B;;AAOH,eAAsB,0BAAoD;CAUxE,MAAM,MAAM,MAAM,SATH,KAAK,KAClB,MACA,WACA,uBACA,kBACA,WACA,YACD,CAEiC;AAClC,KAAI,CAAC,IACH,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS;EACV;AAGH,KAAI;EACF,MAAM,OAAO,KAAK,MAAM,IAAI;EAC5B,MAAM,UAA4D,EAAE;EACpE,IAAI,YAAY;EAEhB,SAAS,KACP,MACA,OACM;AACN,OAAI,KAAK,SAAS,UAAU;IAC1B,MAAM,WAAY,KAAK,YAAY,EAAE;IACrC,MAAM,OAAQ,KAAK,QAAmB;AACtC,QAAI,SAAS,SAAS,EACpB,SAAQ,KAAK;KAAE;KAAM,OAAO,SAAS;KAAQ;KAAO,CAAC;AAEvD,SAAK,MAAM,SAAS,SAClB,MAAK,OAAO,QAAQ,EAAE;cAEf,KAAK,SAAS,MACvB;;EAIJ,MAAM,QAAQ,KAAK;AACnB,OAAK,MAAM,QAAQ,OAAO,OAAO,MAAM,CACrC,KAAI,QAAQ,OAAO,SAAS,YAAY,cAAe,KACrD,MAAK,MAAiC,EAAE;AAK5C,UAAQ,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AAWzC,SAAO;GACL,IAAI;GACJ,OAAO;GACP,SAZY;IACZ,oBAAoB;IACpB,kBAAkB,QAAQ;IAC1B;IACA;IACA;IACA,GAAG,QAAQ,MAAM,GAAG,GAAG,CAAC,KAAK,MAAM,KAAK,KAAK,OAAO,KAAK,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,MAAM,QAAQ;IACxG,CAKgB,KAAK,KAAK;GAC1B;SACK;AACN,SAAO;GACL,IAAI;GACJ,OAAO;GACP,SAAS;GACV;;;AAQL,eAAsB,wBAAkD;CACtE,MAAM,WAAW,KAAK,KACpB,MACA,WACA,uBACA,kBACA,WACA,UACD;AAED,KAAI,CAAE,MAAM,OAAO,SAAS,CAC1B,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS;EACV;CAIH,MAAM,QAAQ,KAAK,KAAK,GAAG,QAAQ,EAAE,oBAAoB,KAAK,KAAK,CAAC,KAAK;AACzE,KAAI;AACF,QAAM,GAAG,SAAS,UAAU,MAAM;EAalC,MAAM,SAAS,MAAM,KAAK,WAAW,CAAC,OAXxB;;;;;;;;;MASZ,MAAM,CAE2C,CAAC;AAEpD,MAAI,CAAC,OACH,QAAO;GACL,IAAI;GACJ,OAAO;GACP,SAAS;GACV;AAYH,SAAO;GACL,IAAI;GACJ,OAAO;GACP,SAZY;IACZ;IACA;IACA,GAAG,OAAO,MAAM,KAAK,CAAC,KAAK,SAAS;KAClC,MAAM,CAAC,QAAQ,UAAU,KAAK,MAAM,IAAI;AACxC,YAAO,KAAK,OAAO,IAAI,OAAO;MAC9B;IACH,CAKgB,KAAK,KAAK;GAC1B;WACO;AACR,MAAI;AACF,SAAM,GAAG,OAAO,MAAM;UAChB;;;AAUZ,eAAsB,uBAAiD;CACrE,MAAM,QAAkB,EAAE;AAI1B,KAAI,CADc,MAAM,KAAK,MAAM,CAAC,YAAY,CAAC,CAE/C,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS;EACV;AAKH,KAAI,CADe,MAAM,KAAK,MAAM,CAAC,QAAQ,SAAS,CAAC,CAErD,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS;EACV;CAIH,MAAM,WAAW,MAAM,KAAK,MAAM;EAChC;EAAO;EACP;EAAQ;EACT,CAAC;AAEF,KAAI,UAAU;EAEZ,MAAM,CAAC,OAAO,MAAM,KAAK,SAAS,UAAU,MAAM,aAAa,WAAW,WAAW,aADvE,SAAS,MAAM,IAAK;AAElC,QAAM,KAAK,qBAAqB,GAAG;AACnC,MAAI,MAAO,OAAM,KAAK,eAAe,QAAQ;AAC7C,MAAI,KAAM,OAAM,KAAK,WAAW,OAAO;AACvC,MAAI,IAAK,OAAM,KAAK,UAAU,MAAM;AACpC,MAAI,QAAS,OAAM,KAAK,cAAc,UAAU;AAChD,MAAI,SAAU,OAAM,KAAK,eAAe,WAAW;AACnD,MAAI,KAAM,OAAM,KAAK,cAAc,OAAO;AAC1C,MAAI,YAAa,OAAM,KAAK,mBAAmB,cAAc;AAC7D,MAAI,aAAa,UAAW,OAAM,KAAK,0BAA0B,UAAU,GAAG,YAAY;AAC1F,MAAI,UAAW,OAAM,KAAK,mBAAmB,YAAY;;CAI3D,MAAM,YAAY,MAAM,KAAK,MAAM;EACjC;EAAQ;EAAQ;EAAW;EAAM;EAAU;EAAW;EACtD;EACA;EAAQ;EACT,CAAC;AAEF,KAAI,WAAW;EACb,MAAM,QAAQ,UAAU,MAAM,KAAK,CAAC,OAAO,QAAQ;AACnD,MAAI,MAAM,SAAS,GAAG;AACpB,SAAM,KAAK,IAAI,yCAAyC,GAAG;AAC3D,QAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,CAAC,MAAM,MAAM,MAAM,SAAS,aAAa,KAAK,MAAM,IAAK;IAC/D,MAAM,aAAa,cAAc,SAAS,YAAY;IACtD,MAAM,UAAU,OAAO,KAAK,KAAK,KAAK;IACtC,MAAM,UAAU,OAAO,MAAM,SAAS;AACtC,UAAM,KAAK,KAAK,OAAO,QAAQ,IAAI,WAAW,GAAG,UAAU;;;;CAMjE,MAAM,YAAY,MAAM,KAAK,MAAM;EACjC;EAAO;EACP;EAAQ;EACT,CAAC;AAEF,KAAI,WAAW;EACb,MAAM,QAAQ,UAAU,MAAM,KAAK,CAAC,OAAO,QAAQ;AACnD,MAAI,MAAM,SAAS,GAAG;AACpB,SAAM,KAAK,IAAI,gDAAgD,GAAG;AAClE,SAAM,KAAK,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,KAAK,CAAC;;;AAIrD,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,SAAS,IAAI,MAAM,KAAK,KAAK,GAAG;EAChD;;AAOH,eAAsB,qBAA+C;CACnE,MAAM,QAAkB,EAAE;CAG1B,MAAM,QAAQ,MAAM,mBAAmB,GAAG;CAC1C,MAAM,+BAAe,IAAI,KAAqB;CAC9C,MAAM,iBAA2B,EAAE;AAEnC,MAAK,MAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,EAAE;EAEpC,MAAM,MAAM,MAAM,KAAK,OAAO;GAC5B;GAAM;GACN;GAAO;GAAa;GACpB;GAAe;GAAM;GACtB,CAAC;AACF,MAAI,IACF,MAAK,MAAM,OAAO,IAAI,MAAM,KAAK,CAAC,OAAO,QAAQ,EAAE;AACjD,kBAAe,KAAK,IAAI;GAExB,MAAM,OAAO,IAAI,MAAM,4DAA4D;AACnF,OAAI,MAAM;IACR,MAAM,MAAM,KAAK,GAAG,aAAa;AACjC,iBAAa,IAAI,MAAM,aAAa,IAAI,IAAI,IAAI,KAAK,EAAE;;;;AAM/D,KAAI,aAAa,OAAO,GAAG;EACzB,MAAM,SAAS,CAAC,GAAG,aAAa,SAAS,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG;AACtE,QAAM,KAAK,4CAA4C,GAAG;AAC1D,QAAM,KAAK,2BAA2B,eAAe,SAAS;AAC9D,QAAM,KAAK,IAAI,gBAAgB;AAC/B,OAAK,MAAM,CAAC,MAAM,UAAU,OAC1B,OAAM,KAAK,KAAK,KAAK,IAAI,QAAQ;;AAKrC,KAAI,eAAe,SAAS,GAAG;AAC7B,QAAM,KAAK,IAAI,sCAAsC,GAAG;EAExD,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,eAAe,CAAC,CAAC,MAAM,GAAG,GAAG;AACxD,OAAK,MAAM,OAAO,OAChB,OAAM,KAAK,KAAK,MAAM;;CAM1B,MAAM,UAAU,MAAM,SADL,KAAK,KAAK,MAAM,eAAe,CACR;AACxC,KAAI,SAAS;EAGX,MAAM,SAFY,QAAQ,MAAM,KAAK,CAEZ,MAAM,KAAK;EACpC,MAAM,0BAAU,IAAI,KAAqB;EACzC,MAAM,wBAAQ,IAAI,KAAqB;AAEvC,OAAK,MAAM,QAAQ,QAAQ;GACzB,MAAM,MAAM,KAAK,QAAQ,iBAAiB,GAAG,CAAC,MAAM;GAEpD,MAAM,UAAU,IAAI,MAAM,aAAa;AACvC,OAAI,SAAS;IACX,MAAM,OAAO,QAAQ,GAAG,MAAM,CAAC,QAAQ,MAAM,KAAK;AAClD,YAAQ,IAAI,OAAO,QAAQ,IAAI,KAAK,IAAI,KAAK,EAAE;;GAGjD,MAAM,YAAY,IAAI,MAAM,4GAA4G;AACxI,OAAI,UACF,OAAM,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,GAAG,IAAI,KAAK,EAAE;;AAI/D,MAAI,QAAQ,OAAO,GAAG;GACpB,MAAM,UAAU,CAAC,GAAG,QAAQ,SAAS,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,GAAG,EAAE;AAC9E,SAAM,KAAK,IAAI,iCAAiC,GAAG;AACnD,QAAK,MAAM,CAAC,KAAK,UAAU,QACzB,OAAM,KAAK,KAAK,IAAI,QAAQ,MAAM,IAAI,CAAC,IAAI,MAAM,GAAG;;AAIxD,MAAI,MAAM,OAAO,GAAG;GAClB,MAAM,WAAW,CAAC,GAAG,MAAM,SAAS,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG;AACjE,SAAM,KAAK,IAAI,yCAAyC,GAAG;AAC3D,QAAK,MAAM,CAAC,MAAM,UAAU,SAC1B,OAAM,KAAK,KAAK,KAAK,IAAI,MAAM,GAAG;;;AAKxC,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,SAAS,IACpB,MAAM,KAAK,KAAK,GAChB;EACL;;AAOH,eAAsB,wBAAkD;CACtE,MAAM,QAAkB,EAAE;CAG1B,MAAM,QAAQ,MAAM,SAAS,KAAK,KAAK,MAAM,SAAS,CAAC;AACvD,KAAI,OAAO;EAET,MAAM,gBAAgB,MAAM,MAAM,wBAAwB;AAC1D,MAAI,cAAe,OAAM,KAAK,mBAAmB,cAAc,GAAG,MAAM,GAAG;EAE3E,MAAM,SAAS,MAAM,MAAM,sBAAsB;AACjD,MAAI,OACF,MAAK,MAAM,KAAK,OACd,OAAM,KAAK,gBAAgB,EAAE,MAAM,IAAI,CAAC,KAAK;;CAMnD,MAAM,UAAU,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC;AAC7C,KAAI,QAAS,OAAM,KAAK,mBAAmB,UAAU;CAGrD,MAAM,SAAS,MAAM,SAAS,KAAK,KAAK,MAAM,UAAU,CAAC;AACzD,KAAI,QAAQ;EACV,MAAM,YAAY,OAAO,MAAM,wBAAwB;AACvD,MAAI,UAAW,OAAM,KAAK,sBAAsB,UAAU,GAAG,MAAM,GAAG;EACtE,MAAM,gBAAgB,OAAO,MAAM,sBAAsB;AACzD,MAAI,cAAe,OAAM,KAAK,oBAAoB,cAAc,GAAG,MAAM,GAAG;;CAI9E,MAAM,eAAe,MAAM,SAAS,KAAK,KAAK,MAAM,WAAW,cAAc,CAAC;AAC9E,KAAI,aACF,KAAI;EACF,MAAM,MAAM,KAAK,MAAM,aAAa;EACpC,MAAM,QAAQ,OAAO,KAAK,IAAI,SAAS,EAAE,CAAC;AAC1C,MAAI,MAAM,SAAS,EACjB,OAAM,KAAK,wBAAwB,MAAM,KAAK,KAAK,GAAG;SAElD;CAMV,MAAM,cAAc,MAAM,SAAS,KAAK,KAAK,MAAM,UAAU,cAAc,CAAC;AAC5E,KAAI,aAAa;EACf,MAAM,aAAa,YAAY,MAAM,yBAAyB;AAC9D,MAAI,WACF,OAAM,KAAK,uBAAuB,WAAW,KAAK,MAAM,EAAE,QAAQ,sBAAsB,GAAG,CAAC,CAAC,KAAK,KAAK,GAAG;;CAK9G,MAAM,cAAc,MAAM,KAAK,OAAO;EAAC;EAAO;EAAuB;EAAW;EAAW;EAAY;EAAO,CAAC;AAC/G,KAAI,YAAa,OAAM,KAAK,kBAAkB,cAAc;CAE5D,MAAM,aAAa,MAAM,KAAK,UAAU;EAAC;EAAU;EAAa;EAAU,CAAC;AAC3E,KAAI,cAAc,CAAC,WAAW,SAAS,QAAQ,CAAE,OAAM,KAAK,kBAAkB,aAAa;CAE3F,MAAM,aAAa,MAAM,KAAK,UAAU,CAAC,SAAS,CAAC;AACnD,KAAI,WAAY,OAAM,KAAK,aAAa,aAAa;AAErD,QAAO;EACL,IAAI;EACJ,OAAO;EACP,SAAS,MAAM,SAAS,IACpB;GAAC;GAAgC;GAAI,GAAG;GAAM,CAAC,KAAK,KAAK,GACzD;EACL;;;AAQH,eAAe,mBAAmB,MAAiC;CACjE,MAAM,SAAS,MAAM,KAAK,QAAQ;EAChC,KAAK,KAAK,MAAM,YAAY;EAC5B;EAAa;EACb;EAAS;EACT;EAAS;EACT;EAAU,IAAI;EACf,CAAC;AAEF,KAAI,CAAC,OAAQ,QAAO,EAAE;AACtB,QAAO,OACJ,MAAM,KAAK,CACX,OAAO,QAAQ,CACf,KAAK,MAAM,EAAE,QAAQ,WAAW,GAAG,CAAC;;;AAQzC,MAAa,iBAAiB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;ACjhCD,eAAsB,cAAsC;AAC1D,KAAI;AACF,SAAO,MAAM,GAAG,SAAS,gBAAgB,EAAE,QAAQ;SAC7C;AACN,SAAO;;;;AAKX,eAAsB,YAAY,SAAkC;CAClE,MAAM,cAAc,gBAAgB;AACpC,OAAM,GAAG,UAAU,aAAa,SAAS,QAAQ;AACjD,QAAO;;;;;;AAOT,eAAsB,eAAe,MAK4B;CAC/D,MAAM,UAAU,MAAM,WAAW;CAGjC,MAAM,OAAO,UAAUC,QAAgB,4BAA4B,GAAG;CAEtE,MAAM,UAAU,MAAM,QAAQ,WAC5B,eAAe,KAAK,cAAc,WAAW,CAAC,CAC/C;CAED,MAAM,UAA6B,EAAE;CACrC,MAAM,SAAmB,EAAE;AAE3B,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,IAAI,QAAQ;AAClB,MAAI,EAAE,WAAW,YACf,SAAQ,KAAK,EAAE,MAAM;OAChB;GACL,MAAM,OAAO,eAAe,GAAG;AAC/B,UAAO,KAAK,KAAK;AACjB,OAAI,QAAS,MAAa,aAAa,KAAK,WAAW,OAAO,EAAE,OAAO,GAAG;;;AAI9E,OAAM,QAAQ,WAAW,QAAQ,OAAO,YAAY,OAAO,OAAO,UAAU;AAG5E,KAAI,CAAC,MAAM,QACT,MAAK,MAAM,SAAS,QAClB,KAAI;AACF,QAAM,kBAAkB,OAAO,MAAM;SAC/B;AAUZ,QAAO;EAAE,aAFW,MAAM,YADH,eAAe,QAAQ,CACO;EAE/B;EAAS"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as info, c as warn, i as bold, n as addConnectorEntry, o as log, s as spinner, t as ALL_COLLECTORS } from "./entry.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/connectors/mac/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Run all Mac collectors and write results to raw/connector/mac/.
|
|
7
|
+
* Supports dedup: if a scan_id already exists and content is unchanged, skip.
|
|
8
|
+
*/
|
|
9
|
+
async function scanMac(opts = {}) {
|
|
10
|
+
const result = {
|
|
11
|
+
total: ALL_COLLECTORS.length,
|
|
12
|
+
created: 0,
|
|
13
|
+
updated: 0,
|
|
14
|
+
skipped: 0,
|
|
15
|
+
failed: []
|
|
16
|
+
};
|
|
17
|
+
const spin = opts.dryRun ? null : spinner("Scanning Mac system context...");
|
|
18
|
+
const settled = await Promise.allSettled(ALL_COLLECTORS.map((collector) => collector()));
|
|
19
|
+
const collected = [];
|
|
20
|
+
for (let i = 0; i < settled.length; i++) {
|
|
21
|
+
const s = settled[i];
|
|
22
|
+
if (s.status === "fulfilled") collected.push(s.value);
|
|
23
|
+
else {
|
|
24
|
+
const name = ALL_COLLECTORS[i].name;
|
|
25
|
+
result.failed.push(name);
|
|
26
|
+
warn(`Collector ${name} failed: ${String(s.reason)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (opts.dryRun) {
|
|
30
|
+
log("");
|
|
31
|
+
info(`Would scan ${collected.length} sources:`);
|
|
32
|
+
for (const c of collected) {
|
|
33
|
+
const lines = c.content.split("\n").length;
|
|
34
|
+
log(` ${bold(c.id)} — ${c.title} (${lines} lines)`);
|
|
35
|
+
}
|
|
36
|
+
if (result.failed.length > 0) warn(`${result.failed.length} collector(s) failed: ${result.failed.join(", ")}`);
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
for (const entry of collected) try {
|
|
40
|
+
const status = await addConnectorEntry("mac", entry);
|
|
41
|
+
if (status === "created") result.created++;
|
|
42
|
+
else if (status === "updated") result.updated++;
|
|
43
|
+
else result.skipped++;
|
|
44
|
+
} catch (err) {
|
|
45
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
46
|
+
warn(`Failed to write ${entry.id}: ${msg}`);
|
|
47
|
+
result.failed.push(entry.id);
|
|
48
|
+
}
|
|
49
|
+
spin?.stop();
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
export { scanMac };
|
|
55
|
+
//# sourceMappingURL=mac-C9SDXZGK.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mac-C9SDXZGK.mjs","names":["console.spinner","console.bold"],"sources":["../src/connectors/mac/index.ts"],"sourcesContent":["/**\n * Mac connector — scans local macOS environment for personalized context.\n * Orchestrates all collectors, deduplicates, and writes to raw/connector/mac/.\n */\n\nimport type { CollectorResult } from \"../../types.js\";\nimport { addConnectorEntry } from \"../../raw/add.js\";\nimport * as console from \"../../utils/console.js\";\nimport { ALL_COLLECTORS } from \"./collectors.js\";\n\nexport interface ScanMacOptions {\n /** Only print what would be scanned, don't write */\n dryRun?: boolean;\n}\n\nexport interface ScanMacResult {\n /** Total collectors executed */\n total: number;\n /** Files newly created */\n created: number;\n /** Files updated (content changed) */\n updated: number;\n /** Files skipped (content unchanged) */\n skipped: number;\n /** Collectors that failed */\n failed: string[];\n}\n\n/**\n * Run all Mac collectors and write results to raw/connector/mac/.\n * Supports dedup: if a scan_id already exists and content is unchanged, skip.\n */\nexport async function scanMac(\n opts: ScanMacOptions = {},\n): Promise<ScanMacResult> {\n const result: ScanMacResult = {\n total: ALL_COLLECTORS.length,\n created: 0,\n updated: 0,\n skipped: 0,\n failed: [],\n };\n\n const spin = opts.dryRun ? null : console.spinner(\"Scanning Mac system context...\");\n\n // Run all collectors concurrently\n const settled = await Promise.allSettled(\n ALL_COLLECTORS.map((collector) => collector()),\n );\n\n const collected: CollectorResult[] = [];\n\n for (let i = 0; i < settled.length; i++) {\n const s = settled[i];\n if (s.status === \"fulfilled\") {\n collected.push(s.value);\n } else {\n const name = ALL_COLLECTORS[i].name;\n result.failed.push(name);\n console.warn(`Collector ${name} failed: ${String(s.reason)}`);\n }\n }\n\n if (opts.dryRun) {\n console.log(\"\");\n console.info(`Would scan ${collected.length} sources:`);\n for (const c of collected) {\n const lines = c.content.split(\"\\n\").length;\n console.log(` ${console.bold(c.id)} — ${c.title} (${lines} lines)`);\n }\n if (result.failed.length > 0) {\n console.warn(`${result.failed.length} collector(s) failed: ${result.failed.join(\", \")}`);\n }\n return result;\n }\n\n // Write results to raw layer\n for (const entry of collected) {\n try {\n const status = await addConnectorEntry(\"mac\", entry);\n if (status === \"created\") result.created++;\n else if (status === \"updated\") result.updated++;\n else result.skipped++;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`Failed to write ${entry.id}: ${msg}`);\n result.failed.push(entry.id);\n }\n }\n\n spin?.stop();\n\n return result;\n}\n"],"mappings":";;;;;;;;AAgCA,eAAsB,QACpB,OAAuB,EAAE,EACD;CACxB,MAAM,SAAwB;EAC5B,OAAO,eAAe;EACtB,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ,EAAE;EACX;CAED,MAAM,OAAO,KAAK,SAAS,OAAOA,QAAgB,iCAAiC;CAGnF,MAAM,UAAU,MAAM,QAAQ,WAC5B,eAAe,KAAK,cAAc,WAAW,CAAC,CAC/C;CAED,MAAM,YAA+B,EAAE;AAEvC,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,IAAI,QAAQ;AAClB,MAAI,EAAE,WAAW,YACf,WAAU,KAAK,EAAE,MAAM;OAClB;GACL,MAAM,OAAO,eAAe,GAAG;AAC/B,UAAO,OAAO,KAAK,KAAK;AACxB,QAAa,aAAa,KAAK,WAAW,OAAO,EAAE,OAAO,GAAG;;;AAIjE,KAAI,KAAK,QAAQ;AACf,MAAY,GAAG;AACf,OAAa,cAAc,UAAU,OAAO,WAAW;AACvD,OAAK,MAAM,KAAK,WAAW;GACzB,MAAM,QAAQ,EAAE,QAAQ,MAAM,KAAK,CAAC;AACpC,OAAY,KAAKC,KAAa,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,MAAM,SAAS;;AAEtE,MAAI,OAAO,OAAO,SAAS,EACzB,MAAa,GAAG,OAAO,OAAO,OAAO,wBAAwB,OAAO,OAAO,KAAK,KAAK,GAAG;AAE1F,SAAO;;AAIT,MAAK,MAAM,SAAS,UAClB,KAAI;EACF,MAAM,SAAS,MAAM,kBAAkB,OAAO,MAAM;AACpD,MAAI,WAAW,UAAW,QAAO;WACxB,WAAW,UAAW,QAAO;MACjC,QAAO;UACL,KAAK;EACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,OAAa,mBAAmB,MAAM,GAAG,IAAI,MAAM;AACnD,SAAO,OAAO,KAAK,MAAM,GAAG;;AAIhC,OAAM,MAAM;AAEZ,QAAO"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "personal-ai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Personal AI Identity Provider — local-first AI agent identity system. One command to scan, compile your profile, and deploy to any agent.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pai": "./dist/entry.mjs"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.mjs",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/",
|
|
15
|
+
"SKILL.md",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"ai",
|
|
21
|
+
"personal-ai",
|
|
22
|
+
"agent",
|
|
23
|
+
"profile",
|
|
24
|
+
"identity",
|
|
25
|
+
"cli",
|
|
26
|
+
"local-first",
|
|
27
|
+
"cursor",
|
|
28
|
+
"claude",
|
|
29
|
+
"llm"
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/piai/personal-ai-skills-new"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"dev": "tsx src/entry.ts",
|
|
37
|
+
"build": "tsdown",
|
|
38
|
+
"lint": "oxlint src/",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"check": "pnpm lint && pnpm typecheck && pnpm test",
|
|
43
|
+
"prepublishOnly": "pnpm check && pnpm build"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=22"
|
|
47
|
+
},
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@mariozechner/pi-agent-core": "^0.52.7",
|
|
51
|
+
"@mariozechner/pi-ai": "^0.52.7",
|
|
52
|
+
"@sinclair/typebox": "^0.34.48",
|
|
53
|
+
"chalk": "^5.6.2",
|
|
54
|
+
"commander": "^14.0.3",
|
|
55
|
+
"googleapis": "^171.4.0",
|
|
56
|
+
"gray-matter": "^4.0.3",
|
|
57
|
+
"json5": "^2.2.3",
|
|
58
|
+
"minimatch": "^10.1.2",
|
|
59
|
+
"open": "^11.0.0",
|
|
60
|
+
"openai": "^6.18.0",
|
|
61
|
+
"ora": "^9.3.0",
|
|
62
|
+
"zod": "^4.3.6"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/node": "^25.2.1",
|
|
66
|
+
"oxlint": "^1.43.0",
|
|
67
|
+
"tsdown": "^0.20.3",
|
|
68
|
+
"tsx": "^4.21.0",
|
|
69
|
+
"typescript": "^5.9.3",
|
|
70
|
+
"vitest": "^4.0.18"
|
|
71
|
+
}
|
|
72
|
+
}
|