lobster-cli 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 +389 -0
- package/dist/agent/core.js +1013 -0
- package/dist/agent/core.js.map +1 -0
- package/dist/agent/index.js +1027 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/brain/index.js +60 -0
- package/dist/brain/index.js.map +1 -0
- package/dist/browser/dom/index.js +1096 -0
- package/dist/browser/dom/index.js.map +1 -0
- package/dist/browser/index.js +2034 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/manager.js +86 -0
- package/dist/browser/manager.js.map +1 -0
- package/dist/browser/page-adapter.js +1345 -0
- package/dist/browser/page-adapter.js.map +1 -0
- package/dist/cascade/index.js +138 -0
- package/dist/cascade/index.js.map +1 -0
- package/dist/config/index.js +110 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.js +66 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/discover/index.js +545 -0
- package/dist/discover/index.js.map +1 -0
- package/dist/index.js +5529 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.js +4206 -0
- package/dist/lib.js.map +1 -0
- package/dist/llm/client.js +379 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/index.js +397 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/openai-client.js +214 -0
- package/dist/llm/openai-client.js.map +1 -0
- package/dist/output/index.js +93 -0
- package/dist/output/index.js.map +1 -0
- package/dist/pipeline/index.js +802 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/router/decision.js +80 -0
- package/dist/router/decision.js.map +1 -0
- package/dist/router/index.js +3443 -0
- package/dist/router/index.js.map +1 -0
- package/dist/types/index.js +23 -0
- package/dist/types/index.js.map +1 -0
- package/logo.svg +11 -0
- package/package.json +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pipeline/template.ts","../../src/pipeline/registry.ts","../../src/pipeline/steps/fetch.ts","../../src/pipeline/steps/browser.ts","../../src/pipeline/steps/transform.ts","../../src/pipeline/steps/intercept.ts","../../src/pipeline/steps/download.ts","../../src/pipeline/steps/tap.ts","../../src/utils/logger.ts","../../src/pipeline/executor.ts"],"sourcesContent":["/**\n * Template expression engine for pipeline YAML.\n * Resolves ${{ expr }} expressions with context variables and filters.\n */\n\nconst EXPR_RE = /\\$\\{\\{\\s*(.*?)\\s*\\}\\}/g;\n\ninterface RenderContext {\n args: Record<string, unknown>;\n item?: unknown;\n data?: unknown;\n index?: number;\n}\n\nexport function renderTemplate(template: unknown, ctx: RenderContext): unknown {\n if (typeof template !== 'string') {\n if (typeof template === 'object' && template !== null) {\n if (Array.isArray(template)) return template.map((v) => renderTemplate(v, ctx));\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(template)) {\n result[k] = renderTemplate(v, ctx);\n }\n return result;\n }\n return template;\n }\n\n // Full expression: entire string is one expression — return raw value (not stringified)\n const fullMatch = template.match(/^\\$\\{\\{\\s*(.*?)\\s*\\}\\}$/);\n if (fullMatch) {\n return evaluateExpression(fullMatch[1], ctx);\n }\n\n // Partial: interpolate into string\n return template.replace(EXPR_RE, (_, expr) => {\n const val = evaluateExpression(expr, ctx);\n return val === null || val === undefined ? '' : String(val);\n });\n}\n\nfunction evaluateExpression(expr: string, ctx: RenderContext): unknown {\n // Handle pipe filters: expr | filter(arg)\n const parts = expr.split(/\\s*\\|\\s*/);\n let value = resolveValue(parts[0].trim(), ctx);\n\n for (let i = 1; i < parts.length; i++) {\n value = applyFilter(value, parts[i].trim());\n }\n\n return value;\n}\n\nfunction resolveValue(path: string, ctx: RenderContext): unknown {\n // Handle simple arithmetic: index + 1\n const arithMatch = path.match(/^(\\w[\\w.]*)\\s*([+\\-*])\\s*(\\d+)$/);\n if (arithMatch) {\n const base = Number(resolvePath(arithMatch[1], ctx));\n const op = arithMatch[2];\n const num = Number(arithMatch[3]);\n if (op === '+') return base + num;\n if (op === '-') return base - num;\n if (op === '*') return base * num;\n }\n\n // Handle logical OR: a || b\n const orMatch = path.match(/^(.+?)\\s*\\|\\|\\s*(.+)$/);\n if (orMatch) {\n const left = resolvePath(orMatch[1].trim(), ctx);\n if (left !== null && left !== undefined && left !== '' && left !== false) return left;\n // Right side: could be a string literal\n const right = orMatch[2].trim();\n if ((right.startsWith(\"'\") && right.endsWith(\"'\")) || (right.startsWith('\"') && right.endsWith('\"'))) {\n return right.slice(1, -1);\n }\n return resolvePath(right, ctx);\n }\n\n // String literal\n if ((path.startsWith(\"'\") && path.endsWith(\"'\")) || (path.startsWith('\"') && path.endsWith('\"'))) {\n return path.slice(1, -1);\n }\n\n // Number literal\n if (!isNaN(Number(path)) && path !== '') return Number(path);\n\n return resolvePath(path, ctx);\n}\n\nfunction resolvePath(path: string, ctx: RenderContext): unknown {\n // Resolve against context: args.*, item.*, data.*, index\n if (path === 'index') return ctx.index ?? 0;\n\n const parts = path.split('.');\n let root: unknown;\n\n if (parts[0] === 'args') {\n root = ctx.args;\n parts.shift();\n } else if (parts[0] === 'item') {\n root = ctx.item;\n parts.shift();\n } else if (parts[0] === 'data') {\n root = ctx.data;\n parts.shift();\n } else {\n // Try item first, then args, then data\n root = getNestedValue(ctx.item, parts);\n if (root !== undefined) return root;\n root = getNestedValue(ctx.args, parts);\n if (root !== undefined) return root;\n root = getNestedValue(ctx.data, parts);\n if (root !== undefined) return root;\n return undefined;\n }\n\n return getNestedValue(root, parts);\n}\n\nfunction getNestedValue(obj: unknown, parts: string[]): unknown {\n let current = obj;\n for (const part of parts) {\n if (current === null || current === undefined) return undefined;\n if (typeof current === 'object') {\n current = (current as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n return current;\n}\n\nfunction applyFilter(value: unknown, filter: string): unknown {\n const match = filter.match(/^(\\w+)(?:\\((.+)\\))?$/);\n if (!match) return value;\n\n const name = match[1];\n const arg = match[2]?.replace(/^['\"]|['\"]$/g, '');\n\n switch (name) {\n case 'default':\n return value === null || value === undefined || value === '' ? arg : value;\n case 'join':\n return Array.isArray(value) ? value.join(arg || ', ') : value;\n case 'upper':\n return typeof value === 'string' ? value.toUpperCase() : value;\n case 'lower':\n return typeof value === 'string' ? value.toLowerCase() : value;\n case 'trim':\n return typeof value === 'string' ? value.trim() : value;\n case 'truncate': {\n const len = parseInt(arg || '100');\n if (typeof value === 'string' && value.length > len) return value.slice(0, len) + '...';\n return value;\n }\n case 'replace': {\n if (typeof value !== 'string' || !arg) return value;\n const [from, to] = arg.split(',').map((s) => s.trim().replace(/^['\"]|['\"]$/g, ''));\n return value.replaceAll(from, to || '');\n }\n case 'keys':\n return typeof value === 'object' && value !== null ? Object.keys(value) : [];\n case 'length':\n return Array.isArray(value) ? value.length : typeof value === 'string' ? value.length : 0;\n case 'first':\n return Array.isArray(value) ? value[0] : value;\n case 'last':\n return Array.isArray(value) ? value[value.length - 1] : value;\n case 'json':\n return JSON.stringify(value);\n case 'slugify':\n return typeof value === 'string' ? value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') : value;\n case 'sanitize':\n // Filename-safe: remove/replace dangerous chars\n return typeof value === 'string' ? value.replace(/[<>:\"/\\\\|?*\\x00-\\x1f]/g, '_').replace(/__+/g, '_').trim() : value;\n case 'ext': {\n // Extract file extension from URL/path\n if (typeof value !== 'string') return '';\n const extMatch = value.match(/\\.([a-zA-Z0-9]+)(?:\\?.*)?$/);\n return extMatch ? extMatch[1] : '';\n }\n case 'basename': {\n // Extract filename from URL/path\n if (typeof value !== 'string') return '';\n try { return new URL(value).pathname.split('/').pop() || ''; } catch {}\n return value.split('/').pop() || '';\n }\n default:\n return value;\n }\n}\n","import type { StepHandler } from '../types/pipeline.js';\n\nconst stepHandlers = new Map<string, StepHandler>();\n\nexport function registerStep(name: string, handler: StepHandler): void {\n stepHandlers.set(name, handler);\n}\n\nexport function getStep(name: string): StepHandler | undefined {\n return stepHandlers.get(name);\n}\n\nexport function getStepNames(): string[] {\n return [...stepHandlers.keys()];\n}\n","/**\n * Pipeline step: fetch — HTTP API requests with batch IPC optimization.\n *\n * When data is an array and URL references `item`, all per-item fetches\n * are batched into a single browser evaluate() call — eliminating N-1\n * cross-process IPC round trips.\n */\n\nimport type { PipelineContext } from '../../types/pipeline.js';\nimport { renderTemplate } from '../template.js';\nimport { registerStep } from '../registry.js';\n\n/**\n * Batch fetch: send all URLs into the browser as a single evaluate() call.\n * All fetches execute inside V8, results return as one JSON array.\n */\nasync function fetchBatchInBrowser(\n page: NonNullable<PipelineContext['page']>,\n urls: string[],\n method: string,\n headers: Record<string, string>,\n concurrency: number,\n): Promise<unknown[]> {\n const headersJs = JSON.stringify(headers);\n const urlsJs = JSON.stringify(urls);\n\n return page.evaluate<unknown[]>(`\n (async () => {\n const urls = ${urlsJs};\n const method = ${JSON.stringify(method)};\n const headers = ${headersJs};\n const concurrency = ${concurrency};\n\n const results = new Array(urls.length);\n let idx = 0;\n\n async function worker() {\n while (idx < urls.length) {\n const i = idx++;\n try {\n const resp = await fetch(urls[i], { method, headers, credentials: \"include\" });\n results[i] = await resp.json();\n } catch (e) {\n results[i] = { error: e.message };\n }\n }\n }\n\n const workers = Array.from({ length: Math.min(concurrency, urls.length) }, () => worker());\n await Promise.all(workers);\n return results;\n })()\n `);\n}\n\n/**\n * Concurrent pool for non-browser fetches.\n */\nasync function mapConcurrent<T, R>(\n items: T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = new Array(items.length);\n let index = 0;\n\n async function worker() {\n while (index < items.length) {\n const i = index++;\n results[i] = await fn(items[i], i);\n }\n }\n\n const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());\n await Promise.all(workers);\n return results;\n}\n\n/**\n * Single URL fetch — browser or direct.\n */\nasync function fetchSingle(\n page: PipelineContext['page'],\n url: string,\n method: string,\n headers: Record<string, string>,\n): Promise<unknown> {\n if (page) {\n const headersJs = JSON.stringify(headers);\n const urlJs = JSON.stringify(url);\n const methodJs = JSON.stringify(method);\n return page.evaluate(`\n (async () => {\n const resp = await fetch(${urlJs}, {\n method: ${methodJs}, headers: ${headersJs}, credentials: \"include\"\n });\n return await resp.json();\n })()\n `);\n }\n\n const resp = await fetch(url, { method, headers });\n return resp.json();\n}\n\nregisterStep('fetch', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n const data = ctx.data;\n const urlOrObj = typeof params === 'string' ? params : ((params as any)?.url ?? '');\n const method = ((params as any)?.method as string) || 'GET';\n const rawHeaders: Record<string, unknown> = (params as any)?.headers ?? {};\n const rawParams: Record<string, unknown> = (params as any)?.params ?? {};\n const urlTemplate = String(urlOrObj);\n\n // Render headers\n const headers: Record<string, string> = {};\n for (const [k, v] of Object.entries(rawHeaders)) {\n headers[k] = String(renderTemplate(v, { args: ctx.args, data }));\n }\n\n // Per-item batch fetch when data is array and URL references `item`\n if (Array.isArray(data) && urlTemplate.includes('item')) {\n const concurrency = typeof (params as any)?.concurrency === 'number'\n ? (params as any).concurrency : 5;\n\n // Render all URLs upfront\n const renderedParams: Record<string, string> = {};\n for (const [k, v] of Object.entries(rawParams)) {\n renderedParams[k] = String(renderTemplate(v, { args: ctx.args, data }));\n }\n\n const urls = data.map((item: unknown, index: number) => {\n let url = String(renderTemplate(urlTemplate, { args: ctx.args, data, item, index }));\n if (Object.keys(renderedParams).length > 0) {\n const qs = new URLSearchParams(renderedParams).toString();\n url = `${url}${url.includes('?') ? '&' : '?'}${qs}`;\n }\n return url;\n });\n\n // BATCH IPC: if browser available, run all fetches in a single evaluate()\n if (ctx.page) {\n return fetchBatchInBrowser(ctx.page, urls, method.toUpperCase(), headers, concurrency);\n }\n\n // Non-browser: concurrent pool\n return mapConcurrent(urls, concurrency, async (url) => {\n return fetchSingle(null, url, method.toUpperCase(), headers);\n });\n }\n\n // Single URL fetch\n let url = String(renderTemplate(urlOrObj, { args: ctx.args, data }));\n\n // Append query params\n const renderedParams: Record<string, string> = {};\n for (const [k, v] of Object.entries(rawParams)) {\n renderedParams[k] = String(renderTemplate(v, { args: ctx.args, data }));\n }\n if (Object.keys(renderedParams).length > 0) {\n const qs = new URLSearchParams(renderedParams).toString();\n url = `${url}${url.includes('?') ? '&' : '?'}${qs}`;\n }\n\n return fetchSingle(ctx.page, url, method.toUpperCase(), headers);\n});\n","import type { PipelineContext } from '../../types/pipeline.js';\nimport { renderTemplate } from '../template.js';\nimport { registerStep } from '../registry.js';\n\nregisterStep('navigate', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for navigate step');\n const url = renderTemplate(params, { args: ctx.args, data: ctx.data }) as string;\n await ctx.page.goto(url);\n return ctx.data;\n});\n\nregisterStep('click', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for click step');\n const ref = renderTemplate(params, { args: ctx.args, data: ctx.data });\n await ctx.page.click(ref as string | number);\n return ctx.data;\n});\n\nregisterStep('type', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for type step');\n const p = renderTemplate(params, { args: ctx.args, data: ctx.data }) as Record<string, unknown>;\n await ctx.page.typeText(p.ref as string | number, p.text as string);\n if (p.submit) await ctx.page.pressKey('Enter');\n return ctx.data;\n});\n\nregisterStep('wait', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for wait step');\n const rendered = renderTemplate(params, { args: ctx.args, data: ctx.data });\n if (typeof rendered === 'number') {\n await ctx.page.wait(rendered);\n } else {\n await ctx.page.wait(rendered as { text?: string; time?: number; timeout?: number });\n }\n return ctx.data;\n});\n\nregisterStep('press', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for press step');\n const key = renderTemplate(params, { args: ctx.args, data: ctx.data }) as string;\n await ctx.page.pressKey(key);\n return ctx.data;\n});\n\nregisterStep('snapshot', async (ctx: PipelineContext, _params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for snapshot step');\n return ctx.page.snapshot();\n});\n\nregisterStep('evaluate', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for evaluate step');\n const js = renderTemplate(params, { args: ctx.args, data: ctx.data }) as string;\n const result = await ctx.page.evaluate(js);\n // Auto-parse JSON strings\n if (typeof result === 'string') {\n try { return JSON.parse(result); } catch { return result; }\n }\n return result;\n});\n","import type { PipelineContext } from '../../types/pipeline.js';\nimport { renderTemplate } from '../template.js';\nimport { registerStep } from '../registry.js';\n\nregisterStep('select', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n const path = renderTemplate(params, { args: ctx.args, data: ctx.data }) as string;\n const parts = path.split('.');\n let current: unknown = ctx.data;\n for (const part of parts) {\n if (current === null || current === undefined) return undefined;\n\n // Array index: items[0], data[2]\n const indexMatch = part.match(/^(\\w+)\\[(\\d+)\\]$/);\n if (indexMatch) {\n current = (current as Record<string, unknown>)[indexMatch[1]];\n if (Array.isArray(current)) current = current[Number(indexMatch[2])];\n else return undefined;\n continue;\n }\n\n // Wildcard: items[*].title → flatten array\n const wildcardMatch = part.match(/^(\\w+)\\[\\*\\]$/);\n if (wildcardMatch) {\n current = (current as Record<string, unknown>)[wildcardMatch[1]];\n if (!Array.isArray(current)) return undefined;\n // If there are more parts, map into each item\n const remaining = parts.slice(parts.indexOf(part) + 1);\n if (remaining.length > 0) {\n return current.map((item) => {\n let val: unknown = item;\n for (const r of remaining) {\n if (val === null || val === undefined) return undefined;\n val = (val as Record<string, unknown>)[r];\n }\n return val;\n });\n }\n continue;\n }\n\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n});\n\nregisterStep('map', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!Array.isArray(ctx.data)) throw new Error('map requires array data');\n const template = params as Record<string, unknown>;\n return ctx.data.map((item, index) =>\n renderTemplate(template, { args: ctx.args, item, data: ctx.data, index })\n );\n});\n\nregisterStep('filter', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!Array.isArray(ctx.data)) throw new Error('filter requires array data');\n const expr = params as string;\n return ctx.data.filter((item, index) => {\n const result = renderTemplate(`\\${{ ${expr} }}`, { args: ctx.args, item, data: ctx.data, index });\n return Boolean(result);\n });\n});\n\nregisterStep('sort', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!Array.isArray(ctx.data)) throw new Error('sort requires array data');\n const p = params as { by: string; order?: 'asc' | 'desc' };\n const sorted = [...ctx.data].sort((a, b) => {\n const va = (a as Record<string, unknown>)[p.by];\n const vb = (b as Record<string, unknown>)[p.by];\n if (typeof va === 'number' && typeof vb === 'number') return va - vb;\n return String(va).localeCompare(String(vb));\n });\n return p.order === 'desc' ? sorted.reverse() : sorted;\n});\n\nregisterStep('limit', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!Array.isArray(ctx.data)) return ctx.data;\n const n = renderTemplate(params, { args: ctx.args, data: ctx.data });\n return ctx.data.slice(0, Number(n) || 20);\n});\n","import type { PipelineContext } from '../../types/pipeline.js';\nimport { renderTemplate } from '../template.js';\nimport { registerStep } from '../registry.js';\n\nregisterStep('intercept', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for intercept step');\n const p = params as { pattern: string; trigger?: string; select?: string; timeout?: number };\n const pattern = renderTemplate(p.pattern, { args: ctx.args, data: ctx.data }) as string;\n\n await ctx.page.installInterceptor(pattern);\n\n // Trigger action\n if (p.trigger) {\n const trigger = renderTemplate(p.trigger, { args: ctx.args, data: ctx.data }) as string;\n const [action, value] = trigger.split(':');\n switch (action) {\n case 'navigate': await ctx.page.goto(value); break;\n case 'click': await ctx.page.click(value); break;\n case 'evaluate': await ctx.page.evaluate(value); break;\n case 'scroll': await ctx.page.scroll('down'); break;\n }\n }\n\n // Wait for intercepted requests\n const timeout = p.timeout || 10;\n await ctx.page.wait(timeout);\n\n const requests = await ctx.page.getInterceptedRequests();\n if (requests.length === 0) return ctx.data;\n\n let result: unknown = requests.map((r: any) => r.body);\n if (result && Array.isArray(result) && result.length === 1) result = result[0];\n\n // Optional sub-selection\n if (p.select && result) {\n const parts = p.select.split('.');\n let current: unknown = result;\n for (const part of parts) {\n if (current && typeof current === 'object') {\n current = (current as Record<string, unknown>)[part];\n }\n }\n result = current;\n }\n\n return result;\n});\n","/**\n * Pipeline step: download — files, videos, documents.\n *\n * Features:\n * - HTTP download with concurrency control\n * - yt-dlp integration for video platforms\n * - Browser cookie forwarding (Netscape format)\n * - Document extraction (HTML/Markdown/JSON)\n * - Progress tracking\n * - Skip-existing support\n * - Filename templating\n */\n\nimport { writeFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { tmpdir } from 'node:os';\nimport type { PipelineContext } from '../../types/pipeline.js';\nimport { renderTemplate } from '../template.js';\nimport { registerStep } from '../registry.js';\n\ninterface DownloadParams {\n url?: string;\n dir?: string;\n filename?: string;\n concurrency?: number;\n skip_existing?: boolean;\n timeout?: number;\n // yt-dlp options\n video?: boolean;\n format?: string;\n // Document extraction\n content?: 'html' | 'markdown' | 'json' | 'text';\n metadata?: Record<string, unknown>;\n}\n\ninterface DownloadResult {\n url: string;\n file: string;\n success: boolean;\n size?: number;\n error?: string;\n content?: string;\n}\n\nclass DownloadProgressTracker {\n private total: number;\n private completed = 0;\n private failed = 0;\n private totalBytes = 0;\n\n constructor(total: number) { this.total = total; }\n\n success(bytes: number) {\n this.completed++;\n this.totalBytes += bytes;\n }\n\n fail() {\n this.completed++;\n this.failed++;\n }\n\n summary(): string {\n return `Downloaded ${this.completed - this.failed}/${this.total} files (${formatBytes(this.totalBytes)})${this.failed > 0 ? `, ${this.failed} failed` : ''}`;\n }\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\n/**\n * Export browser cookies in Netscape format for yt-dlp.\n */\nasync function exportCookiesNetscape(ctx: PipelineContext, domain: string): Promise<string | null> {\n if (!ctx.page) return null;\n try {\n const cookies = await ctx.page.getCookies({ domain });\n if (cookies.length === 0) return null;\n\n const lines = ['# Netscape HTTP Cookie File'];\n for (const c of cookies) {\n const httpOnly = c.httpOnly ? 'TRUE' : 'FALSE';\n const secure = c.secure ? 'TRUE' : 'FALSE';\n const expires = c.expires ? Math.floor(c.expires) : 0;\n lines.push(`${c.domain || domain}\\tTRUE\\t${c.path || '/'}\\t${secure}\\t${expires}\\t${c.name}\\t${c.value}`);\n }\n\n const tmpFile = join(tmpdir(), `lobster-cookies-${Date.now()}.txt`);\n writeFileSync(tmpFile, lines.join('\\n'));\n return tmpFile;\n } catch {\n return null;\n }\n}\n\n/**\n * Check if yt-dlp is available.\n */\nfunction hasYtDlp(): boolean {\n try {\n execSync('yt-dlp --version', { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Download a video using yt-dlp.\n */\nfunction downloadWithYtDlp(url: string, dir: string, opts: {\n format?: string;\n cookieFile?: string | null;\n filename?: string;\n}): DownloadResult {\n const args = ['yt-dlp', '-o', join(dir, opts.filename || '%(title)s.%(ext)s')];\n\n if (opts.format) args.push('-f', opts.format);\n if (opts.cookieFile) args.push('--cookies', opts.cookieFile);\n args.push('--no-warnings', '--no-progress', url);\n\n try {\n execSync(args.join(' '), { stdio: 'pipe', timeout: 300000 });\n return { url, file: dir, success: true };\n } catch (err: any) {\n return { url, file: '', success: false, error: err.message?.slice(0, 200) };\n }\n}\n\n/**\n * Concurrent download worker pool.\n */\nasync function downloadPool<T>(\n items: T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<DownloadResult>,\n): Promise<DownloadResult[]> {\n const results: DownloadResult[] = new Array(items.length);\n let idx = 0;\n\n async function worker() {\n while (idx < items.length) {\n const i = idx++;\n results[i] = await fn(items[i], i);\n }\n }\n\n const workers = Array.from(\n { length: Math.min(concurrency, items.length) },\n () => worker(),\n );\n await Promise.all(workers);\n return results;\n}\n\nregisterStep('download', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n const p = params as DownloadParams;\n const dir = renderTemplate(p.dir || './downloads', { args: ctx.args, data: ctx.data }) as string;\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\n const concurrency = p.concurrency || 3;\n const skipExisting = p.skip_existing ?? false;\n const timeout = (p.timeout || 60) * 1000;\n\n // Collect URLs\n const items: { url: string; item?: unknown; index: number }[] = [];\n if (p.url) {\n items.push({\n url: renderTemplate(p.url, { args: ctx.args, data: ctx.data }) as string,\n index: 0,\n });\n } else if (Array.isArray(ctx.data)) {\n for (let i = 0; i < (ctx.data as unknown[]).length; i++) {\n const item = (ctx.data as unknown[])[i];\n const url = typeof item === 'string'\n ? item\n : (item as Record<string, unknown>).url as string;\n if (url) items.push({ url, item, index: i });\n }\n }\n\n if (items.length === 0) return [];\n\n const tracker = new DownloadProgressTracker(items.length);\n\n // ── Video download path (yt-dlp) ──\n if (p.video) {\n if (!hasYtDlp()) {\n throw new Error('yt-dlp not found. Install: brew install yt-dlp (mac) or pip install yt-dlp');\n }\n\n const cookieFile = await exportCookiesNetscape(ctx, new URL(items[0].url).hostname);\n\n const results = await downloadPool(items, Math.min(concurrency, 2), async (entry) => {\n const result = downloadWithYtDlp(entry.url, dir, {\n format: p.format,\n cookieFile,\n filename: p.filename\n ? renderTemplate(p.filename, { args: ctx.args, item: entry.item, data: ctx.data, index: entry.index }) as string\n : undefined,\n });\n if (result.success) tracker.success(0);\n else tracker.fail();\n return result;\n });\n\n // Clean up cookie file\n if (cookieFile) try { require('fs').unlinkSync(cookieFile); } catch {}\n\n if (ctx.debug) console.log(tracker.summary());\n return results;\n }\n\n // ── Document extraction path ──\n if (p.content) {\n const results = await downloadPool(items, concurrency, async (entry) => {\n try {\n const resp = await fetch(entry.url, {\n signal: AbortSignal.timeout(timeout),\n });\n if (!resp.ok) return { url: entry.url, file: '', success: false, error: `HTTP ${resp.status}` };\n\n let content: string;\n const html = await resp.text();\n\n if (p.content === 'html') {\n content = html;\n } else if (p.content === 'text') {\n // Strip tags\n content = html.replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ').trim();\n } else if (p.content === 'json') {\n try { content = JSON.stringify(JSON.parse(html), null, 2); } catch { content = html; }\n } else {\n // markdown — basic conversion\n content = html\n .replace(/<h[1-6][^>]*>(.*?)<\\/h[1-6]>/gi, '\\n## $1\\n')\n .replace(/<p[^>]*>(.*?)<\\/p>/gi, '\\n$1\\n')\n .replace(/<a[^>]*href=\"([^\"]*)\"[^>]*>(.*?)<\\/a>/gi, '[$2]($1)')\n .replace(/<[^>]+>/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n }\n\n // Save to file\n const filename = p.filename\n ? renderTemplate(p.filename, { args: ctx.args, item: entry.item, data: ctx.data, index: entry.index }) as string\n : basename(new URL(entry.url).pathname).replace(/\\.[^.]+$/, '') + (p.content === 'json' ? '.json' : '.md');\n const filepath = join(dir, filename);\n\n // Prepend metadata if provided\n if (p.metadata) {\n const meta = renderTemplate(p.metadata, { args: ctx.args, item: entry.item, data: ctx.data, index: entry.index }) as Record<string, unknown>;\n const header = Object.entries(meta).map(([k, v]) => `${k}: ${v}`).join('\\n');\n content = `---\\n${header}\\n---\\n\\n${content}`;\n }\n\n writeFileSync(filepath, content, 'utf-8');\n tracker.success(Buffer.byteLength(content));\n return { url: entry.url, file: filepath, success: true, size: Buffer.byteLength(content), content: content.slice(0, 200) };\n } catch (err: any) {\n tracker.fail();\n return { url: entry.url, file: '', success: false, error: err.message };\n }\n });\n\n if (ctx.debug) console.log(tracker.summary());\n return results;\n }\n\n // ── Standard HTTP download path ──\n const results = await downloadPool(items, concurrency, async (entry) => {\n try {\n const filename = p.filename\n ? renderTemplate(p.filename, { args: ctx.args, item: entry.item, data: ctx.data, index: entry.index }) as string\n : decodeURIComponent(basename(new URL(entry.url).pathname)) || `download-${entry.index}`;\n const filepath = join(dir, filename);\n\n if (skipExisting && existsSync(filepath)) {\n tracker.success(0);\n return { url: entry.url, file: filepath, success: true, size: 0 };\n }\n\n const resp = await fetch(entry.url, {\n signal: AbortSignal.timeout(timeout),\n });\n if (!resp.ok) {\n tracker.fail();\n return { url: entry.url, file: '', success: false, error: `HTTP ${resp.status}` };\n }\n\n const buffer = Buffer.from(await resp.arrayBuffer());\n writeFileSync(filepath, buffer);\n tracker.success(buffer.length);\n return { url: entry.url, file: filepath, success: true, size: buffer.length };\n } catch (err: any) {\n tracker.fail();\n return { url: entry.url, file: '', success: false, error: err.message };\n }\n });\n\n if (ctx.debug) console.log(tracker.summary());\n return results;\n});\n","/**\n * Pipeline step: tap — Vue store action bridge.\n *\n * Calls a Pinia/Vuex store action inside the browser and intercepts\n * the resulting network request to capture the response.\n *\n * This is the most powerful data extraction method for Vue apps — it\n * triggers the app's own data fetching logic and captures the result.\n *\n * Usage in YAML pipeline:\n * - tap:\n * store: searchStore\n * action: fetchResults\n * capture: /api/search\n * args:\n * - ${{ args.query }}\n * - page: 1\n * timeout: 5\n * select: data.items\n */\n\nimport type { PipelineContext } from '../../types/pipeline.js';\nimport { renderTemplate } from '../template.js';\nimport { registerStep } from '../registry.js';\n\ninterface TapParams {\n store: string;\n action: string;\n capture: string;\n args?: unknown[];\n timeout?: number;\n select?: string;\n}\n\nregisterStep('tap', async (ctx: PipelineContext, params: unknown): Promise<unknown> => {\n if (!ctx.page) throw new Error('Browser page required for tap step');\n\n const p = renderTemplate(params, { args: ctx.args, data: ctx.data }) as TapParams;\n const storeName = p.store;\n const actionName = p.action;\n const capturePattern = p.capture;\n const actionArgs = p.args || [];\n const timeoutSec = p.timeout || 5;\n const selectPath = p.select;\n\n // Build a self-contained JS block that:\n // 1. Patches fetch/XHR to intercept matching responses\n // 2. Finds the Pinia/Vuex store\n // 3. Calls the action with provided args\n // 4. Waits for the intercepted response\n // 5. Restores original fetch/XHR\n const result = await ctx.page.evaluate<unknown>(`\n (async () => {\n let captured = null;\n let captureResolve;\n const capturePromise = new Promise(r => { captureResolve = r; });\n const capturePattern = ${JSON.stringify(capturePattern)};\n const timeoutMs = ${timeoutSec * 1000};\n\n // 1. Patch fetch\n const origFetch = window.fetch;\n window.fetch = async function(...args) {\n const resp = await origFetch.apply(this, args);\n try {\n const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || '';\n if (url.includes(capturePattern) && !captured) {\n captured = await resp.clone().json();\n captureResolve();\n }\n } catch {}\n return resp;\n };\n\n // 2. Patch XHR\n const origSend = XMLHttpRequest.prototype.send;\n const origOpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function(method, url, ...rest) {\n this.__tapUrl = url;\n return origOpen.call(this, method, url, ...rest);\n };\n XMLHttpRequest.prototype.send = function(...args) {\n this.addEventListener('load', function() {\n if (this.__tapUrl?.includes(capturePattern) && !captured) {\n try {\n captured = JSON.parse(this.responseText);\n captureResolve();\n } catch {}\n }\n });\n return origSend.apply(this, args);\n };\n\n try {\n // 3. Find the store\n const app = document.querySelector('#app');\n let store = null;\n\n // Try Pinia via __vue_app__\n if (app?.__vue_app__) {\n const pinia = app.__vue_app__.config?.globalProperties?.$pinia;\n if (pinia?._s) {\n store = pinia._s.get(${JSON.stringify(storeName)});\n }\n // Try Vuex\n if (!store) {\n const vuex = app.__vue_app__.config?.globalProperties?.$store;\n if (vuex) store = vuex;\n }\n }\n\n // Fallback: global pinia\n if (!store && window.__pinia?._s) {\n store = window.__pinia._s.get(${JSON.stringify(storeName)});\n }\n\n if (!store) {\n return { error: 'Store not found: ' + ${JSON.stringify(storeName)} };\n }\n\n // 4. Call the action\n const actionFn = store[${JSON.stringify(actionName)}];\n if (typeof actionFn !== 'function') {\n return { error: 'Action not found: ' + ${JSON.stringify(actionName)} };\n }\n\n await actionFn.apply(store, ${JSON.stringify(actionArgs)});\n\n // 5. Wait for capture\n if (!captured) {\n await Promise.race([\n capturePromise,\n new Promise(r => setTimeout(r, timeoutMs)),\n ]);\n }\n } finally {\n // 6. Restore originals\n window.fetch = origFetch;\n XMLHttpRequest.prototype.send = origSend;\n XMLHttpRequest.prototype.open = origOpen;\n }\n\n return captured;\n })()\n `);\n\n if (!result) return null;\n if ((result as any)?.error) {\n throw new Error((result as any).error);\n }\n\n // Apply select path if specified (e.g., \"data.items\")\n if (selectPath) {\n let current: unknown = result;\n for (const part of selectPath.split('.')) {\n if (current === null || current === undefined) return null;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n }\n\n return result;\n});\n","import chalk from 'chalk';\n\nexport const log = {\n info: (msg: string) => console.log(chalk.blue('ℹ'), msg),\n success: (msg: string) => console.log(chalk.green('✓'), msg),\n warn: (msg: string) => console.log(chalk.yellow('⚠'), msg),\n error: (msg: string) => console.error(chalk.red('✗'), msg),\n debug: (msg: string) => {\n if (process.env.LOBSTER_DEBUG) console.log(chalk.gray('⋯'), msg);\n },\n step: (n: number, msg: string) => console.log(chalk.cyan(`[${n}]`), msg),\n dim: (msg: string) => console.log(chalk.dim(msg)),\n};\n","import type { IPage } from '../types/page.js';\nimport type { PipelineContext, PipelineStepDef } from '../types/pipeline.js';\nimport { getStep } from './registry.js';\nimport { log } from '../utils/logger.js';\n\nexport async function executePipeline(\n steps: PipelineStepDef[],\n page: IPage | null,\n args: Record<string, unknown>,\n debug = false\n): Promise<unknown> {\n const ctx: PipelineContext = { page, args, data: null, debug };\n\n for (let i = 0; i < steps.length; i++) {\n const stepDef = steps[i];\n const [stepName, params] = Object.entries(stepDef)[0];\n\n const handler = getStep(stepName);\n if (!handler) {\n throw new Error(`Unknown pipeline step: ${stepName}`);\n }\n\n if (debug) {\n log.step(i + 1, `${stepName}`);\n }\n\n ctx.data = await handler(ctx, params);\n\n if (debug && ctx.data !== undefined) {\n const preview = JSON.stringify(ctx.data)?.slice(0, 200);\n log.dim(` → ${preview}...`);\n }\n }\n\n return ctx.data;\n}\n"],"mappings":"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","names":["renderedParams","url","results"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/adapter/registry.ts
|
|
2
|
+
var REGISTRY_KEY = "__lobster_registry__";
|
|
3
|
+
if (!globalThis[REGISTRY_KEY]) {
|
|
4
|
+
globalThis[REGISTRY_KEY] = /* @__PURE__ */ new Map();
|
|
5
|
+
}
|
|
6
|
+
function getRegistry() {
|
|
7
|
+
return globalThis[REGISTRY_KEY];
|
|
8
|
+
}
|
|
9
|
+
function getAdapter(site, name) {
|
|
10
|
+
return getRegistry().get(`${site}/${name}`);
|
|
11
|
+
}
|
|
12
|
+
function getAdapterByDomain(domain) {
|
|
13
|
+
const adapters = [];
|
|
14
|
+
for (const adapter of getRegistry().values()) {
|
|
15
|
+
if (adapter.domain && domain.includes(adapter.domain)) adapters.push(adapter);
|
|
16
|
+
}
|
|
17
|
+
return adapters;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/router/decision.ts
|
|
21
|
+
function makeRoutingDecision(request) {
|
|
22
|
+
if (request.site && request.command) {
|
|
23
|
+
const adapter = getAdapter(request.site, request.command);
|
|
24
|
+
if (adapter) {
|
|
25
|
+
return {
|
|
26
|
+
level: 2 /* ADAPTER */,
|
|
27
|
+
reason: `Matched adapter: ${request.site}/${request.command}`,
|
|
28
|
+
adapter
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (request.url) {
|
|
33
|
+
try {
|
|
34
|
+
const domain = new URL(request.url).hostname;
|
|
35
|
+
const adapters = getAdapterByDomain(domain);
|
|
36
|
+
if (adapters.length > 0) {
|
|
37
|
+
return {
|
|
38
|
+
level: 2 /* ADAPTER */,
|
|
39
|
+
reason: `Found adapter for domain: ${domain}`,
|
|
40
|
+
adapter: adapters[0]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (request.url && !request.task) {
|
|
47
|
+
return {
|
|
48
|
+
level: 0 /* HTTP */,
|
|
49
|
+
reason: "Direct URL fetch (no task specified)"
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (request.url) {
|
|
53
|
+
const url = request.url;
|
|
54
|
+
if (url.endsWith(".json") || url.includes("/api/") || url.includes("/v1/") || url.includes("/v2/")) {
|
|
55
|
+
return {
|
|
56
|
+
level: 0 /* HTTP */,
|
|
57
|
+
reason: "URL appears to be an API endpoint"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (request.task) {
|
|
62
|
+
const taskLower = request.task.toLowerCase();
|
|
63
|
+
const interactionWords = ["click", "scroll", "fill", "type", "login", "sign in", "search", "navigate", "find", "extract", "get"];
|
|
64
|
+
const needsInteraction = interactionWords.some((w) => taskLower.includes(w));
|
|
65
|
+
if (needsInteraction || request.url) {
|
|
66
|
+
return {
|
|
67
|
+
level: 3 /* AGENT */,
|
|
68
|
+
reason: "Task requires web interaction"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
level: 3 /* AGENT */,
|
|
74
|
+
reason: "Defaulting to AI agent for unrecognized task"
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
makeRoutingDecision
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=decision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/adapter/registry.ts","../../src/router/decision.ts"],"sourcesContent":["import type { Adapter } from '../types/adapter.js';\nimport { Strategy } from '../types/adapter.js';\n\n// Global registry — shared across module instances (critical for plugins)\nconst REGISTRY_KEY = '__lobster_registry__';\nif (!(globalThis as any)[REGISTRY_KEY]) {\n (globalThis as any)[REGISTRY_KEY] = new Map<string, Adapter>();\n}\n\nfunction getRegistry(): Map<string, Adapter> {\n return (globalThis as any)[REGISTRY_KEY];\n}\n\nexport function cli(def: Partial<Adapter> & { site: string; name: string }): Adapter {\n const adapter: Adapter = {\n site: def.site,\n name: def.name,\n description: def.description || `${def.site} ${def.name}`,\n domain: def.domain,\n strategy: def.strategy || Strategy.PUBLIC,\n browser: def.browser ?? (def.strategy !== Strategy.PUBLIC),\n args: def.args || [],\n columns: def.columns,\n func: def.func,\n pipeline: def.pipeline,\n timeoutSeconds: def.timeoutSeconds,\n navigateBefore: def.navigateBefore,\n };\n\n const fullName = `${adapter.site}/${adapter.name}`;\n getRegistry().set(fullName, adapter);\n return adapter;\n}\n\nexport function getAdapter(site: string, name: string): Adapter | undefined {\n return getRegistry().get(`${site}/${name}`);\n}\n\nexport function getAdapterBySite(site: string): Adapter[] {\n const adapters: Adapter[] = [];\n for (const [key, adapter] of getRegistry()) {\n if (key.startsWith(`${site}/`)) adapters.push(adapter);\n }\n return adapters;\n}\n\nexport function getAdapterByDomain(domain: string): Adapter[] {\n const adapters: Adapter[] = [];\n for (const adapter of getRegistry().values()) {\n if (adapter.domain && domain.includes(adapter.domain)) adapters.push(adapter);\n }\n return adapters;\n}\n\nexport function getAllAdapters(): Adapter[] {\n return [...getRegistry().values()];\n}\n\nexport function getAllSites(): string[] {\n const sites = new Set<string>();\n for (const adapter of getRegistry().values()) {\n sites.add(adapter.site);\n }\n return [...sites].sort();\n}\n\nexport { Strategy };\n","import { ExecutionLevel } from '../types/router.js';\nimport type { RoutingDecision, ExecutionRequest } from '../types/router.js';\nimport { getAdapter, getAdapterByDomain } from '../adapter/registry.js';\n\nexport function makeRoutingDecision(request: ExecutionRequest): RoutingDecision {\n // 1. Explicit adapter match\n if (request.site && request.command) {\n const adapter = getAdapter(request.site, request.command);\n if (adapter) {\n return {\n level: ExecutionLevel.ADAPTER,\n reason: `Matched adapter: ${request.site}/${request.command}`,\n adapter,\n };\n }\n }\n\n // 2. URL-based adapter match\n if (request.url) {\n try {\n const domain = new URL(request.url).hostname;\n const adapters = getAdapterByDomain(domain);\n if (adapters.length > 0) {\n return {\n level: ExecutionLevel.ADAPTER,\n reason: `Found adapter for domain: ${domain}`,\n adapter: adapters[0],\n };\n }\n } catch {}\n }\n\n // 3. Simple HTTP check — URL-only request with no interaction task\n if (request.url && !request.task) {\n return {\n level: ExecutionLevel.HTTP,\n reason: 'Direct URL fetch (no task specified)',\n };\n }\n\n // 4. Simple fetch detection — URL looks like an API\n if (request.url) {\n const url = request.url;\n if (url.endsWith('.json') || url.includes('/api/') || url.includes('/v1/') || url.includes('/v2/')) {\n return {\n level: ExecutionLevel.HTTP,\n reason: 'URL appears to be an API endpoint',\n };\n }\n }\n\n // 5. Task requires interaction — use agent\n if (request.task) {\n const taskLower = request.task.toLowerCase();\n const interactionWords = ['click', 'scroll', 'fill', 'type', 'login', 'sign in', 'search', 'navigate', 'find', 'extract', 'get'];\n const needsInteraction = interactionWords.some((w) => taskLower.includes(w));\n\n if (needsInteraction || request.url) {\n return {\n level: ExecutionLevel.AGENT,\n reason: 'Task requires web interaction',\n };\n }\n }\n\n // Default: agent for anything unrecognized\n return {\n level: ExecutionLevel.AGENT,\n reason: 'Defaulting to AI agent for unrecognized task',\n };\n}\n"],"mappings":";AAIA,IAAM,eAAe;AACrB,IAAI,CAAE,WAAmB,YAAY,GAAG;AACtC,EAAC,WAAmB,YAAY,IAAI,oBAAI,IAAqB;AAC/D;AAEA,SAAS,cAAoC;AAC3C,SAAQ,WAAmB,YAAY;AACzC;AAuBO,SAAS,WAAW,MAAc,MAAmC;AAC1E,SAAO,YAAY,EAAE,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE;AAC5C;AAUO,SAAS,mBAAmB,QAA2B;AAC5D,QAAM,WAAsB,CAAC;AAC7B,aAAW,WAAW,YAAY,EAAE,OAAO,GAAG;AAC5C,QAAI,QAAQ,UAAU,OAAO,SAAS,QAAQ,MAAM,EAAG,UAAS,KAAK,OAAO;AAAA,EAC9E;AACA,SAAO;AACT;;;AChDO,SAAS,oBAAoB,SAA4C;AAE9E,MAAI,QAAQ,QAAQ,QAAQ,SAAS;AACnC,UAAM,UAAU,WAAW,QAAQ,MAAM,QAAQ,OAAO;AACxD,QAAI,SAAS;AACX,aAAO;AAAA,QACL;AAAA,QACA,QAAQ,oBAAoB,QAAQ,IAAI,IAAI,QAAQ,OAAO;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,QAAQ,KAAK;AACf,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,QAAQ,GAAG,EAAE;AACpC,YAAM,WAAW,mBAAmB,MAAM;AAC1C,UAAI,SAAS,SAAS,GAAG;AACvB,eAAO;AAAA,UACL;AAAA,UACA,QAAQ,6BAA6B,MAAM;AAAA,UAC3C,SAAS,SAAS,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAAC;AAAA,EACX;AAGA,MAAI,QAAQ,OAAO,CAAC,QAAQ,MAAM;AAChC,WAAO;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,EACF;AAGA,MAAI,QAAQ,KAAK;AACf,UAAM,MAAM,QAAQ;AACpB,QAAI,IAAI,SAAS,OAAO,KAAK,IAAI,SAAS,OAAO,KAAK,IAAI,SAAS,MAAM,KAAK,IAAI,SAAS,MAAM,GAAG;AAClG,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,MAAI,QAAQ,MAAM;AAChB,UAAM,YAAY,QAAQ,KAAK,YAAY;AAC3C,UAAM,mBAAmB,CAAC,SAAS,UAAU,QAAQ,QAAQ,SAAS,WAAW,UAAU,YAAY,QAAQ,WAAW,KAAK;AAC/H,UAAM,mBAAmB,iBAAiB,KAAK,CAAC,MAAM,UAAU,SAAS,CAAC,CAAC;AAE3E,QAAI,oBAAoB,QAAQ,KAAK;AACnC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,EACV;AACF;","names":[]}
|