dsh-plugin-model-proxy 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/LICENSE +21 -0
- package/README.md +157 -0
- package/cordis.patch.yml +15 -0
- package/lib/client/ModelProxyCard.d.ts +14 -0
- package/lib/client/ModelProxyCard.d.ts.map +1 -0
- package/lib/client/catalog.d.ts +140 -0
- package/lib/client/catalog.d.ts.map +1 -0
- package/lib/client/controller.d.ts +107 -0
- package/lib/client/controller.d.ts.map +1 -0
- package/lib/client/index.d.ts +8 -0
- package/lib/client/index.d.ts.map +1 -0
- package/lib/client/locales.d.ts +106 -0
- package/lib/client/locales.d.ts.map +1 -0
- package/lib/client.js +1129 -0
- package/lib/client.js.map +7 -0
- package/lib/host/config.d.ts +47 -0
- package/lib/host/config.d.ts.map +1 -0
- package/lib/host/config.js +153 -0
- package/lib/host/config.js.map +1 -0
- package/lib/host/credentials.d.ts +24 -0
- package/lib/host/credentials.d.ts.map +1 -0
- package/lib/host/credentials.js +45 -0
- package/lib/host/credentials.js.map +1 -0
- package/lib/host/dispatcher.d.ts +30 -0
- package/lib/host/dispatcher.d.ts.map +1 -0
- package/lib/host/dispatcher.js +167 -0
- package/lib/host/dispatcher.js.map +1 -0
- package/lib/host/fetch-wrap.d.ts +45 -0
- package/lib/host/fetch-wrap.d.ts.map +1 -0
- package/lib/host/fetch-wrap.js +100 -0
- package/lib/host/fetch-wrap.js.map +1 -0
- package/lib/host/index.d.ts +18 -0
- package/lib/host/index.d.ts.map +1 -0
- package/lib/host/index.js +262 -0
- package/lib/host/index.js.map +1 -0
- package/lib/host/probe.d.ts +30 -0
- package/lib/host/probe.d.ts.map +1 -0
- package/lib/host/probe.js +43 -0
- package/lib/host/probe.js.map +1 -0
- package/lib/invariant.d.ts +5 -0
- package/lib/invariant.d.ts.map +1 -0
- package/lib/invariant.js +7 -0
- package/lib/invariant.js.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/client/index.ts", "../src/client/catalog.ts", "../src/client/ModelProxyCard.tsx", "../src/client/controller.ts", "../src/client/locales.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Browser half: registers `settings.plugin.item` card for `model-proxy`.\n * Non-invasive: only depends on the declared slot via type-only import.\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport { ProviderCatalogStore } from './catalog.js'\nimport { ModelProxyCard } from './ModelProxyCard.js'\nimport { ModelProxyController, type ModelProxyConfig } from './controller.js'\nimport { en, zh } from './locales.js'\n\nexport const inject = ['slots', 'locale', 'connection', 'remote', 'settingsScope']\n\nexport function apply(ctx: ClientContext): void {\n const t = ctx.locale.bind('settings.modelProxy' as never)\n ctx.effect(() => ctx.locale.register('settings.modelProxy' as never, { en, zh } as never), 'model-proxy: locale')\n\n const scope = (ctx.settingsScope as any).bind({ namespace: 'model-proxy' })\n const ctrl = new ModelProxyController(scope as never)\n // Subscribe lifecycle tied to this plugin fiber\n ctx.effect(() => {\n ctrl.bind()\n return () => ctrl.dispose()\n }, 'model-proxy: scope bind')\n\n // Live provider/model catalog for the card dropdowns (llm.providers /\n // llm.models, refreshed on llm/adapters-updated + settings/document-updated).\n const cctx = ctx as never as { connection: { api: unknown }; remote: unknown }\n const catalog = new ProviderCatalogStore(\n cctx.connection.api as never,\n cctx.remote as never,\n )\n ctx.effect(() => {\n catalog.bind()\n return () => catalog.dispose()\n }, 'model-proxy: catalog')\n\n ctx.slots.inject('settings.plugin.item', () =>\n ctx.slots.register(\n {\n name: 'settings.plugin.item',\n key: 'model-proxy',\n locale: 'settings.modelProxy' as never,\n inject: () => ({ controller: ctrl, catalog }),\n } as never,\n ModelProxyCard as never,\n ),\n )\n\n // Also support direct section registration if user prefers a top-level section.\n // Uncomment to get a dedicated nav entry instead of Plugins tab card:\n // ctx.slots.inject('settings.section', () => ctx.slots.register({\n // name: 'settings.section',\n // id: 'model-proxy',\n // order: 20,\n // label: () => (t as (k:string)=>string)('nav'),\n // locale: 'settings.modelProxy' as never,\n // inject: () => ({ controller: ctrl }),\n // } as never, ModelProxyCard as never))\n}\n", "/**\n * Provider/model catalog feeding the settings card dropdowns.\n *\n * Data sources mirror the built-in Models page and the model-selection\n * surface:\n * - `llm.providers` \u2014 configurable provider directory merged with live routes\n * (`{ providers: [{ provider, displayName, settingsNs, settingsPath,\n * active, declared? }] }`)\n * - `llm.models` \u2014 advisory per-provider model catalog\n * (`{ groups: [{ id, name, models: [{ id, name?, \u2026 }] }], failures }`)\n * - `settings.describe` \u2014 optional enrichment: cross-references each row's\n * settings address to derive `configured`, so the card can group\n * user-configured providers ahead of the bare directory (same semantics as\n * the built-in Models page).\n *\n * Refresh triggers mirror ui-model-selection's convergence sources:\n * `llm/adapters-updated` (registry topology commits) and\n * `settings/document-updated` (configuration-owned catalogs).\n *\n * Everything here is advisory. The card always keeps a free-text escape\n * hatch, because proxy rules legitimately target things no catalog lists:\n * wildcard patterns (`vendor-*`, `*`) and providers whose adapter is not\n * installed (yet).\n */\n\n/** One selectable provider row (shape of `llm.providers.providers`). */\nexport type CatalogProvider = {\n provider: string\n displayName: string\n active: boolean\n /** Settings namespace whose section configures this provider (absent = no settings address). */\n settingsNs?: string\n /** Path from that section's root to this provider's profile object. */\n settingsPath?: string[]\n /** Adapter knows this route only because configuration declared it. */\n declared?: boolean\n /**\n * Mirrors ui-settings-models' derivation: the provider's settings namespace\n * exists AND its profile object is present in the merged value. Undefined\n * when unknown (no settings address, or settings.describe unavailable).\n */\n configured?: boolean\n}\n\n/** One selectable model row (subset of an `llm.models` group entry). */\nexport type CatalogModel = {\n id: string\n name?: string\n}\n\nexport type CatalogSnapshot = {\n status: 'idle' | 'loading' | 'ready' | 'unavailable'\n providers: CatalogProvider[]\n /** Provider id \u2192 catalog models; absent/empty means no known models. */\n modelsByProvider: Record<string, CatalogModel[]>\n}\n\n/** Structural wire face so tests can pass fakes without a transport. */\nexport type CatalogApiFace = {\n llm: {\n providers(payload: object): Promise<unknown>\n models(payload: object): Promise<unknown>\n }\n /**\n * Optional settings mirror used to derive each provider's `configured`\n * flag. Absent or failing keeps configured undefined \u2014 grouping degrades\n * to the flat directory list.\n */\n settings?: {\n describe(payload: object): Promise<unknown>\n }\n}\n\n/** Structural face of `ctx.remote` \u2014 `$on` returns its own disposer. */\nexport type CatalogRemoteFace = {\n $on(event: string, listener: (...args: never[]) => void): () => void\n}\n\ntype Listener = () => void\n\nconst EMPTY: CatalogSnapshot = { status: 'idle', providers: [], modelsByProvider: {} }\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : undefined\n}\n\n/**\n * Unwrap one RPC envelope (`{ result: { ok, value | error } }`), throwing on\n * a business rejection with its wire message.\n */\nexport function unwrapRpc(response: unknown): unknown {\n const envelope = asRecord(response)\n const result = envelope === undefined ? undefined : asRecord(envelope.result)\n if (result === undefined) throw new Error('malformed RPC envelope')\n if (result.ok !== true) {\n const error = asRecord(result.error)\n throw new Error(typeof error?.message === 'string' && error.message !== '' ? error.message : 'RPC request failed')\n }\n return result.value\n}\n\n/**\n * Extract selectable provider rows from an unwrapped `llm.providers` value.\n * Malformed rows are skipped, never fatal \u2014 the dropdown degrades to whatever\n * is parseable.\n */\nexport function pickProviders(value: unknown): CatalogProvider[] {\n const root = asRecord(value)\n const rows = Array.isArray(root?.providers) ? root.providers : []\n const out: CatalogProvider[] = []\n for (const row of rows) {\n const r = asRecord(row)\n if (r === undefined || typeof r.provider !== 'string' || r.provider === '') continue\n const p: CatalogProvider = {\n provider: r.provider,\n displayName: typeof r.displayName === 'string' && r.displayName !== '' ? r.displayName : r.provider,\n active: r.active === true,\n }\n if (typeof r.settingsNs === 'string' && r.settingsNs !== '') {\n p.settingsNs = r.settingsNs\n if (Array.isArray(r.settingsPath)) {\n p.settingsPath = r.settingsPath.filter((k): k is string => typeof k === 'string')\n }\n }\n if (r.declared === true || r.declared === false) p.declared = r.declared\n out.push(p)\n }\n return out\n}\n\n/** Deep-get along a path; undefined as soon as any hop is missing. */\nexport function getPath(value: unknown, path: readonly string[]): unknown {\n let cur: unknown = value\n for (const key of path) {\n const rec = asRecord(cur)\n if (rec === undefined) return undefined\n cur = rec[key]\n }\n return cur\n}\n\n/**\n * Attach `configured` to provider rows, mirroring ui-settings-models'\n * derivation: the settings namespace exists in the mirror AND either the\n * provider configures via the whole section (`settingsPath` empty) or its\n * profile object is present in the merged value. Rows without a settings\n * address keep `configured` undefined (unknown, not false).\n */\nexport function attachConfigured(\n providers: CatalogProvider[],\n namespaces: ReadonlyArray<{ ns?: unknown; value?: unknown }>,\n): CatalogProvider[] {\n const byNs = new Map<string, unknown>()\n for (const n of namespaces) {\n if (typeof n.ns === 'string' && !(byNs.has(n.ns))) byNs.set(n.ns, n.value)\n }\n return providers.map((p) => {\n if (p.settingsNs === undefined || !byNs.has(p.settingsNs)) return p\n const value = byNs.get(p.settingsNs)\n const path = p.settingsPath ?? []\n const configured = path.length === 0 ? value !== undefined : getPath(value, path) !== undefined\n return { ...p, configured }\n })\n}\n\n/**\n * Group an unwrapped `llm.models` value into provider id \u2192 model rows.\n * Failures entries are ignored (the dropdown simply offers nothing for a\n * provider whose catalog failed host-side).\n */\nexport function pickModelsByProvider(value: unknown): Record<string, CatalogModel[]> {\n const root = asRecord(value)\n const groups = Array.isArray(root?.groups) ? root.groups : []\n const map: Record<string, CatalogModel[]> = {}\n for (const group of groups) {\n const g = asRecord(group)\n if (g === undefined || typeof g.id !== 'string' || g.id === '' || !Array.isArray(g.models)) continue\n const models: CatalogModel[] = []\n for (const row of g.models) {\n const m = asRecord(row)\n if (m === undefined || typeof m.id !== 'string' || m.id === '') continue\n models.push({ id: m.id, ...(typeof m.name === 'string' && m.name !== '' ? { name: m.name } : {}) })\n }\n map[g.id] = models\n }\n return map\n}\n\n/**\n * uSES-compatible store over the two catalog RPCs. Loads once on bind(),\n * reloads on forwarded topology/settings events, and keeps the last good\n * rows across a failed background refresh so an outage never blanks open\n * dropdowns.\n */\nexport class ProviderCatalogStore {\n private readonly listeners = new Set<Listener>()\n private snapshot: CatalogSnapshot = EMPTY\n private generation = 0\n private unsubs: Array<() => void> = []\n private bound = false\n\n constructor(\n private readonly api: CatalogApiFace,\n private readonly remote?: CatalogRemoteFace,\n ) {}\n\n /** Idempotent: start loading and subscribe to refresh events. */\n bind(): void {\n if (this.bound) return\n this.bound = true\n if (this.remote !== undefined) {\n this.unsubs.push(this.remote.$on('llm/adapters-updated', () => void this.load()))\n this.unsubs.push(this.remote.$on('settings/document-updated', () => void this.load()))\n }\n void this.load()\n }\n\n /** Stop listening; in-flight loads become no-ops via the generation guard. */\n dispose(): void {\n this.bound = false\n for (const off of this.unsubs.splice(0)) {\n try {\n off()\n } catch {\n // A disposer failing must not mask the remaining ones.\n }\n }\n this.generation++\n this.listeners.clear()\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n getSnapshot(): CatalogSnapshot {\n return this.snapshot\n }\n\n /**\n * Fetch both catalogs. Each call settles independently (`allSettled`\n * semantics via mapped thunks): a provider-catalog failure fails the whole\n * load, while a model-catalog failure degrades to an empty model map \u2014\n * models are an enrichment, providers drive the primary dropdown.\n */\n async load(): Promise<void> {\n const generation = ++this.generation\n if (this.snapshot.status === 'idle') this.set({ ...EMPTY, status: 'loading' })\n const settle = <T>(promise: Promise<T>): Promise<{ ok: true; value: T } | { ok: false }> =>\n promise.then(\n (value) => ({ ok: true as const, value }),\n () => ({ ok: false as const }),\n )\n const [providersOutcome, modelsOutcome, settingsOutcome] = await Promise.all([\n settle(this.api.llm.providers({})),\n settle(this.api.llm.models({})),\n // Enrichment only: a describe failure must not degrade the dropdowns.\n settle(this.api.settings?.describe({}) ?? Promise.reject(new Error('no settings face'))),\n ])\n if (generation !== this.generation) return\n let providers: CatalogProvider[]\n if (!providersOutcome.ok) {\n this.markUnavailable()\n return\n }\n try {\n // Also catches a business-rejection envelope (`result.ok === false`),\n // which resolves rather than rejects.\n providers = pickProviders(unwrapRpc(providersOutcome.value))\n } catch {\n this.markUnavailable()\n return\n }\n let modelsByProvider: Record<string, CatalogModel[]> = {}\n if (modelsOutcome.ok) {\n try {\n modelsByProvider = pickModelsByProvider(unwrapRpc(modelsOutcome.value))\n } catch {\n modelsByProvider = {}\n }\n }\n if (settingsOutcome.ok) {\n try {\n const described = unwrapRpc(settingsOutcome.value) as { namespaces?: unknown }\n const namespaces = Array.isArray(described?.namespaces) ? described.namespaces : []\n providers = attachConfigured(\n providers,\n namespaces.map((n) => {\n const r = asRecord(n)\n return { ns: r?.ns, value: r?.value }\n }),\n )\n } catch {\n // Keep configured undefined \u2014 flat list, no grouping.\n }\n }\n this.set({ status: 'ready', providers, modelsByProvider })\n }\n\n /**\n * Keep last good rows across a failed refresh; the card falls back to\n * free-text inputs only when the snapshot holds no providers at all.\n */\n private markUnavailable(): void {\n this.set({ ...this.snapshot, status: 'unavailable' })\n }\n\n private set(snapshot: CatalogSnapshot): void {\n this.snapshot = snapshot\n for (const listener of [...this.listeners]) listener()\n }\n}\n", "import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport type { CSSProperties } from 'react'\nimport type { CatalogSnapshot } from './catalog.js'\nimport { ProviderCatalogStore } from './catalog.js'\nimport type { ModelProxyController, ModelProxyConfig, ProxyRule } from './controller.js'\nimport { buildCreatorRules, ensureRuleIds, groupByProvider, makeRuleId } from './controller.js'\nimport { en } from './locales.js'\n\ntype Props = {\n controller: ModelProxyController\n /** Live provider/model catalog; absent or empty falls back to free text. */\n catalog?: ProviderCatalogStore\n t?: (k: keyof typeof en) => string\n}\n\nconst NO_CATALOG: CatalogSnapshot = { status: 'idle', providers: [], modelsByProvider: {} }\n\n/** Sentinel <option> that flips a field into free-text mode (never stored). */\nconst CUSTOM_SENTINEL = '__custom__'\n\ntype FieldOption = { value: string; label: string }\n\n/** Rendered shape of one <optgroup>; label undefined renders a flat list. */\ntype FieldGroup = { label?: string; options: FieldOption[] }\n\n/**\n * One rule field rendered as a dropdown over catalog options with a\n * \"Custom\u2026\" escape hatch into free text.\n *\n * Mode is derived, not synced: an existing value absent from the options\n * (hand-written yaml rule, wildcard pattern) opens in custom mode; picking\n * the sentinel option enters custom mode without touching the value; the\n * \u25BE button returns to the list. When no options exist at all (catalog\n * unloaded/unavailable/empty) only the text input renders.\n */\nfunction CatalogField(props: {\n value: string\n groups: FieldGroup[]\n onChange: (value: string) => void\n disabled: boolean\n chooseLabel: string\n customLabel: string\n listTitle: string\n inputPlaceholder: string\n}) {\n const { value, groups, onChange, disabled, chooseLabel, customLabel, listTitle, inputPlaceholder } = props\n const [customMode, setCustomMode] = useState(() => value !== '' && !groups.some((g) => g.options.some((o) => o.value === value)))\n const flat = groups.flatMap((g) => g.options)\n const showSelect = flat.length > 0 && !customMode\n\n if (flat.length === 0) {\n return (\n <input\n value={value}\n placeholder={inputPlaceholder}\n onChange={(e) => onChange(e.target.value)}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc', width: '100%', boxSizing: 'border-box' }}\n disabled={disabled}\n />\n )\n }\n\n return (\n <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>\n {showSelect ? (\n <select\n value={flat.some((o) => o.value === value) ? value : ''}\n onChange={(e) => {\n if (e.target.value === CUSTOM_SENTINEL) {\n setCustomMode(true)\n return\n }\n onChange(e.target.value)\n }}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc', flex: 1, minWidth: 0 }}\n disabled={disabled}\n >\n {!flat.some((o) => o.value === value) && <option value=\"\">{chooseLabel}</option>}\n {groups.map((g, gi) =>\n g.label === undefined ? (\n g.options.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)\n ) : (\n <optgroup key={`${gi}:${g.label}`} label={g.label}>\n {g.options.map((o) => (\n <option key={o.value} value={o.value}>{o.label}</option>\n ))}\n </optgroup>\n ),\n )}\n <option value={CUSTOM_SENTINEL}>{customLabel}</option>\n </select>\n ) : (\n <>\n <input\n value={value}\n placeholder={inputPlaceholder}\n onChange={(e) => onChange(e.target.value)}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc', flex: 1, minWidth: 0 }}\n disabled={disabled}\n />\n <button\n type=\"button\"\n title={listTitle}\n onClick={() => setCustomMode(false)}\n disabled={disabled}\n style={{\n padding: '4px 8px',\n borderRadius: 6,\n border: '1px solid #ddd',\n background: '#fff',\n cursor: 'pointer',\n lineHeight: 1,\n }}\n >\n \u25BE\n </button>\n </>\n )}\n </div>\n )\n}\n\n/** Keep in sync with SUPPORTED_SCHEMES in src/host/config.ts. */\nconst SUPPORTED_SCHEMES = ['http:', 'https:', 'socks5:', 'socks5h:', 'socks:']\n\n/** Draft state of the batch-add panel: one spec fanned out over N models. */\ntype CreatorState = {\n provider: string\n checked: string[]\n customPattern: string\n proxyUrl: string\n purpose: string\n credentialRef: string\n}\n\nconst EMPTY_CREATOR: CreatorState = {\n provider: '',\n checked: [],\n customPattern: '',\n proxyUrl: '',\n purpose: '',\n credentialRef: '',\n}\n\nfunction validateUrl(urlStr: string): string | undefined {\n try {\n const u = new URL(urlStr)\n if (!SUPPORTED_SCHEMES.includes(u.protocol)) return 'unsupported scheme'\n if (!u.hostname) return 'missing host'\n return undefined\n } catch {\n return 'invalid URL'\n }\n}\n\nfunction validateRule(r: ProxyRule, all: ProxyRule[]): string | undefined {\n if (r.provider !== r.provider.trim()) return 'whitespace around provider'\n if (r.model !== r.model.trim()) return 'whitespace around model'\n if (r.purpose !== undefined && r.purpose !== '' && r.purpose !== r.purpose.trim()) return 'whitespace around purpose'\n if (r.credentialRef !== undefined && r.credentialRef !== '' && r.credentialRef !== r.credentialRef.trim()) return 'whitespace around credential ref'\n if (!r.provider.trim()) return 'provider required'\n if (!r.model.trim()) return 'model required'\n // prefix patterns allow a single trailing '*' only (mirror host assertServiceable)\n const starCount = (r.model.match(/\\*/g) ?? []).length\n const isBareWildcard = r.model === '*'\n if (!isBareWildcard && starCount > 0 && !(starCount === 1 && r.model.endsWith('*'))) {\n return 'at most one trailing * (e.g. \"vendor-*\")'\n }\n if (r.proxyUrl !== '') {\n const err = validateUrl(r.proxyUrl)\n if (err) return err\n }\n const dup = all.filter(x => x.provider === r.provider && x.model === r.model).length > 1\n if (dup) return 'duplicate'\n return undefined\n}\n\n/** Mirrors host assertServiceable for defaultProxy so bad values never reach the wire. */\nexport function validateDefaultProxy(defaultProxy: string): string | undefined {\n if (defaultProxy === '') return undefined\n return validateUrl(defaultProxy)\n}\n\n/**\n * Header of one provider group: identity, shared-fields bulk actions, and a\n * two-step destructive delete. The proxy input applies to every member rule;\n * matching semantics are untouched because cross-provider reordering never\n * influences resolution.\n */\nfunction GroupHeader(props: {\n provider: string\n displayName: string\n count: number\n allEnabled: boolean\n disabled: boolean\n t: (k: keyof typeof en) => string\n onApplyProxy(url: string): void\n onSetEnabled(enabled: boolean): void\n onDelete(): void\n}) {\n const { provider, displayName, count, allEnabled, disabled, t, onApplyProxy, onSetEnabled, onDelete } = props\n const [proxy, setProxy] = useState('')\n const [confirming, setConfirming] = useState(false)\n\n useEffect(() => {\n if (!confirming) return\n const timer = setTimeout(() => setConfirming(false), 3000)\n return () => clearTimeout(timer)\n }, [confirming])\n\n return (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>\n <span style={{ fontWeight: 700 }}>\n {displayName === provider ? provider : `${displayName} \u00B7 ${provider}`}\n <span style={{ fontWeight: 400, opacity: 0.6, marginLeft: 6 }}>{count}</span>\n </span>\n <span style={{ flex: 1 }} />\n <input\n value={proxy}\n placeholder={t('groupApplyProxy')}\n onChange={(e) => setProxy(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') onApplyProxy(proxy.trim())\n }}\n style={{ padding: '4px 8px', borderRadius: 6, border: '1px solid #ddd', width: 200 }}\n disabled={disabled}\n />\n <button type=\"button\" onClick={() => onApplyProxy(proxy.trim())} disabled={disabled || proxy.trim() === ''}\n style={smallButtonStyle}>\n {t('groupApply')}\n </button>\n <button type=\"button\" onClick={() => onSetEnabled(!allEnabled)} disabled={disabled} style={smallButtonStyle}>\n {allEnabled ? t('groupDisableAll') : t('groupEnableAll')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n if (confirming) {\n setConfirming(false)\n onDelete()\n } else {\n setConfirming(true)\n }\n }}\n disabled={disabled}\n style={{\n ...smallButtonStyle,\n borderColor: confirming ? '#d66' : '#ddd',\n color: confirming ? '#d66' : undefined,\n }}\n >\n {confirming ? t('groupConfirmDelete') : t('groupDelete')}\n </button>\n </div>\n )\n}\n\nconst smallButtonStyle: CSSProperties = {\n padding: '4px 8px',\n borderRadius: 6,\n border: '1px solid #ddd',\n background: '#fff',\n cursor: 'pointer',\n lineHeight: 1,\n}\n\nexport function ModelProxyCard({ controller, catalog, t: tProp }: Props) {\n const t = tProp ?? ((k: keyof typeof en) => en[k])\n const snap = useSyncExternalStore(\n (cb) => controller.subscribe(cb),\n () => controller.getSnapshot(),\n () => controller.getSnapshot(),\n )\n const cat = useSyncExternalStore(\n useCallback((cb: () => void) => (catalog ? catalog.subscribe(cb) : () => undefined), [catalog]),\n useCallback(() => catalog?.getSnapshot() ?? NO_CATALOG, [catalog]),\n useCallback(() => catalog?.getSnapshot() ?? NO_CATALOG, [catalog]),\n )\n\n const cfg: ModelProxyConfig = useMemo(() => {\n return ensureRuleIds(\n snap.value ?? {\n enabled: true,\n rules: [],\n defaultProxy: '',\n debug: false,\n },\n )\n }, [snap.value])\n\n const [draft, setDraft] = useState<ModelProxyConfig>(cfg)\n const [saving, setSaving] = useState(false)\n const [msg, setMsg] = useState<string | undefined>()\n\n const dirty = useMemo(() => JSON.stringify(draft) !== JSON.stringify(cfg), [draft, cfg])\n\n // Sync remote \u2192 draft only while the user has NO local edits. Re-syncing on\n // every cfg change (e.g. the intermediate snapshots produced while a save's\n // field writes settle) would clobber in-progress editing mid-save.\n useEffect(() => {\n if (!dirty) setDraft(cfg)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [cfg])\n\n // Auto-clear \"saved\" feedback; clear pending timer on unmount/save restart.\n useEffect(() => {\n if (!msg || msg !== t('saved')) return\n const timer = setTimeout(() => setMsg(undefined), 1500)\n return () => clearTimeout(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [msg])\n\n const defaultProxyError = useMemo(() => validateDefaultProxy(draft.defaultProxy), [draft.defaultProxy])\n\n // Dropdown options from the live provider/model catalogs. Wire order is\n // preserved (registration order host-side); dormant routes are still\n // selectable because rules may pre-date their adapter.\n //\n // When settings.describe enriched rows with `configured`, providers split\n // into two groups \u2014 user-configured first, bare directory second \u2014 so the\n // common case is one click away without hiding pre-provisioning targets.\n const providerGroups: FieldGroup[] = useMemo(() => {\n const opts: FieldOption[] = cat.providers.map((p) => ({\n value: p.provider,\n label: p.displayName === p.provider ? p.provider : `${p.displayName} (${p.provider})${p.active ? '' : ` \u00B7 ${t('dormant')}`}`,\n }))\n if (!cat.providers.some((p) => p.configured !== undefined)) return [{ options: opts }]\n const configured: FieldOption[] = []\n const directory: FieldOption[] = []\n for (let i = 0; i < cat.providers.length; i++) {\n const p = cat.providers[i]!\n ;(p.configured === true ? configured : directory).push(opts[i]!)\n }\n return [\n ...(configured.length > 0 ? [{ label: t('groupConfigured'), options: configured }] : []),\n ...(directory.length > 0 ? [{ label: t('groupDirectory'), options: directory }] : []),\n ]\n }, [cat, t])\n const modelOptionsFor = useCallback(\n (provider: string): FieldOption[] => {\n const models = cat.modelsByProvider[provider] ?? []\n if (models.length === 0) return []\n const wildcard: FieldOption = { value: '*', label: `* (${t('wildcardAll')})` }\n return [\n ...(models.some((m) => m.id === '*') ? [] : [wildcard]),\n ...models.map((m) => ({ value: m.id, label: m.name && m.name !== m.id ? `${m.name} (${m.id})` : m.id })),\n ]\n },\n [cat, t],\n )\n const catalogMissing =\n catalog !== undefined && cat.status === 'unavailable' && cat.providers.length === 0\n\n const onSave = useCallback(async () => {\n // local validation before write \u2014 mirror the host's assertServiceable so\n // rejections surface inline instead of as partial commits\n for (const r of draft.rules) {\n const err = validateRule(r, draft.rules)\n if (err) {\n setMsg(t('invalid'))\n return\n }\n }\n if (defaultProxyError) {\n setMsg(t('invalid'))\n return\n }\n setSaving(true)\n setMsg(undefined)\n try {\n await controller.save(draft)\n setMsg(t('saved'))\n } catch (e) {\n setMsg(String(e))\n } finally {\n setSaving(false)\n }\n }, [draft, controller, t, defaultProxyError])\n\n const setField = useCallback(\n (patch: Partial<ModelProxyConfig>) => setDraft((d) => ({ ...d, ...patch })),\n [],\n )\n\n const [creatorOpen, setCreatorOpen] = useState(false)\n const [creator, setCreator] = useState<CreatorState>(EMPTY_CREATOR)\n const [creatorError, setCreatorError] = useState<string | undefined>()\n\n const setCreatorField = useCallback(\n (patch: Partial<CreatorState>) => setCreator((c) => ({ ...c, ...patch })),\n [],\n )\n\n // Whether draft.rules already covers provider+model \u2014 such checkboxes are\n // disabled so batch creation can never produce a duplicate-rule rejection.\n const ruleExists = useCallback(\n (provider: string, model: string) => draft.rules.some((r) => r.provider === provider && r.model === model),\n [draft.rules],\n )\n\n const creatorModelCount = useMemo(() => {\n if (creator.provider.trim() === '') return 0\n const custom = creator.customPattern.trim()\n return creator.checked.length + (custom !== '' ? 1 : 0)\n }, [creator])\n\n const createRules = useCallback(() => {\n const models = [...creator.checked]\n const custom = creator.customPattern.trim()\n if (custom !== '') models.push(custom)\n const newRules = buildCreatorRules({\n provider: creator.provider,\n models,\n proxyUrl: creator.proxyUrl,\n purpose: creator.purpose,\n credentialRef: creator.credentialRef,\n enabled: true,\n })\n const combined = [...newRules, ...draft.rules]\n for (const r of newRules) {\n const err = validateRule(r, combined)\n if (err) {\n setCreatorError(err === 'duplicate' ? t('duplicate') : err)\n return\n }\n }\n setDraft((d) => ({ ...d, rules: [...newRules, ...d.rules] }))\n setCreator(EMPTY_CREATOR)\n setCreatorError(undefined)\n setCreatorOpen(false)\n }, [creator, draft.rules, t])\n\n const applyGroupProxy = useCallback((provider: string, url: string) => {\n setDraft((d) => ({\n ...d,\n rules: d.rules.map((r) => (r.provider === provider ? { ...r, proxyUrl: url } : r)),\n }))\n }, [])\n\n const setGroupEnabled = useCallback((provider: string, enabled: boolean) => {\n setDraft((d) => ({\n ...d,\n rules: d.rules.map((r) => (r.provider === provider ? { ...r, enabled } : r)),\n }))\n }, [])\n\n const removeGroup = useCallback((provider: string) => {\n setDraft((d) => ({ ...d, rules: d.rules.filter((r) => r.provider !== provider) }))\n }, [])\n\n const updateRule = useCallback((id: string, patch: Partial<ProxyRule>) => {\n setDraft((d) => ({\n ...d,\n rules: d.rules.map((r) => (r.id === id ? { ...r, ...patch } : r)),\n }))\n }, [])\n\n const removeRule = useCallback((id: string) => {\n setDraft((d) => ({ ...d, rules: d.rules.filter((r) => r.id !== id) }))\n }, [])\n\n if (snap.status === 'loading') return <div style={{ padding: 12, opacity: 0.7 }}>{t('reading')}</div>\n if (snap.status === 'unavailable') return <div style={{ padding: 12 }}>{t('unavailable')}</div>\n\n return (\n <div style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 12 }}>\n <div>\n <div style={{ fontWeight: 700, fontSize: 16 }}>{t('title')}</div>\n <div style={{ opacity: 0.7, fontSize: 12, marginTop: 4 }}>{t('desc')}</div>\n {!snap.writable && <div style={{ color: '#a66', fontSize: 12, marginTop: 6 }}>{t('readOnly')}</div>}\n {catalogMissing && <div style={{ opacity: 0.6, fontSize: 12, marginTop: 6 }}>{t('catalogUnavailable')}</div>}\n </div>\n\n <label style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n <input type=\"checkbox\" checked={draft.enabled} onChange={(e) => setField({ enabled: e.target.checked })} disabled={!snap.writable} />\n <span>{t('enabled')}</span>\n </label>\n\n <label style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n <input type=\"checkbox\" checked={draft.debug} onChange={(e) => setField({ debug: e.target.checked })} disabled={!snap.writable} />\n <span>{t('debug')}</span>\n </label>\n\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\n <div style={{ fontWeight: 600 }}>{t('rules')}</div>\n <button\n onClick={() => {\n setCreatorOpen((o) => !o)\n setCreatorError(undefined)\n }}\n disabled={!snap.writable}\n style={{ padding: '6px 10px', borderRadius: 6, border: '1px solid #ccc', background: '#fff', cursor: 'pointer' }}\n >\n \uFF0B {t('addRule')}\n </button>\n </div>\n\n {draft.rules.length === 0 && !creatorOpen && <div style={{ opacity: 0.6, fontSize: 12 }}>{t('empty')}</div>}\n\n {creatorOpen && (\n <div style={{ border: '1px solid #bbb', borderRadius: 10, padding: 12, display: 'flex', flexDirection: 'column', gap: 8 }}>\n <div style={{ fontWeight: 600 }}>{t('creatorTitle')}</div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('provider')}</span>\n <CatalogField\n value={creator.provider}\n groups={providerGroups}\n onChange={(v) => setCreatorField({ provider: v })}\n disabled={!snap.writable}\n chooseLabel={t('choose')}\n customLabel={t('customOption')}\n listTitle={t('backToList')}\n inputPlaceholder={t('providerPlaceholder')}\n />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('proxyUrl')}</span>\n <input\n value={creator.proxyUrl}\n placeholder={t('proxyPlaceholder')}\n onChange={(e) => setCreatorField({ proxyUrl: e.target.value })}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc' }}\n disabled={!snap.writable}\n />\n </label>\n </div>\n\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('creatorModels')}</span>\n {modelOptionsFor(creator.provider).length === 0 ? (\n <span style={{ opacity: 0.6, fontSize: 12 }}>{t('noneCatalogued')}</span>\n ) : (\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }}>\n {modelOptionsFor(creator.provider).map((o) => {\n const exists = ruleExists(creator.provider, o.value)\n const checked = creator.checked.includes(o.value)\n return (\n <label key={o.value} style={{ display: 'flex', gap: 6, alignItems: 'center', opacity: exists ? 0.45 : 1 }}>\n <input\n type=\"checkbox\"\n checked={checked}\n disabled={!snap.writable || exists}\n onChange={() =>\n setCreatorField({\n checked: checked ? creator.checked.filter((m) => m !== o.value) : [...creator.checked, o.value],\n })\n }\n />\n <span style={{ fontSize: 12 }}>{o.label}{exists ? ` \u00B7 ${t('alreadyRuled')}` : ''}</span>\n </label>\n )\n })}\n </div>\n )}\n <input\n value={creator.customPattern}\n placeholder={t('creatorPatternHint')}\n onChange={(e) => setCreatorField({ customPattern: e.target.value })}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc' }}\n disabled={!snap.writable}\n />\n </div>\n\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('purpose')}</span>\n <input\n value={creator.purpose}\n placeholder={t('purposePlaceholder')}\n onChange={(e) => setCreatorField({ purpose: e.target.value })}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc' }}\n disabled={!snap.writable}\n />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('credRef')}</span>\n <input\n value={creator.credentialRef}\n placeholder={t('credRefPlaceholder')}\n onChange={(e) => setCreatorField({ credentialRef: e.target.value })}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc' }}\n disabled={!snap.writable}\n />\n </label>\n </div>\n\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, alignItems: 'center' }}>\n {creatorError && <span style={{ color: '#b33', fontSize: 11 }}>{creatorError}</span>}\n <button\n onClick={() => {\n setCreatorOpen(false)\n setCreator(EMPTY_CREATOR)\n setCreatorError(undefined)\n }}\n style={smallButtonStyle}\n >\n {t('cancel')}\n </button>\n <button\n onClick={createRules}\n disabled={!snap.writable || creator.provider.trim() === '' || creatorModelCount === 0}\n style={{\n ...smallButtonStyle,\n borderColor: '#222',\n background: creatorModelCount > 0 ? '#222' : '#999',\n color: '#fff',\n cursor: creatorModelCount > 0 ? 'pointer' : 'not-allowed',\n }}\n >\n {t('createN').replace('{n}', String(creatorModelCount))}\n </button>\n </div>\n </div>\n )}\n\n {groupByProvider(draft.rules).map((group) => {\n const allEnabled = group.rules.every((r) => r.enabled)\n const entry = cat.providers.find((p) => p.provider === group.provider)\n const displayName = entry?.displayName !== undefined && entry.displayName !== group.provider\n ? `${entry.displayName}`\n : group.provider\n return (\n <div key={group.provider} style={{ border: '1px solid #e8e8e8', borderRadius: 10, padding: 10, display: 'flex', flexDirection: 'column', gap: 8 }}>\n <GroupHeader\n provider={group.provider}\n displayName={displayName}\n count={group.rules.length}\n allEnabled={allEnabled}\n disabled={!snap.writable}\n t={t}\n onApplyProxy={(url) => applyGroupProxy(group.provider, url)}\n onSetEnabled={(enabled) => setGroupEnabled(group.provider, enabled)}\n onDelete={() => removeGroup(group.provider)}\n />\n <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>\n {group.rules.map((raw) => {\n // ensureRuleIds guarantees every rendered rule carries an id.\n const r = raw as ProxyRule & { id: string }\n const err = validateRule(r, draft.rules)\n return (\n <div\n key={r.id}\n style={{\n border: `1px solid ${err ? '#d66' : '#ddd'}`,\n borderRadius: 8,\n padding: 8,\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n gap: 8,\n background: r.enabled ? '#fff' : '#f7f7f7',\n }}\n >\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('model')}</span>\n <CatalogField\n value={r.model}\n groups={[{ options: modelOptionsFor(r.provider) }]}\n onChange={(v) => updateRule(r.id, { model: v })}\n disabled={!snap.writable}\n chooseLabel={t('choose')}\n customLabel={t('customOption')}\n listTitle={t('backToList')}\n inputPlaceholder={t('modelPlaceholder')}\n />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('proxyUrl')}</span>\n <input\n value={r.proxyUrl}\n placeholder={t('proxyPlaceholder')}\n onChange={(e) => updateRule(r.id, { proxyUrl: e.target.value })}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc' }}\n disabled={!snap.writable}\n />\n {err && <span style={{ color: '#b33', fontSize: 11 }}>{err === 'duplicate' ? t('duplicate') : err}</span>}\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('purpose')}</span>\n <input\n value={r.purpose ?? ''}\n placeholder={t('purposePlaceholder')}\n onChange={(e) => updateRule(r.id, { purpose: e.target.value })}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc' }}\n disabled={!snap.writable}\n />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 11, opacity: 0.7 }}>{t('credRef')}</span>\n <input\n value={r.credentialRef ?? ''}\n placeholder={t('credRefPlaceholder')}\n onChange={(e) => updateRule(r.id, { credentialRef: e.target.value })}\n style={{ padding: '6px 8px', borderRadius: 6, border: '1px solid #ccc' }}\n disabled={!snap.writable}\n />\n </label>\n <label style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n <input type=\"checkbox\" checked={r.enabled} onChange={(e) => updateRule(r.id, { enabled: e.target.checked })} disabled={!snap.writable} />\n <span style={{ fontSize: 12 }}>{t('enabledShort')}</span>\n </label>\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>\n <button\n onClick={() => removeRule(r.id)}\n disabled={!snap.writable}\n style={{ padding: '4px 8px', borderRadius: 6, border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}\n >\n {t('delete')}\n </button>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )\n })}\n\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <span style={{ fontSize: 12, fontWeight: 600 }}>{t('defaultProxy')}</span>\n <input\n value={draft.defaultProxy}\n placeholder={t('defaultPlaceholder')}\n onChange={(e) => setField({ defaultProxy: e.target.value })}\n style={{ padding: '6px 8px', borderRadius: 6, border: `1px solid ${defaultProxyError ? '#d66' : '#ccc'}` }}\n disabled={!snap.writable}\n />\n {defaultProxyError && <span style={{ color: '#b33', fontSize: 11 }}>{defaultProxyError}</span>}\n </label>\n\n <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n <button\n onClick={onSave}\n disabled={!snap.writable || !dirty || saving}\n style={{\n padding: '8px 14px',\n borderRadius: 8,\n border: '1px solid #222',\n background: dirty ? '#222' : '#999',\n color: '#fff',\n cursor: dirty ? 'pointer' : 'not-allowed',\n }}\n >\n {saving ? t('saving') : t('save')}\n </button>\n {msg && <span style={{ fontSize: 12, opacity: 0.8 }}>{msg}</span>}\n </div>\n </div>\n )\n}\n", "/**\n * Client controller for model-proxy settings card.\n * Binds to `model-proxy` namespace via ctx.settingsScope and exposes\n * a small observable store for the card.\n */\n\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client'\n\nexport type ProxyRule = {\n /** Card key; may be absent on hand-written yaml rules until edited here. */\n id?: string\n provider: string\n model: string\n proxyUrl: string\n enabled: boolean\n /** Only apply to calls with this GenerateOptions.purpose; empty = all. */\n purpose?: string\n /** Credentials-service entry (\"user:password\") overriding proxyUrl userinfo. */\n credentialRef?: string\n}\n\nexport type ModelProxyConfig = {\n enabled: boolean\n rules: ProxyRule[]\n defaultProxy: string\n debug: boolean\n}\n\nexport type ModelProxySnapshot = {\n status: 'loading' | 'ready' | 'unavailable'\n value?: ModelProxyConfig\n revision?: number\n writable: boolean\n error?: string\n}\n\ntype Listener = () => void\n\n/** Fields the host schema knows; everything else is a legacy/foreign key. */\nconst KNOWN_RULE_FIELDS = ['id', 'provider', 'model', 'proxyUrl', 'enabled', 'purpose', 'credentialRef'] as const\n\n/**\n * Strip unknown keys from drafted rules before persisting. The wire layer\n * tolerates extra keys and passes them through materialization, so without\n * this a legacy field (e.g. the removed `note`) read from the stored document\n * would ride every save forever. Writing only known fields converges stored\n * documents to the schema shape.\n *\n * Dropping is loud, not silent: every stripped key is named on the console,\n * so a hand-written key vanishing on save is never a mystery.\n */\nexport function sanitizeRules(rules: ProxyRule[]): ProxyRule[] {\n return rules.map((rule) => {\n const out: Record<string, unknown> = {}\n const dropped: string[] = []\n for (const [key, value] of Object.entries(rule)) {\n if ((KNOWN_RULE_FIELDS as readonly string[]).includes(key)) out[key] = value\n else dropped.push(key)\n }\n if (dropped.length > 0) {\n console.warn(`[model-proxy] dropping unknown rule field(s): ${dropped.join(', ')}`)\n }\n return out as ProxyRule\n })\n}\n\n/** Stable-enough client-side id for rule cards (React keys / list diffs). */\nexport function makeRuleId(): string {\n return Math.random().toString(36).slice(2, 9)\n}\n\n/** One \"batch add\" spec: shared fields + the model patterns to fan out over. */\nexport type CreatorSpec = {\n provider: string\n /** Model ids / wildcard patterns; one rule each. Duplicates collapse. */\n models: string[]\n proxyUrl: string\n purpose?: string\n credentialRef?: string\n enabled?: boolean\n}\n\n/**\n * Fan one creator spec out into N prefilled rules sharing provider and\n * connection fields. Returns [] when provider or models are effectively\n * empty \u2014 callers gate the button on the same predicate for inline counts.\n */\nexport function buildCreatorRules(spec: CreatorSpec): ProxyRule[] {\n const provider = spec.provider.trim()\n if (provider === '') return []\n const models = [...new Set(spec.models.map((m) => m.trim()).filter((m) => m !== ''))]\n if (models.length === 0) return []\n const purpose = (spec.purpose ?? '').trim()\n const credentialRef = (spec.credentialRef ?? '').trim()\n return models.map((model) => ({\n id: makeRuleId(),\n provider,\n model,\n proxyUrl: spec.proxyUrl.trim(),\n enabled: spec.enabled ?? true,\n ...(purpose !== '' ? { purpose } : {}),\n ...(credentialRef !== '' ? { credentialRef } : {}),\n }))\n}\n\n/**\n * Group rules by provider for the card's grouped view: group order is first\n * appearance, intra-group order preserved. Matching semantics only care about\n * order WITHIN a provider (same-pass first-wins), so regrouping across\n * providers is display-safe by construction.\n */\nexport function groupByProvider(rules: ProxyRule[]): Array<{ provider: string; rules: ProxyRule[] }> {\n const groups: Array<{ provider: string; rules: ProxyRule[] }> = []\n const index = new Map<string, number>()\n for (const rule of rules) {\n let gi = index.get(rule.provider)\n if (gi === undefined) {\n gi = groups.length\n index.set(rule.provider, gi)\n groups.push({ provider: rule.provider, rules: [] })\n }\n groups[gi]!.rules.push(rule)\n }\n return groups\n}\n\n/**\n * Fill missing rule ids so every card has a stable React key. Host-side code\n * never relies on `id` (the schema leaves it optional), so this is purely a\n * UI concern and does not dirty the saved document.\n */\nexport function ensureRuleIds(cfg: ModelProxyConfig): ModelProxyConfig {\n return { ...cfg, rules: cfg.rules.map((r) => (r.id ? r : { ...r, id: makeRuleId() })) }\n}\n\nexport class ModelProxyController {\n private listeners = new Set<Listener>()\n private snapshot: ModelProxySnapshot = { status: 'loading', writable: false }\n private unsub?: () => void\n\n constructor(private readonly scope: SettingsScope<ModelProxyConfig>) {}\n\n bind(): void {\n this.unsub = this.scope.subscribe(() => this.pull())\n this.pull()\n }\n\n dispose(): void {\n this.unsub?.()\n this.listeners.clear()\n }\n\n subscribe(l: Listener): () => void {\n this.listeners.add(l)\n return () => this.listeners.delete(l)\n }\n\n getSnapshot(): ModelProxySnapshot {\n return this.snapshot\n }\n\n private pull(): void {\n const s = this.scope.getSnapshot() as unknown as {\n status: string\n value?: ModelProxyConfig\n revision?: number\n writable: boolean\n }\n // status mapping: SettingsScope snapshot uses 'loading' | 'ready' | 'unavailable'\n this.snapshot = {\n status: (s.status as ModelProxySnapshot['status']) ?? 'loading',\n value: s.value,\n revision: s.revision,\n writable: s.writable,\n }\n this.emit()\n }\n\n private emit(): void {\n for (const l of [...this.listeners]) l()\n }\n\n /**\n * Persist the config as four fenced field writes.\n *\n * The scope contract exposes per-field writes only, so full atomicity would\n * require collapsing the namespace to a single field (a breaking settings\n * shape change). Until then we order the writes so the field most likely to\n * be REJECTED by host validation (`rules`) goes FIRST: if it fails, nothing\n * else has been committed and the failure aborts cleanly instead of leaving\n * mixed old/new state. The remaining fields are booleans plus a URL that the\n * card pre-validates with the same checks the host applies, making their\n * rejection practically unreachable.\n */\n async save(next: ModelProxyConfig): Promise<void> {\n await this.scope.set('rules', sanitizeRules(next.rules))\n await this.scope.set('defaultProxy', next.defaultProxy)\n await this.scope.set('enabled', next.enabled)\n await this.scope.set('debug', next.debug)\n }\n\n async setEnabled(v: boolean): Promise<void> {\n await this.scope.set('enabled', v)\n }\n\n async setDebug(v: boolean): Promise<void> {\n await this.scope.set('debug', v)\n }\n}\n", "export const en = {\n title: 'Model Proxy',\n desc: 'Route specific provider/model pairs through HTTP/HTTPS/SOCKS5 proxies. Specificity: exact model > prefix \"vendor-*\" > \"*\". Optional purpose filter targets compaction/session-title calls.',\n enabled: 'Enable proxy routing',\n debug: 'Debug logging (host log)',\n rules: 'Rules (first match wins \u2014 exact before prefix before \"*\")',\n addRule: 'Add rule',\n provider: 'Provider',\n providerPlaceholder: 'e.g. openai',\n model: 'Model',\n modelPlaceholder: \"e.g. gpt-4o-mini, vendor-* or *\",\n purpose: 'Purpose filter',\n purposePlaceholder: 'empty = all purposes (e.g. compaction, session-title)',\n credRef: 'Credential ref',\n credRefPlaceholder: 'credentials entry holding user:password (optional)',\n proxyUrl: 'Proxy URL',\n proxyPlaceholder: 'socks5://127.0.0.1:1080 or http://127.0.0.1:7890 (empty=direct)',\n enabledShort: 'On',\n delete: 'Delete',\n defaultProxy: 'Fallback proxy (when no rule matches)',\n defaultPlaceholder: 'optional, e.g. http://127.0.0.1:7890',\n saving: 'Saving\u2026',\n save: 'Save',\n saved: 'Saved',\n unavailable: 'Model proxy settings unavailable.',\n empty: 'No rules yet \u2014 add one above. Tip: use model \"*\" for all models of a provider.',\n invalid: 'Please fix highlighted fields.',\n duplicate: 'Duplicate provider+model',\n reading: 'Loading\u2026',\n readOnly: 'Read-only (settings provider not writable).',\n choose: 'Select\u2026',\n customOption: 'Custom\u2026',\n backToList: 'Pick from the installed catalog',\n dormant: 'not configured',\n groupConfigured: 'Configured providers',\n groupDirectory: 'Other directory routes',\n wildcardAll: 'any model',\n catalogUnavailable: 'Provider catalog unavailable \u2014 type values manually.',\n creatorTitle: 'New rules',\n creatorModels: 'Models',\n creatorPatternHint: 'Or a pattern (vendor-* or *)',\n createN: 'Create {n} rule(s)',\n cancel: 'Cancel',\n alreadyRuled: 'rule exists',\n noneCatalogued: 'No catalog models \u2014 use * or a custom pattern.',\n groupApplyProxy: 'proxy for all\u2026',\n groupApply: 'Apply',\n groupEnableAll: 'Enable all',\n groupDisableAll: 'Disable all',\n groupDelete: 'Delete group',\n groupConfirmDelete: 'Sure?',\n} as const\n\nexport const zh = {\n title: '\u6A21\u578B\u4EE3\u7406',\n desc: '\u6309 provider/model \u7C92\u5EA6\u8D70\u4EE3\u7406\uFF08HTTP/HTTPS/SOCKS5\uFF09\u3002\u4F18\u5148\u7EA7\uFF1A\u7CBE\u786E model > \u524D\u7F00 \"vendor-*\" > \"*\"\u3002\u53EF\u9009\u7528\u9014\u8FC7\u6EE4\u53EF\u5355\u72EC\u63A7\u5236 compaction/session-title \u7B49\u8C03\u7528\u3002',\n enabled: '\u542F\u7528\u4EE3\u7406\u8DEF\u7531',\n debug: '\u8C03\u8BD5\u65E5\u5FD7\uFF08Host \u65E5\u5FD7\uFF09',\n rules: '\u89C4\u5219\uFF08\u9996\u6761\u547D\u4E2D\u5373\u7528 \u2014 \u7CBE\u786E > \u524D\u7F00 > \"*\"\uFF09',\n addRule: '\u6DFB\u52A0\u89C4\u5219',\n provider: 'Provider',\n providerPlaceholder: '\u4F8B\u5982 openai',\n model: 'Model',\n modelPlaceholder: '\u4F8B\u5982 gpt-4o-mini\u3001vendor-* \u6216 *',\n purpose: '\u7528\u9014\u8FC7\u6EE4',\n purposePlaceholder: '\u7559\u7A7A=\u6240\u6709\u7528\u9014\uFF08\u5982 compaction\u3001session-title\uFF09',\n credRef: '\u51ED\u636E\u5F15\u7528',\n credRefPlaceholder: 'credentials \u670D\u52A1\u6761\u76EE\uFF08user:password\uFF09\uFF0C\u53EF\u9009',\n proxyUrl: '\u4EE3\u7406\u5730\u5740',\n proxyPlaceholder: 'socks5://127.0.0.1:1080 \u6216 http://127.0.0.1:7890\uFF08\u7A7A=\u76F4\u8FDE\uFF09',\n enabledShort: '\u542F\u7528',\n delete: '\u5220\u9664',\n defaultProxy: '\u515C\u5E95\u4EE3\u7406\uFF08\u65E0\u89C4\u5219\u547D\u4E2D\u65F6\uFF09',\n defaultPlaceholder: '\u53EF\u9009\uFF0C\u4F8B\u5982 http://127.0.0.1:7890',\n saving: '\u4FDD\u5B58\u4E2D\u2026',\n save: '\u4FDD\u5B58',\n saved: '\u5DF2\u4FDD\u5B58',\n unavailable: '\u6A21\u578B\u4EE3\u7406\u8BBE\u7F6E\u4E0D\u53EF\u7528\u3002',\n empty: '\u6682\u65E0\u89C4\u5219 \u2014 \u70B9\u51FB\u4E0A\u65B9\u6DFB\u52A0\u3002\u63D0\u793A\uFF1Amodel \u7528 \"*\" \u8868\u793A\u8BE5 provider \u7684\u6240\u6709\u6A21\u578B\u3002',\n invalid: '\u8BF7\u4FEE\u6B63\u6807\u7EA2\u5B57\u6BB5\u3002',\n duplicate: 'provider+model \u91CD\u590D',\n reading: '\u52A0\u8F7D\u4E2D\u2026',\n readOnly: '\u53EA\u8BFB\uFF08settings provider \u4E0D\u53EF\u5199\uFF09\u3002',\n choose: '\u8BF7\u9009\u62E9\u2026',\n customOption: '\u81EA\u5B9A\u4E49\u2026',\n backToList: '\u4ECE\u5DF2\u5B89\u88C5\u76EE\u5F55\u9009\u62E9',\n dormant: '\u672A\u914D\u7F6E',\n groupConfigured: '\u5DF2\u914D\u7F6E\u7684 Provider',\n groupDirectory: '\u76EE\u5F55\u4E2D\u7684\u5176\u4ED6\u8DEF\u7531',\n wildcardAll: '\u6240\u6709\u6A21\u578B',\n catalogUnavailable: '\u63D0\u4F9B\u65B9\u76EE\u5F55\u4E0D\u53EF\u7528 \u2014 \u8BF7\u624B\u52A8\u8F93\u5165\u3002',\n creatorTitle: '\u65B0\u5EFA\u89C4\u5219',\n creatorModels: '\u52FE\u9009\u6A21\u578B',\n creatorPatternHint: '\u6216\u81EA\u5B9A\u4E49\u6A21\u5F0F\uFF08vendor-*\u3001*\uFF09',\n createN: '\u521B\u5EFA {n} \u6761\u89C4\u5219',\n cancel: '\u53D6\u6D88',\n alreadyRuled: '\u5DF2\u6709\u89C4\u5219',\n noneCatalogued: '\u8BE5 provider \u65E0\u76EE\u5F55\u6A21\u578B\u2014\u2014\u53EF\u7528 * \u6216\u81EA\u5B9A\u4E49\u6A21\u5F0F\u3002',\n groupApplyProxy: '\u6574\u7EC4\u4EE3\u7406\u5730\u5740\u2026',\n groupApply: '\u5E94\u7528',\n groupEnableAll: '\u5168\u90E8\u542F\u7528',\n groupDisableAll: '\u5168\u90E8\u505C\u7528',\n groupDelete: '\u5220\u9664\u672C\u7EC4',\n groupConfirmDelete: '\u786E\u8BA4\uFF1F',\n} as const\n\nexport type LocaleKey = keyof typeof en\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgFA,IAAM,QAAyB,EAAE,QAAQ,QAAQ,WAAW,CAAC,GAAG,kBAAkB,CAAC,EAAE;AAErF,SAAS,SAAS,OAAqD;AACrE,SAAO,OAAO,UAAU,YAAY,UAAU,OAAQ,QAAoC;AAC5F;AAMO,SAAS,UAAU,UAA4B;AACpD,QAAM,WAAW,SAAS,QAAQ;AAClC,QAAM,SAAS,aAAa,SAAY,SAAY,SAAS,SAAS,MAAM;AAC5E,MAAI,WAAW,OAAW,OAAM,IAAI,MAAM,wBAAwB;AAClE,MAAI,OAAO,OAAO,MAAM;AACtB,UAAM,QAAQ,SAAS,OAAO,KAAK;AACnC,UAAM,IAAI,MAAM,OAAO,OAAO,YAAY,YAAY,MAAM,YAAY,KAAK,MAAM,UAAU,oBAAoB;AAAA,EACnH;AACA,SAAO,OAAO;AAChB;AAOO,SAAS,cAAc,OAAmC;AAC/D,QAAM,OAAO,SAAS,KAAK;AAC3B,QAAM,OAAO,MAAM,QAAQ,MAAM,SAAS,IAAI,KAAK,YAAY,CAAC;AAChE,QAAM,MAAyB,CAAC;AAChC,aAAW,OAAO,MAAM;AACtB,UAAM,IAAI,SAAS,GAAG;AACtB,QAAI,MAAM,UAAa,OAAO,EAAE,aAAa,YAAY,EAAE,aAAa,GAAI;AAC5E,UAAM,IAAqB;AAAA,MACzB,UAAU,EAAE;AAAA,MACZ,aAAa,OAAO,EAAE,gBAAgB,YAAY,EAAE,gBAAgB,KAAK,EAAE,cAAc,EAAE;AAAA,MAC3F,QAAQ,EAAE,WAAW;AAAA,IACvB;AACA,QAAI,OAAO,EAAE,eAAe,YAAY,EAAE,eAAe,IAAI;AAC3D,QAAE,aAAa,EAAE;AACjB,UAAI,MAAM,QAAQ,EAAE,YAAY,GAAG;AACjC,UAAE,eAAe,EAAE,aAAa,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AAAA,MAClF;AAAA,IACF;AACA,QAAI,EAAE,aAAa,QAAQ,EAAE,aAAa,MAAO,GAAE,WAAW,EAAE;AAChE,QAAI,KAAK,CAAC;AAAA,EACZ;AACA,SAAO;AACT;AAGO,SAAS,QAAQ,OAAgB,MAAkC;AACxE,MAAI,MAAe;AACnB,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,SAAS,GAAG;AACxB,QAAI,QAAQ,OAAW,QAAO;AAC9B,UAAM,IAAI,GAAG;AAAA,EACf;AACA,SAAO;AACT;AASO,SAAS,iBACd,WACA,YACmB;AACnB,QAAM,OAAO,oBAAI,IAAqB;AACtC,aAAW,KAAK,YAAY;AAC1B,QAAI,OAAO,EAAE,OAAO,YAAY,CAAE,KAAK,IAAI,EAAE,EAAE,EAAI,MAAK,IAAI,EAAE,IAAI,EAAE,KAAK;AAAA,EAC3E;AACA,SAAO,UAAU,IAAI,CAAC,MAAM;AAC1B,QAAI,EAAE,eAAe,UAAa,CAAC,KAAK,IAAI,EAAE,UAAU,EAAG,QAAO;AAClE,UAAM,QAAQ,KAAK,IAAI,EAAE,UAAU;AACnC,UAAM,OAAO,EAAE,gBAAgB,CAAC;AAChC,UAAM,aAAa,KAAK,WAAW,IAAI,UAAU,SAAY,QAAQ,OAAO,IAAI,MAAM;AACtF,WAAO,EAAE,GAAG,GAAG,WAAW;AAAA,EAC5B,CAAC;AACH;AAOO,SAAS,qBAAqB,OAAgD;AACnF,QAAM,OAAO,SAAS,KAAK;AAC3B,QAAM,SAAS,MAAM,QAAQ,MAAM,MAAM,IAAI,KAAK,SAAS,CAAC;AAC5D,QAAM,MAAsC,CAAC;AAC7C,aAAW,SAAS,QAAQ;AAC1B,UAAM,IAAI,SAAS,KAAK;AACxB,QAAI,MAAM,UAAa,OAAO,EAAE,OAAO,YAAY,EAAE,OAAO,MAAM,CAAC,MAAM,QAAQ,EAAE,MAAM,EAAG;AAC5F,UAAM,SAAyB,CAAC;AAChC,eAAW,OAAO,EAAE,QAAQ;AAC1B,YAAM,IAAI,SAAS,GAAG;AACtB,UAAI,MAAM,UAAa,OAAO,EAAE,OAAO,YAAY,EAAE,OAAO,GAAI;AAChE,aAAO,KAAK,EAAE,IAAI,EAAE,IAAI,GAAI,OAAO,EAAE,SAAS,YAAY,EAAE,SAAS,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC,EAAG,CAAC;AAAA,IACpG;AACA,QAAI,EAAE,EAAE,IAAI;AAAA,EACd;AACA,SAAO;AACT;AAQO,IAAM,uBAAN,MAA2B;AAAA,EAOhC,YACmB,KACA,QACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EARF,YAAY,oBAAI,IAAc;AAAA,EACvC,WAA4B;AAAA,EAC5B,aAAa;AAAA,EACb,SAA4B,CAAC;AAAA,EAC7B,QAAQ;AAAA;AAAA,EAQhB,OAAa;AACX,QAAI,KAAK,MAAO;AAChB,SAAK,QAAQ;AACb,QAAI,KAAK,WAAW,QAAW;AAC7B,WAAK,OAAO,KAAK,KAAK,OAAO,IAAI,wBAAwB,MAAM,KAAK,KAAK,KAAK,CAAC,CAAC;AAChF,WAAK,OAAO,KAAK,KAAK,OAAO,IAAI,6BAA6B,MAAM,KAAK,KAAK,KAAK,CAAC,CAAC;AAAA,IACvF;AACA,SAAK,KAAK,KAAK;AAAA,EACjB;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,QAAQ;AACb,eAAW,OAAO,KAAK,OAAO,OAAO,CAAC,GAAG;AACvC,UAAI;AACF,YAAI;AAAA,MACN,QAAQ;AAAA,MAER;AAAA,IACF;AACA,SAAK;AACL,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEA,UAAU,UAAgC;AACxC,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM;AACX,WAAK,UAAU,OAAO,QAAQ;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,cAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAsB;AAC1B,UAAM,aAAa,EAAE,KAAK;AAC1B,QAAI,KAAK,SAAS,WAAW,OAAQ,MAAK,IAAI,EAAE,GAAG,OAAO,QAAQ,UAAU,CAAC;AAC7E,UAAM,SAAS,CAAI,YACjB,QAAQ;AAAA,MACN,CAAC,WAAW,EAAE,IAAI,MAAe,MAAM;AAAA,MACvC,OAAO,EAAE,IAAI,MAAe;AAAA,IAC9B;AACF,UAAM,CAAC,kBAAkB,eAAe,eAAe,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3E,OAAO,KAAK,IAAI,IAAI,UAAU,CAAC,CAAC,CAAC;AAAA,MACjC,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC;AAAA;AAAA,MAE9B,OAAO,KAAK,IAAI,UAAU,SAAS,CAAC,CAAC,KAAK,QAAQ,OAAO,IAAI,MAAM,kBAAkB,CAAC,CAAC;AAAA,IACzF,CAAC;AACD,QAAI,eAAe,KAAK,WAAY;AACpC,QAAI;AACJ,QAAI,CAAC,iBAAiB,IAAI;AACxB,WAAK,gBAAgB;AACrB;AAAA,IACF;AACA,QAAI;AAGF,kBAAY,cAAc,UAAU,iBAAiB,KAAK,CAAC;AAAA,IAC7D,QAAQ;AACN,WAAK,gBAAgB;AACrB;AAAA,IACF;AACA,QAAI,mBAAmD,CAAC;AACxD,QAAI,cAAc,IAAI;AACpB,UAAI;AACF,2BAAmB,qBAAqB,UAAU,cAAc,KAAK,CAAC;AAAA,MACxE,QAAQ;AACN,2BAAmB,CAAC;AAAA,MACtB;AAAA,IACF;AACA,QAAI,gBAAgB,IAAI;AACtB,UAAI;AACF,cAAM,YAAY,UAAU,gBAAgB,KAAK;AACjD,cAAM,aAAa,MAAM,QAAQ,WAAW,UAAU,IAAI,UAAU,aAAa,CAAC;AAClF,oBAAY;AAAA,UACV;AAAA,UACA,WAAW,IAAI,CAAC,MAAM;AACpB,kBAAM,IAAI,SAAS,CAAC;AACpB,mBAAO,EAAE,IAAI,GAAG,IAAI,OAAO,GAAG,MAAM;AAAA,UACtC,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AACA,SAAK,IAAI,EAAE,QAAQ,SAAS,WAAW,iBAAiB,CAAC;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAwB;AAC9B,SAAK,IAAI,EAAE,GAAG,KAAK,UAAU,QAAQ,cAAc,CAAC;AAAA,EACtD;AAAA,EAEQ,IAAI,UAAiC;AAC3C,SAAK,WAAW;AAChB,eAAW,YAAY,CAAC,GAAG,KAAK,SAAS,EAAG,UAAS;AAAA,EACvD;AACF;;;AC1TA,mBAAgF;;;ACuChF,IAAM,oBAAoB,CAAC,MAAM,YAAY,SAAS,YAAY,WAAW,WAAW,eAAe;AAYhG,SAAS,cAAc,OAAiC;AAC7D,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,MAA+B,CAAC;AACtC,UAAM,UAAoB,CAAC;AAC3B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,UAAK,kBAAwC,SAAS,GAAG,EAAG,KAAI,GAAG,IAAI;AAAA,UAClE,SAAQ,KAAK,GAAG;AAAA,IACvB;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,KAAK,iDAAiD,QAAQ,KAAK,IAAI,CAAC,EAAE;AAAA,IACpF;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,aAAqB;AACnC,SAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;AAC9C;AAkBO,SAAS,kBAAkB,MAAgC;AAChE,QAAM,WAAW,KAAK,SAAS,KAAK;AACpC,MAAI,aAAa,GAAI,QAAO,CAAC;AAC7B,QAAM,SAAS,CAAC,GAAG,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC;AACpF,MAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AACjC,QAAM,WAAW,KAAK,WAAW,IAAI,KAAK;AAC1C,QAAM,iBAAiB,KAAK,iBAAiB,IAAI,KAAK;AACtD,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B,IAAI,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA,UAAU,KAAK,SAAS,KAAK;AAAA,IAC7B,SAAS,KAAK,WAAW;AAAA,IACzB,GAAI,YAAY,KAAK,EAAE,QAAQ,IAAI,CAAC;AAAA,IACpC,GAAI,kBAAkB,KAAK,EAAE,cAAc,IAAI,CAAC;AAAA,EAClD,EAAE;AACJ;AAQO,SAAS,gBAAgB,OAAqE;AACnG,QAAM,SAA0D,CAAC;AACjE,QAAM,QAAQ,oBAAI,IAAoB;AACtC,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,MAAM,IAAI,KAAK,QAAQ;AAChC,QAAI,OAAO,QAAW;AACpB,WAAK,OAAO;AACZ,YAAM,IAAI,KAAK,UAAU,EAAE;AAC3B,aAAO,KAAK,EAAE,UAAU,KAAK,UAAU,OAAO,CAAC,EAAE,CAAC;AAAA,IACpD;AACA,WAAO,EAAE,EAAG,MAAM,KAAK,IAAI;AAAA,EAC7B;AACA,SAAO;AACT;AAOO,SAAS,cAAc,KAAyC;AACrE,SAAO,EAAE,GAAG,KAAK,OAAO,IAAI,MAAM,IAAI,CAAC,MAAO,EAAE,KAAK,IAAI,EAAE,GAAG,GAAG,IAAI,WAAW,EAAE,CAAE,EAAE;AACxF;AAEO,IAAM,uBAAN,MAA2B;AAAA,EAKhC,YAA6B,OAAwC;AAAxC;AAAA,EAAyC;AAAA,EAAzC;AAAA,EAJrB,YAAY,oBAAI,IAAc;AAAA,EAC9B,WAA+B,EAAE,QAAQ,WAAW,UAAU,MAAM;AAAA,EACpE;AAAA,EAIR,OAAa;AACX,SAAK,QAAQ,KAAK,MAAM,UAAU,MAAM,KAAK,KAAK,CAAC;AACnD,SAAK,KAAK;AAAA,EACZ;AAAA,EAEA,UAAgB;AACd,SAAK,QAAQ;AACb,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEA,UAAU,GAAyB;AACjC,SAAK,UAAU,IAAI,CAAC;AACpB,WAAO,MAAM,KAAK,UAAU,OAAO,CAAC;AAAA,EACtC;AAAA,EAEA,cAAkC;AAChC,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,OAAa;AACnB,UAAM,IAAI,KAAK,MAAM,YAAY;AAOjC,SAAK,WAAW;AAAA,MACd,QAAS,EAAE,UAA2C;AAAA,MACtD,OAAO,EAAE;AAAA,MACT,UAAU,EAAE;AAAA,MACZ,UAAU,EAAE;AAAA,IACd;AACA,SAAK,KAAK;AAAA,EACZ;AAAA,EAEQ,OAAa;AACnB,eAAW,KAAK,CAAC,GAAG,KAAK,SAAS,EAAG,GAAE;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,KAAK,MAAuC;AAChD,UAAM,KAAK,MAAM,IAAI,SAAS,cAAc,KAAK,KAAK,CAAC;AACvD,UAAM,KAAK,MAAM,IAAI,gBAAgB,KAAK,YAAY;AACtD,UAAM,KAAK,MAAM,IAAI,WAAW,KAAK,OAAO;AAC5C,UAAM,KAAK,MAAM,IAAI,SAAS,KAAK,KAAK;AAAA,EAC1C;AAAA,EAEA,MAAM,WAAW,GAA2B;AAC1C,UAAM,KAAK,MAAM,IAAI,WAAW,CAAC;AAAA,EACnC;AAAA,EAEA,MAAM,SAAS,GAA2B;AACxC,UAAM,KAAK,MAAM,IAAI,SAAS,CAAC;AAAA,EACjC;AACF;;;AChNO,IAAM,KAAK;AAAA,EAChB,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AAAA,EACV,qBAAqB;AAAA,EACrB,OAAO;AAAA,EACP,kBAAkB;AAAA,EAClB,SAAS;AAAA,EACT,oBAAoB;AAAA,EACpB,SAAS;AAAA,EACT,oBAAoB;AAAA,EACpB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,OAAO;AAAA,EACP,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,oBAAoB;AACtB;AAEO,IAAM,KAAK;AAAA,EAChB,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AAAA,EACV,qBAAqB;AAAA,EACrB,OAAO;AAAA,EACP,kBAAkB;AAAA,EAClB,SAAS;AAAA,EACT,oBAAoB;AAAA,EACpB,SAAS;AAAA,EACT,oBAAoB;AAAA,EACpB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,OAAO;AAAA,EACP,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,oBAAoB;AACtB;;;AFpDM;AArCN,IAAM,aAA8B,EAAE,QAAQ,QAAQ,WAAW,CAAC,GAAG,kBAAkB,CAAC,EAAE;AAG1F,IAAM,kBAAkB;AAiBxB,SAAS,aAAa,OASnB;AACD,QAAM,EAAE,OAAO,QAAQ,UAAU,UAAU,aAAa,aAAa,WAAW,iBAAiB,IAAI;AACrG,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,MAAM,UAAU,MAAM,CAAC,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,KAAK,CAAC,MAAM,EAAE,UAAU,KAAK,CAAC,CAAC;AAChI,QAAM,OAAO,OAAO,QAAQ,CAAC,MAAM,EAAE,OAAO;AAC5C,QAAM,aAAa,KAAK,SAAS,KAAK,CAAC;AAEvC,MAAI,KAAK,WAAW,GAAG;AACrB,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,aAAa;AAAA,QACb,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,QACxC,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,kBAAkB,OAAO,QAAQ,WAAW,aAAa;AAAA,QAC/G;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SACE,4CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,YAAY,SAAS,GACzD,uBACC;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,KAAK,KAAK,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,QAAQ;AAAA,MACrD,UAAU,CAAC,MAAM;AACf,YAAI,EAAE,OAAO,UAAU,iBAAiB;AACtC,wBAAc,IAAI;AAClB;AAAA,QACF;AACA,iBAAS,EAAE,OAAO,KAAK;AAAA,MACzB;AAAA,MACA,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,kBAAkB,MAAM,GAAG,UAAU,EAAE;AAAA,MAC7F;AAAA,MAEC;AAAA,SAAC,KAAK,KAAK,CAAC,MAAM,EAAE,UAAU,KAAK,KAAK,4CAAC,YAAO,OAAM,IAAI,uBAAY;AAAA,QACtE,OAAO;AAAA,UAAI,CAAC,GAAG,OACd,EAAE,UAAU,SACV,EAAE,QAAQ,IAAI,CAAC,MAAM,4CAAC,YAAqB,OAAO,EAAE,OAAQ,YAAE,SAA5B,EAAE,KAAgC,CAAS,IAE7E,4CAAC,cAAkC,OAAO,EAAE,OACzC,YAAE,QAAQ,IAAI,CAAC,MACd,4CAAC,YAAqB,OAAO,EAAE,OAAQ,YAAE,SAA5B,EAAE,KAAgC,CAChD,KAHY,GAAG,EAAE,IAAI,EAAE,KAAK,EAI/B;AAAA,QAEJ;AAAA,QACA,4CAAC,YAAO,OAAO,iBAAkB,uBAAY;AAAA;AAAA;AAAA,EAC/C,IAEA,4EACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,aAAa;AAAA,QACb,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,QACxC,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,kBAAkB,MAAM,GAAG,UAAU,EAAE;AAAA,QAC7F;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,OAAO;AAAA,QACP,SAAS,MAAM,cAAc,KAAK;AAAA,QAClC;AAAA,QACA,OAAO;AAAA,UACL,SAAS;AAAA,UACT,cAAc;AAAA,UACd,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,QAAQ;AAAA,UACR,YAAY;AAAA,QACd;AAAA,QACD;AAAA;AAAA,IAED;AAAA,KACF,GAEJ;AAEJ;AAGA,IAAM,oBAAoB,CAAC,SAAS,UAAU,WAAW,YAAY,QAAQ;AAY7E,IAAM,gBAA8B;AAAA,EAClC,UAAU;AAAA,EACV,SAAS,CAAC;AAAA,EACV,eAAe;AAAA,EACf,UAAU;AAAA,EACV,SAAS;AAAA,EACT,eAAe;AACjB;AAEA,SAAS,YAAY,QAAoC;AACvD,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,MAAM;AACxB,QAAI,CAAC,kBAAkB,SAAS,EAAE,QAAQ,EAAG,QAAO;AACpD,QAAI,CAAC,EAAE,SAAU,QAAO;AACxB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,GAAc,KAAsC;AACxE,MAAI,EAAE,aAAa,EAAE,SAAS,KAAK,EAAG,QAAO;AAC7C,MAAI,EAAE,UAAU,EAAE,MAAM,KAAK,EAAG,QAAO;AACvC,MAAI,EAAE,YAAY,UAAa,EAAE,YAAY,MAAM,EAAE,YAAY,EAAE,QAAQ,KAAK,EAAG,QAAO;AAC1F,MAAI,EAAE,kBAAkB,UAAa,EAAE,kBAAkB,MAAM,EAAE,kBAAkB,EAAE,cAAc,KAAK,EAAG,QAAO;AAClH,MAAI,CAAC,EAAE,SAAS,KAAK,EAAG,QAAO;AAC/B,MAAI,CAAC,EAAE,MAAM,KAAK,EAAG,QAAO;AAE5B,QAAM,aAAa,EAAE,MAAM,MAAM,KAAK,KAAK,CAAC,GAAG;AAC/C,QAAM,iBAAiB,EAAE,UAAU;AACnC,MAAI,CAAC,kBAAkB,YAAY,KAAK,EAAE,cAAc,KAAK,EAAE,MAAM,SAAS,GAAG,IAAI;AACnF,WAAO;AAAA,EACT;AACA,MAAI,EAAE,aAAa,IAAI;AACrB,UAAM,MAAM,YAAY,EAAE,QAAQ;AAClC,QAAI,IAAK,QAAO;AAAA,EAClB;AACA,QAAM,MAAM,IAAI,OAAO,OAAK,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS;AACvF,MAAI,IAAK,QAAO;AAChB,SAAO;AACT;AAGO,SAAS,qBAAqB,cAA0C;AAC7E,MAAI,iBAAiB,GAAI,QAAO;AAChC,SAAO,YAAY,YAAY;AACjC;AAQA,SAAS,YAAY,OAUlB;AACD,QAAM,EAAE,UAAU,aAAa,OAAO,YAAY,UAAU,GAAG,cAAc,cAAc,SAAS,IAAI;AACxG,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAElD,8BAAU,MAAM;AACd,QAAI,CAAC,WAAY;AACjB,UAAM,QAAQ,WAAW,MAAM,cAAc,KAAK,GAAG,GAAI;AACzD,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,6CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,UAAU,QAAQ,KAAK,GAAG,YAAY,SAAS,GAC5E;AAAA,iDAAC,UAAK,OAAO,EAAE,YAAY,IAAI,GAC5B;AAAA,sBAAgB,WAAW,WAAW,GAAG,WAAW,SAAM,QAAQ;AAAA,MACnE,4CAAC,UAAK,OAAO,EAAE,YAAY,KAAK,SAAS,KAAK,YAAY,EAAE,GAAI,iBAAM;AAAA,OACxE;AAAA,IACA,4CAAC,UAAK,OAAO,EAAE,MAAM,EAAE,GAAG;AAAA,IAC1B;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,aAAa,EAAE,iBAAiB;AAAA,QAChC,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,QACxC,WAAW,CAAC,MAAM;AAChB,cAAI,EAAE,QAAQ,QAAS,cAAa,MAAM,KAAK,CAAC;AAAA,QAClD;AAAA,QACA,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,kBAAkB,OAAO,IAAI;AAAA,QACnF;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QAAO,MAAK;AAAA,QAAS,SAAS,MAAM,aAAa,MAAM,KAAK,CAAC;AAAA,QAAG,UAAU,YAAY,MAAM,KAAK,MAAM;AAAA,QACtG,OAAO;AAAA,QACN,YAAE,YAAY;AAAA;AAAA,IACjB;AAAA,IACA,4CAAC,YAAO,MAAK,UAAS,SAAS,MAAM,aAAa,CAAC,UAAU,GAAG,UAAoB,OAAO,kBACxF,uBAAa,EAAE,iBAAiB,IAAI,EAAE,gBAAgB,GACzD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM;AACb,cAAI,YAAY;AACd,0BAAc,KAAK;AACnB,qBAAS;AAAA,UACX,OAAO;AACL,0BAAc,IAAI;AAAA,UACpB;AAAA,QACF;AAAA,QACA;AAAA,QACA,OAAO;AAAA,UACL,GAAG;AAAA,UACH,aAAa,aAAa,SAAS;AAAA,UACnC,OAAO,aAAa,SAAS;AAAA,QAC/B;AAAA,QAEC,uBAAa,EAAE,oBAAoB,IAAI,EAAE,aAAa;AAAA;AAAA,IACzD;AAAA,KACF;AAEJ;AAEA,IAAM,mBAAkC;AAAA,EACtC,SAAS;AAAA,EACT,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,YAAY;AACd;AAEO,SAAS,eAAe,EAAE,YAAY,SAAS,GAAG,MAAM,GAAU;AACvE,QAAM,IAAI,UAAU,CAAC,MAAuB,GAAG,CAAC;AAChD,QAAM,WAAO;AAAA,IACX,CAAC,OAAO,WAAW,UAAU,EAAE;AAAA,IAC/B,MAAM,WAAW,YAAY;AAAA,IAC7B,MAAM,WAAW,YAAY;AAAA,EAC/B;AACA,QAAM,UAAM;AAAA,QACV,0BAAY,CAAC,OAAoB,UAAU,QAAQ,UAAU,EAAE,IAAI,MAAM,QAAY,CAAC,OAAO,CAAC;AAAA,QAC9F,0BAAY,MAAM,SAAS,YAAY,KAAK,YAAY,CAAC,OAAO,CAAC;AAAA,QACjE,0BAAY,MAAM,SAAS,YAAY,KAAK,YAAY,CAAC,OAAO,CAAC;AAAA,EACnE;AAEA,QAAM,UAAwB,sBAAQ,MAAM;AAC1C,WAAO;AAAA,MACL,KAAK,SAAS;AAAA,QACZ,SAAS;AAAA,QACT,OAAO,CAAC;AAAA,QACR,cAAc;AAAA,QACd,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,GAAG,CAAC,KAAK,KAAK,CAAC;AAEf,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAA2B,GAAG;AACxD,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAS,KAAK;AAC1C,QAAM,CAAC,KAAK,MAAM,QAAI,uBAA6B;AAEnD,QAAM,YAAQ,sBAAQ,MAAM,KAAK,UAAU,KAAK,MAAM,KAAK,UAAU,GAAG,GAAG,CAAC,OAAO,GAAG,CAAC;AAKvF,8BAAU,MAAM;AACd,QAAI,CAAC,MAAO,UAAS,GAAG;AAAA,EAE1B,GAAG,CAAC,GAAG,CAAC;AAGR,8BAAU,MAAM;AACd,QAAI,CAAC,OAAO,QAAQ,EAAE,OAAO,EAAG;AAChC,UAAM,QAAQ,WAAW,MAAM,OAAO,MAAS,GAAG,IAAI;AACtD,WAAO,MAAM,aAAa,KAAK;AAAA,EAEjC,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,wBAAoB,sBAAQ,MAAM,qBAAqB,MAAM,YAAY,GAAG,CAAC,MAAM,YAAY,CAAC;AAStG,QAAM,qBAA+B,sBAAQ,MAAM;AACjD,UAAM,OAAsB,IAAI,UAAU,IAAI,CAAC,OAAO;AAAA,MACpD,OAAO,EAAE;AAAA,MACT,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,GAAG,EAAE,WAAW,KAAK,EAAE,QAAQ,IAAI,EAAE,SAAS,KAAK,SAAM,EAAE,SAAS,CAAC,EAAE;AAAA,IAC5H,EAAE;AACF,QAAI,CAAC,IAAI,UAAU,KAAK,CAAC,MAAM,EAAE,eAAe,MAAS,EAAG,QAAO,CAAC,EAAE,SAAS,KAAK,CAAC;AACrF,UAAM,aAA4B,CAAC;AACnC,UAAM,YAA2B,CAAC;AAClC,aAAS,IAAI,GAAG,IAAI,IAAI,UAAU,QAAQ,KAAK;AAC7C,YAAM,IAAI,IAAI,UAAU,CAAC;AACxB,OAAC,EAAE,eAAe,OAAO,aAAa,WAAW,KAAK,KAAK,CAAC,CAAE;AAAA,IACjE;AACA,WAAO;AAAA,MACL,GAAI,WAAW,SAAS,IAAI,CAAC,EAAE,OAAO,EAAE,iBAAiB,GAAG,SAAS,WAAW,CAAC,IAAI,CAAC;AAAA,MACtF,GAAI,UAAU,SAAS,IAAI,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,SAAS,UAAU,CAAC,IAAI,CAAC;AAAA,IACrF;AAAA,EACF,GAAG,CAAC,KAAK,CAAC,CAAC;AACX,QAAM,sBAAkB;AAAA,IACtB,CAAC,aAAoC;AACnC,YAAM,SAAS,IAAI,iBAAiB,QAAQ,KAAK,CAAC;AAClD,UAAI,OAAO,WAAW,EAAG,QAAO,CAAC;AACjC,YAAM,WAAwB,EAAE,OAAO,KAAK,OAAO,MAAM,EAAE,aAAa,CAAC,IAAI;AAC7E,aAAO;AAAA,QACL,GAAI,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ;AAAA,QACrD,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,IAAI,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE;AAAA,MACzG;AAAA,IACF;AAAA,IACA,CAAC,KAAK,CAAC;AAAA,EACT;AACA,QAAM,iBACJ,YAAY,UAAa,IAAI,WAAW,iBAAiB,IAAI,UAAU,WAAW;AAEpF,QAAM,aAAS,0BAAY,YAAY;AAGrC,eAAW,KAAK,MAAM,OAAO;AAC3B,YAAM,MAAM,aAAa,GAAG,MAAM,KAAK;AACvC,UAAI,KAAK;AACP,eAAO,EAAE,SAAS,CAAC;AACnB;AAAA,MACF;AAAA,IACF;AACA,QAAI,mBAAmB;AACrB,aAAO,EAAE,SAAS,CAAC;AACnB;AAAA,IACF;AACA,cAAU,IAAI;AACd,WAAO,MAAS;AAChB,QAAI;AACF,YAAM,WAAW,KAAK,KAAK;AAC3B,aAAO,EAAE,OAAO,CAAC;AAAA,IACnB,SAAS,GAAG;AACV,aAAO,OAAO,CAAC,CAAC;AAAA,IAClB,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,OAAO,YAAY,GAAG,iBAAiB,CAAC;AAE5C,QAAM,eAAW;AAAA,IACf,CAAC,UAAqC,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,MAAM,EAAE;AAAA,IAC1E,CAAC;AAAA,EACH;AAEA,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,KAAK;AACpD,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAuB,aAAa;AAClE,QAAM,CAAC,cAAc,eAAe,QAAI,uBAA6B;AAErE,QAAM,sBAAkB;AAAA,IACtB,CAAC,UAAiC,WAAW,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,MAAM,EAAE;AAAA,IACxE,CAAC;AAAA,EACH;AAIA,QAAM,iBAAa;AAAA,IACjB,CAAC,UAAkB,UAAkB,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,aAAa,YAAY,EAAE,UAAU,KAAK;AAAA,IACzG,CAAC,MAAM,KAAK;AAAA,EACd;AAEA,QAAM,wBAAoB,sBAAQ,MAAM;AACtC,QAAI,QAAQ,SAAS,KAAK,MAAM,GAAI,QAAO;AAC3C,UAAM,SAAS,QAAQ,cAAc,KAAK;AAC1C,WAAO,QAAQ,QAAQ,UAAU,WAAW,KAAK,IAAI;AAAA,EACvD,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,kBAAc,0BAAY,MAAM;AACpC,UAAM,SAAS,CAAC,GAAG,QAAQ,OAAO;AAClC,UAAM,SAAS,QAAQ,cAAc,KAAK;AAC1C,QAAI,WAAW,GAAI,QAAO,KAAK,MAAM;AACrC,UAAM,WAAW,kBAAkB;AAAA,MACjC,UAAU,QAAQ;AAAA,MAClB;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,SAAS,QAAQ;AAAA,MACjB,eAAe,QAAQ;AAAA,MACvB,SAAS;AAAA,IACX,CAAC;AACD,UAAM,WAAW,CAAC,GAAG,UAAU,GAAG,MAAM,KAAK;AAC7C,eAAW,KAAK,UAAU;AACxB,YAAM,MAAM,aAAa,GAAG,QAAQ;AACpC,UAAI,KAAK;AACP,wBAAgB,QAAQ,cAAc,EAAE,WAAW,IAAI,GAAG;AAC1D;AAAA,MACF;AAAA,IACF;AACA,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,UAAU,GAAG,EAAE,KAAK,EAAE,EAAE;AAC5D,eAAW,aAAa;AACxB,oBAAgB,MAAS;AACzB,mBAAe,KAAK;AAAA,EACtB,GAAG,CAAC,SAAS,MAAM,OAAO,CAAC,CAAC;AAE5B,QAAM,sBAAkB,0BAAY,CAAC,UAAkB,QAAgB;AACrE,aAAS,CAAC,OAAO;AAAA,MACf,GAAG;AAAA,MACH,OAAO,EAAE,MAAM,IAAI,CAAC,MAAO,EAAE,aAAa,WAAW,EAAE,GAAG,GAAG,UAAU,IAAI,IAAI,CAAE;AAAA,IACnF,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAkB,0BAAY,CAAC,UAAkB,YAAqB;AAC1E,aAAS,CAAC,OAAO;AAAA,MACf,GAAG;AAAA,MACH,OAAO,EAAE,MAAM,IAAI,CAAC,MAAO,EAAE,aAAa,WAAW,EAAE,GAAG,GAAG,QAAQ,IAAI,CAAE;AAAA,IAC7E,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,QAAM,kBAAc,0BAAY,CAAC,aAAqB;AACpD,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ,EAAE,EAAE;AAAA,EACnF,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAa,0BAAY,CAAC,IAAY,UAA8B;AACxE,aAAS,CAAC,OAAO;AAAA,MACf,GAAG;AAAA,MACH,OAAO,EAAE,MAAM,IAAI,CAAC,MAAO,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,GAAG,MAAM,IAAI,CAAE;AAAA,IAClE,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAa,0BAAY,CAAC,OAAe;AAC7C,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;AAAA,EACvE,GAAG,CAAC,CAAC;AAEL,MAAI,KAAK,WAAW,UAAW,QAAO,4CAAC,SAAI,OAAO,EAAE,SAAS,IAAI,SAAS,IAAI,GAAI,YAAE,SAAS,GAAE;AAC/F,MAAI,KAAK,WAAW,cAAe,QAAO,4CAAC,SAAI,OAAO,EAAE,SAAS,GAAG,GAAI,YAAE,aAAa,GAAE;AAEzF,SACE,6CAAC,SAAI,OAAO,EAAE,SAAS,IAAI,SAAS,QAAQ,eAAe,UAAU,KAAK,GAAG,GAC3E;AAAA,iDAAC,SACC;AAAA,kDAAC,SAAI,OAAO,EAAE,YAAY,KAAK,UAAU,GAAG,GAAI,YAAE,OAAO,GAAE;AAAA,MAC3D,4CAAC,SAAI,OAAO,EAAE,SAAS,KAAK,UAAU,IAAI,WAAW,EAAE,GAAI,YAAE,MAAM,GAAE;AAAA,MACpE,CAAC,KAAK,YAAY,4CAAC,SAAI,OAAO,EAAE,OAAO,QAAQ,UAAU,IAAI,WAAW,EAAE,GAAI,YAAE,UAAU,GAAE;AAAA,MAC5F,kBAAkB,4CAAC,SAAI,OAAO,EAAE,SAAS,KAAK,UAAU,IAAI,WAAW,EAAE,GAAI,YAAE,oBAAoB,GAAE;AAAA,OACxG;AAAA,IAEA,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,YAAY,SAAS,GAC5D;AAAA,kDAAC,WAAM,MAAK,YAAW,SAAS,MAAM,SAAS,UAAU,CAAC,MAAM,SAAS,EAAE,SAAS,EAAE,OAAO,QAAQ,CAAC,GAAG,UAAU,CAAC,KAAK,UAAU;AAAA,MACnI,4CAAC,UAAM,YAAE,SAAS,GAAE;AAAA,OACtB;AAAA,IAEA,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,YAAY,SAAS,GAC5D;AAAA,kDAAC,WAAM,MAAK,YAAW,SAAS,MAAM,OAAO,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,EAAE,OAAO,QAAQ,CAAC,GAAG,UAAU,CAAC,KAAK,UAAU;AAAA,MAC/H,4CAAC,UAAM,YAAE,OAAO,GAAE;AAAA,OACpB;AAAA,IAEA,6CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,YAAY,SAAS,GACnF;AAAA,kDAAC,SAAI,OAAO,EAAE,YAAY,IAAI,GAAI,YAAE,OAAO,GAAE;AAAA,MAC7C;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM;AACb,2BAAe,CAAC,MAAM,CAAC,CAAC;AACxB,4BAAgB,MAAS;AAAA,UAC3B;AAAA,UACA,UAAU,CAAC,KAAK;AAAA,UAChB,OAAO,EAAE,SAAS,YAAY,cAAc,GAAG,QAAQ,kBAAkB,YAAY,QAAQ,QAAQ,UAAU;AAAA,UAChH;AAAA;AAAA,YACI,EAAE,SAAS;AAAA;AAAA;AAAA,MAChB;AAAA,OACF;AAAA,IAEC,MAAM,MAAM,WAAW,KAAK,CAAC,eAAe,4CAAC,SAAI,OAAO,EAAE,SAAS,KAAK,UAAU,GAAG,GAAI,YAAE,OAAO,GAAE;AAAA,IAEpG,eACC,6CAAC,SAAI,OAAO,EAAE,QAAQ,kBAAkB,cAAc,IAAI,SAAS,IAAI,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GACtH;AAAA,kDAAC,SAAI,OAAO,EAAE,YAAY,IAAI,GAAI,YAAE,cAAc,GAAE;AAAA,MACpD,6CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,qBAAqB,WAAW,KAAK,EAAE,GACpE;AAAA,qDAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,sDAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,UAAU,GAAE;AAAA,UAC5D;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,QAAQ;AAAA,cACf,QAAQ;AAAA,cACR,UAAU,CAAC,MAAM,gBAAgB,EAAE,UAAU,EAAE,CAAC;AAAA,cAChD,UAAU,CAAC,KAAK;AAAA,cAChB,aAAa,EAAE,QAAQ;AAAA,cACvB,aAAa,EAAE,cAAc;AAAA,cAC7B,WAAW,EAAE,YAAY;AAAA,cACzB,kBAAkB,EAAE,qBAAqB;AAAA;AAAA,UAC3C;AAAA,WACF;AAAA,QACA,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,sDAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,UAAU,GAAE;AAAA,UAC5D;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,QAAQ;AAAA,cACf,aAAa,EAAE,kBAAkB;AAAA,cACjC,UAAU,CAAC,MAAM,gBAAgB,EAAE,UAAU,EAAE,OAAO,MAAM,CAAC;AAAA,cAC7D,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,iBAAiB;AAAA,cACvE,UAAU,CAAC,KAAK;AAAA;AAAA,UAClB;AAAA,WACF;AAAA,SACF;AAAA,MAEA,6CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC7D;AAAA,oDAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,eAAe,GAAE;AAAA,QAChE,gBAAgB,QAAQ,QAAQ,EAAE,WAAW,IAC5C,4CAAC,UAAK,OAAO,EAAE,SAAS,KAAK,UAAU,GAAG,GAAI,YAAE,gBAAgB,GAAE,IAElE,4CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,qBAAqB,WAAW,KAAK,EAAE,GACnE,0BAAgB,QAAQ,QAAQ,EAAE,IAAI,CAAC,MAAM;AAC5C,gBAAM,SAAS,WAAW,QAAQ,UAAU,EAAE,KAAK;AACnD,gBAAM,UAAU,QAAQ,QAAQ,SAAS,EAAE,KAAK;AAChD,iBACE,6CAAC,WAAoB,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,YAAY,UAAU,SAAS,SAAS,OAAO,EAAE,GACtG;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL;AAAA,gBACA,UAAU,CAAC,KAAK,YAAY;AAAA,gBAC5B,UAAU,MACR,gBAAgB;AAAA,kBACd,SAAS,UAAU,QAAQ,QAAQ,OAAO,CAAC,MAAM,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG,QAAQ,SAAS,EAAE,KAAK;AAAA,gBAChG,CAAC;AAAA;AAAA,YAEL;AAAA,YACA,6CAAC,UAAK,OAAO,EAAE,UAAU,GAAG,GAAI;AAAA,gBAAE;AAAA,cAAO,SAAS,SAAM,EAAE,cAAc,CAAC,KAAK;AAAA,eAAG;AAAA,eAXvE,EAAE,KAYd;AAAA,QAEJ,CAAC,GACH;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,QAAQ;AAAA,YACf,aAAa,EAAE,oBAAoB;AAAA,YACnC,UAAU,CAAC,MAAM,gBAAgB,EAAE,eAAe,EAAE,OAAO,MAAM,CAAC;AAAA,YAClE,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,iBAAiB;AAAA,YACvE,UAAU,CAAC,KAAK;AAAA;AAAA,QAClB;AAAA,SACF;AAAA,MAEA,6CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,qBAAqB,WAAW,KAAK,EAAE,GACpE;AAAA,qDAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,sDAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,SAAS,GAAE;AAAA,UAC3D;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,QAAQ;AAAA,cACf,aAAa,EAAE,oBAAoB;AAAA,cACnC,UAAU,CAAC,MAAM,gBAAgB,EAAE,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,cAC5D,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,iBAAiB;AAAA,cACvE,UAAU,CAAC,KAAK;AAAA;AAAA,UAClB;AAAA,WACF;AAAA,QACA,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,sDAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,SAAS,GAAE;AAAA,UAC3D;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,QAAQ;AAAA,cACf,aAAa,EAAE,oBAAoB;AAAA,cACnC,UAAU,CAAC,MAAM,gBAAgB,EAAE,eAAe,EAAE,OAAO,MAAM,CAAC;AAAA,cAClE,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,iBAAiB;AAAA,cACvE,UAAU,CAAC,KAAK;AAAA;AAAA,UAClB;AAAA,WACF;AAAA,SACF;AAAA,MAEA,6CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,YAAY,KAAK,GAAG,YAAY,SAAS,GACrF;AAAA,wBAAgB,4CAAC,UAAK,OAAO,EAAE,OAAO,QAAQ,UAAU,GAAG,GAAI,wBAAa;AAAA,QAC7E;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM;AACb,6BAAe,KAAK;AACpB,yBAAW,aAAa;AACxB,8BAAgB,MAAS;AAAA,YAC3B;AAAA,YACA,OAAO;AAAA,YAEN,YAAE,QAAQ;AAAA;AAAA,QACb;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,UAAU,CAAC,KAAK,YAAY,QAAQ,SAAS,KAAK,MAAM,MAAM,sBAAsB;AAAA,YACpF,OAAO;AAAA,cACL,GAAG;AAAA,cACH,aAAa;AAAA,cACb,YAAY,oBAAoB,IAAI,SAAS;AAAA,cAC7C,OAAO;AAAA,cACP,QAAQ,oBAAoB,IAAI,YAAY;AAAA,YAC9C;AAAA,YAEC,YAAE,SAAS,EAAE,QAAQ,OAAO,OAAO,iBAAiB,CAAC;AAAA;AAAA,QACxD;AAAA,SACF;AAAA,OACF;AAAA,IAGD,gBAAgB,MAAM,KAAK,EAAE,IAAI,CAAC,UAAU;AAC3C,YAAM,aAAa,MAAM,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO;AACrD,YAAM,QAAQ,IAAI,UAAU,KAAK,CAAC,MAAM,EAAE,aAAa,MAAM,QAAQ;AACrE,YAAM,cAAc,OAAO,gBAAgB,UAAa,MAAM,gBAAgB,MAAM,WAChF,GAAG,MAAM,WAAW,KACpB,MAAM;AACV,aACE,6CAAC,SAAyB,OAAO,EAAE,QAAQ,qBAAqB,cAAc,IAAI,SAAS,IAAI,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC9I;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,UAAU,MAAM;AAAA,YAChB;AAAA,YACA,OAAO,MAAM,MAAM;AAAA,YACnB;AAAA,YACA,UAAU,CAAC,KAAK;AAAA,YAChB;AAAA,YACA,cAAc,CAAC,QAAQ,gBAAgB,MAAM,UAAU,GAAG;AAAA,YAC1D,cAAc,CAAC,YAAY,gBAAgB,MAAM,UAAU,OAAO;AAAA,YAClE,UAAU,MAAM,YAAY,MAAM,QAAQ;AAAA;AAAA,QAC5C;AAAA,QACA,4CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC5D,gBAAM,MAAM,IAAI,CAAC,QAAQ;AAExB,gBAAM,IAAI;AACV,gBAAM,MAAM,aAAa,GAAG,MAAM,KAAK;AACvC,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,OAAO;AAAA,gBACL,QAAQ,aAAa,MAAM,SAAS,MAAM;AAAA,gBAC1C,cAAc;AAAA,gBACd,SAAS;AAAA,gBACT,SAAS;AAAA,gBACT,qBAAqB;AAAA,gBACrB,KAAK;AAAA,gBACL,YAAY,EAAE,UAAU,SAAS;AAAA,cACnC;AAAA,cAEA;AAAA,6DAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,8DAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,OAAO,GAAE;AAAA,kBACzD;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,EAAE;AAAA,sBACT,QAAQ,CAAC,EAAE,SAAS,gBAAgB,EAAE,QAAQ,EAAE,CAAC;AAAA,sBACjD,UAAU,CAAC,MAAM,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAAA,sBAC9C,UAAU,CAAC,KAAK;AAAA,sBAChB,aAAa,EAAE,QAAQ;AAAA,sBACvB,aAAa,EAAE,cAAc;AAAA,sBAC7B,WAAW,EAAE,YAAY;AAAA,sBACzB,kBAAkB,EAAE,kBAAkB;AAAA;AAAA,kBACxC;AAAA,mBACF;AAAA,gBACA,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,8DAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,UAAU,GAAE;AAAA,kBAC5D;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,EAAE;AAAA,sBACT,aAAa,EAAE,kBAAkB;AAAA,sBACjC,UAAU,CAAC,MAAM,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,MAAM,CAAC;AAAA,sBAC9D,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,iBAAiB;AAAA,sBACvE,UAAU,CAAC,KAAK;AAAA;AAAA,kBAClB;AAAA,kBACC,OAAO,4CAAC,UAAK,OAAO,EAAE,OAAO,QAAQ,UAAU,GAAG,GAAI,kBAAQ,cAAc,EAAE,WAAW,IAAI,KAAI;AAAA,mBACpG;AAAA,gBACA,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,8DAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,SAAS,GAAE;AAAA,kBAC3D;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,EAAE,WAAW;AAAA,sBACpB,aAAa,EAAE,oBAAoB;AAAA,sBACnC,UAAU,CAAC,MAAM,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,sBAC7D,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,iBAAiB;AAAA,sBACvE,UAAU,CAAC,KAAK;AAAA;AAAA,kBAClB;AAAA,mBACF;AAAA,gBACA,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,8DAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,YAAE,SAAS,GAAE;AAAA,kBAC3D;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,EAAE,iBAAiB;AAAA,sBAC1B,aAAa,EAAE,oBAAoB;AAAA,sBACnC,UAAU,CAAC,MAAM,WAAW,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,MAAM,CAAC;AAAA,sBACnE,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,iBAAiB;AAAA,sBACvE,UAAU,CAAC,KAAK;AAAA;AAAA,kBAClB;AAAA,mBACF;AAAA,gBACA,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,YAAY,SAAS,GAC5D;AAAA,8DAAC,WAAM,MAAK,YAAW,SAAS,EAAE,SAAS,UAAU,CAAC,MAAM,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,QAAQ,CAAC,GAAG,UAAU,CAAC,KAAK,UAAU;AAAA,kBACvI,4CAAC,UAAK,OAAO,EAAE,UAAU,GAAG,GAAI,YAAE,cAAc,GAAE;AAAA,mBACpD;AAAA,gBACA,4CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,YAAY,KAAK,EAAE,GAChE;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAS,MAAM,WAAW,EAAE,EAAE;AAAA,oBAC9B,UAAU,CAAC,KAAK;AAAA,oBAChB,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,kBAAkB,YAAY,QAAQ,QAAQ,UAAU;AAAA,oBAE7G,YAAE,QAAQ;AAAA;AAAA,gBACb,GACF;AAAA;AAAA;AAAA,YAnEK,EAAE;AAAA,UAoET;AAAA,QAEJ,CAAC,GACH;AAAA,WA1FQ,MAAM,QA2FhB;AAAA,IAEJ,CAAC;AAAA,IAED,6CAAC,WAAM,OAAO,EAAE,SAAS,QAAQ,eAAe,UAAU,KAAK,EAAE,GAC/D;AAAA,kDAAC,UAAK,OAAO,EAAE,UAAU,IAAI,YAAY,IAAI,GAAI,YAAE,cAAc,GAAE;AAAA,MACnE;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,MAAM;AAAA,UACb,aAAa,EAAE,oBAAoB;AAAA,UACnC,UAAU,CAAC,MAAM,SAAS,EAAE,cAAc,EAAE,OAAO,MAAM,CAAC;AAAA,UAC1D,OAAO,EAAE,SAAS,WAAW,cAAc,GAAG,QAAQ,aAAa,oBAAoB,SAAS,MAAM,GAAG;AAAA,UACzG,UAAU,CAAC,KAAK;AAAA;AAAA,MAClB;AAAA,MACC,qBAAqB,4CAAC,UAAK,OAAO,EAAE,OAAO,QAAQ,UAAU,GAAG,GAAI,6BAAkB;AAAA,OACzF;AAAA,IAEA,6CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,KAAK,GAAG,YAAY,SAAS,GAC1D;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,UAAU,CAAC,KAAK,YAAY,CAAC,SAAS;AAAA,UACtC,OAAO;AAAA,YACL,SAAS;AAAA,YACT,cAAc;AAAA,YACd,QAAQ;AAAA,YACR,YAAY,QAAQ,SAAS;AAAA,YAC7B,OAAO;AAAA,YACP,QAAQ,QAAQ,YAAY;AAAA,UAC9B;AAAA,UAEC,mBAAS,EAAE,QAAQ,IAAI,EAAE,MAAM;AAAA;AAAA,MAClC;AAAA,MACC,OAAO,4CAAC,UAAK,OAAO,EAAE,UAAU,IAAI,SAAS,IAAI,GAAI,eAAI;AAAA,OAC5D;AAAA,KACF;AAEJ;;;AFjuBO,IAAM,SAAS,CAAC,SAAS,UAAU,cAAc,UAAU,eAAe;AAE1E,SAAS,MAAM,KAA0B;AAC9C,QAAM,IAAI,IAAI,OAAO,KAAK,qBAA8B;AACxD,MAAI,OAAO,MAAM,IAAI,OAAO,SAAS,uBAAgC,EAAE,IAAI,GAAG,CAAU,GAAG,qBAAqB;AAEhH,QAAM,QAAS,IAAI,cAAsB,KAAK,EAAE,WAAW,cAAc,CAAC;AAC1E,QAAM,OAAO,IAAI,qBAAqB,KAAc;AAEpD,MAAI,OAAO,MAAM;AACf,SAAK,KAAK;AACV,WAAO,MAAM,KAAK,QAAQ;AAAA,EAC5B,GAAG,yBAAyB;AAI5B,QAAM,OAAO;AACb,QAAM,UAAU,IAAI;AAAA,IAClB,KAAK,WAAW;AAAA,IAChB,KAAK;AAAA,EACP;AACA,MAAI,OAAO,MAAM;AACf,YAAQ,KAAK;AACb,WAAO,MAAM,QAAQ,QAAQ;AAAA,EAC/B,GAAG,sBAAsB;AAEzB,MAAI,MAAM;AAAA,IAAO;AAAA,IAAwB,MACvC,IAAI,MAAM;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ,OAAO,EAAE,YAAY,MAAM,QAAQ;AAAA,MAC7C;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAYF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings schema for dsh-plugin-model-proxy (Host).
|
|
3
|
+
*
|
|
4
|
+
* Namespace: `model-proxy`
|
|
5
|
+
* Single source of truth for validation — used by Host registration
|
|
6
|
+
* and mirrored in Client for fast local feedback.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ProxyRuleSchema: any;
|
|
9
|
+
export interface ProxyRule {
|
|
10
|
+
/** Optional card key for the UI; hand-written yaml rules may omit it. */
|
|
11
|
+
id?: string;
|
|
12
|
+
provider: string;
|
|
13
|
+
model: string;
|
|
14
|
+
proxyUrl: string;
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
/** When set, the rule only applies to LLM calls carrying this purpose. */
|
|
17
|
+
purpose?: string;
|
|
18
|
+
/** Credentials-service entry ("user:password") overriding proxyUrl userinfo. */
|
|
19
|
+
credentialRef?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const ModelProxyConfigSchema: any;
|
|
22
|
+
export interface ModelProxyConfig {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
rules: ProxyRule[];
|
|
25
|
+
defaultProxy: string;
|
|
26
|
+
debug: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare const ModelProxyConfig: any;
|
|
29
|
+
/**
|
|
30
|
+
* Cross-field validation that the schema alone cannot express.
|
|
31
|
+
* Throws with a human-readable message listing the offending rule index.
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertServiceable(config: ModelProxyConfig): void;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve which proxyUrl (if any) applies to a given call.
|
|
36
|
+
* Model specificity: exact match > prefix "vendor-*" > wildcard "*", each pass
|
|
37
|
+
* first-wins in array order. Disabled rules are skipped. A rule with `purpose`
|
|
38
|
+
* set only matches calls carrying the same purpose; rules without one match all.
|
|
39
|
+
*
|
|
40
|
+
* `composeUrl` lets the caller transform the matched rule into an effective
|
|
41
|
+
* URL (e.g. credentialRef composition) — its empty/undefined result means
|
|
42
|
+
* direct exemption, same as `""`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveProxy(config: ModelProxyConfig, provider: string, model: string, purpose?: string, composeUrl?: (rule: ProxyRule) => string | undefined): string | undefined;
|
|
45
|
+
/** Redact password for logging: socks5://user:pass@host -> socks5://user:***@host */
|
|
46
|
+
export declare function redactProxyUrl(url: string): string;
|
|
47
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/host/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,eAAO,MAAM,eAAe,EAAE,GAkB5B,CAAA;AAEF,MAAM,WAAW,SAAS;IACxB,yEAAyE;IACzE,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,sBAAsB,EAAE,GAKnC,CAAA;AAEF,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;CACf;AACD,eAAO,MAAM,gBAAgB,EAAE,GAA4B,CAAA;AAI3D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CA4DhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,MAAM,GAAG,SAAS,GACnD,MAAM,GAAG,SAAS,CAuBpB;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQlD"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings schema for dsh-plugin-model-proxy (Host).
|
|
3
|
+
*
|
|
4
|
+
* Namespace: `model-proxy`
|
|
5
|
+
* Single source of truth for validation — used by Host registration
|
|
6
|
+
* and mirrored in Client for fast local feedback.
|
|
7
|
+
*/
|
|
8
|
+
import z from '@deepseek-ai/schemastery';
|
|
9
|
+
export const ProxyRuleSchema = z.object({
|
|
10
|
+
// NOTE: no `.default(fn)` here — this schemastery fork stores function
|
|
11
|
+
// defaults un-invoked and then type-checks them against the field type,
|
|
12
|
+
// so a rule without an explicit id would throw at resolution time and
|
|
13
|
+
// silently discard the whole hand-written settings section. The client
|
|
14
|
+
// generates ids for cards; host-side code never relies on them.
|
|
15
|
+
id: z.string(),
|
|
16
|
+
provider: z.string().required(),
|
|
17
|
+
// exact id | prefix "vendor-*" (single trailing '*') | "*"
|
|
18
|
+
model: z.string().required(),
|
|
19
|
+
proxyUrl: z.string().required(), // "" = direct, otherwise URL
|
|
20
|
+
enabled: z.boolean().default(true),
|
|
21
|
+
// optional purpose filter: unset matches every purpose; when set it must
|
|
22
|
+
// equal GenerateOptions.purpose (e.g. 'compaction', 'session-title')
|
|
23
|
+
purpose: z.string(),
|
|
24
|
+
// optional name of a credentials-service entry holding "user:password";
|
|
25
|
+
// when set (and resolvable) it overrides any inline userinfo in proxyUrl
|
|
26
|
+
credentialRef: z.string(),
|
|
27
|
+
});
|
|
28
|
+
export const ModelProxyConfigSchema = z.object({
|
|
29
|
+
enabled: z.boolean().default(true),
|
|
30
|
+
rules: z.array(ProxyRuleSchema).default([]),
|
|
31
|
+
defaultProxy: z.string().default(''),
|
|
32
|
+
debug: z.boolean().default(false),
|
|
33
|
+
});
|
|
34
|
+
export const ModelProxyConfig = ModelProxyConfigSchema;
|
|
35
|
+
const SUPPORTED_SCHEMES = new Set(['http:', 'https:', 'socks5:', 'socks5h:', 'socks:']);
|
|
36
|
+
/**
|
|
37
|
+
* Cross-field validation that the schema alone cannot express.
|
|
38
|
+
* Throws with a human-readable message listing the offending rule index.
|
|
39
|
+
*/
|
|
40
|
+
export function assertServiceable(config) {
|
|
41
|
+
// per-rule checks
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
for (let i = 0; i < config.rules.length; i++) {
|
|
44
|
+
const r = config.rules[i];
|
|
45
|
+
if (r.provider !== r.provider.trim()) {
|
|
46
|
+
throw new Error(`model-proxy: rule #${i + 1} provider has leading/trailing whitespace: ${JSON.stringify(r.provider)}`);
|
|
47
|
+
}
|
|
48
|
+
if (r.model !== r.model.trim()) {
|
|
49
|
+
throw new Error(`model-proxy: rule #${i + 1} model has leading/trailing whitespace: ${JSON.stringify(r.model)}`);
|
|
50
|
+
}
|
|
51
|
+
// Prefix patterns allow a single TRAILING '*' only ("vendor-*"); anything
|
|
52
|
+
// else containing '*' would silently match literally, so reject it.
|
|
53
|
+
const starCount = (r.model.match(/\*/g) ?? []).length;
|
|
54
|
+
const isBareWildcard = r.model === '*';
|
|
55
|
+
if (!isBareWildcard && starCount > 0 && !(starCount === 1 && r.model.endsWith('*'))) {
|
|
56
|
+
throw new Error(`model-proxy: rule #${i + 1} model may use at most one trailing '*' as prefix wildcard (e.g. "vendor-*"), got ${JSON.stringify(r.model)}`);
|
|
57
|
+
}
|
|
58
|
+
if (r.purpose !== undefined && r.purpose !== '' && r.purpose !== r.purpose.trim()) {
|
|
59
|
+
throw new Error(`model-proxy: rule #${i + 1} purpose has leading/trailing whitespace: ${JSON.stringify(r.purpose)}`);
|
|
60
|
+
}
|
|
61
|
+
if (r.credentialRef !== undefined && r.credentialRef !== '' && r.credentialRef !== r.credentialRef.trim()) {
|
|
62
|
+
throw new Error(`model-proxy: rule #${i + 1} credentialRef has leading/trailing whitespace: ${JSON.stringify(r.credentialRef)}`);
|
|
63
|
+
}
|
|
64
|
+
const key = `${r.provider}\0${r.model}`;
|
|
65
|
+
if (!r.provider.trim())
|
|
66
|
+
throw new Error(`model-proxy: rule #${i + 1} provider must be non-empty`);
|
|
67
|
+
if (!r.model.trim())
|
|
68
|
+
throw new Error(`model-proxy: rule #${i + 1} model must be non-empty`);
|
|
69
|
+
if (seen.has(key))
|
|
70
|
+
throw new Error(`model-proxy: rule #${i + 1} duplicates provider "${r.provider}" model "${r.model}"`);
|
|
71
|
+
seen.add(key);
|
|
72
|
+
if (r.proxyUrl !== '') {
|
|
73
|
+
let url;
|
|
74
|
+
try {
|
|
75
|
+
url = new URL(r.proxyUrl);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
throw new Error(`model-proxy: rule #${i + 1} proxyUrl is not a valid URL: ${JSON.stringify(r.proxyUrl.slice(0, 32))}…(truncated)`);
|
|
79
|
+
}
|
|
80
|
+
if (!SUPPORTED_SCHEMES.has(url.protocol)) {
|
|
81
|
+
throw new Error(`model-proxy: rule #${i + 1} proxyUrl scheme "${url.protocol}" unsupported; use http://, https://, socks5:// or socks5h://`);
|
|
82
|
+
}
|
|
83
|
+
if (!url.hostname)
|
|
84
|
+
throw new Error(`model-proxy: rule #${i + 1} proxyUrl must have a hostname`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (config.defaultProxy !== '') {
|
|
88
|
+
let url;
|
|
89
|
+
try {
|
|
90
|
+
url = new URL(config.defaultProxy);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
throw new Error(`model-proxy: defaultProxy is not a valid URL: ${JSON.stringify(config.defaultProxy.slice(0, 32))}…(truncated)`);
|
|
94
|
+
}
|
|
95
|
+
if (!SUPPORTED_SCHEMES.has(url.protocol)) {
|
|
96
|
+
throw new Error(`model-proxy: defaultProxy scheme "${url.protocol}" unsupported`);
|
|
97
|
+
}
|
|
98
|
+
if (!url.hostname)
|
|
99
|
+
throw new Error('model-proxy: defaultProxy must have a hostname');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Resolve which proxyUrl (if any) applies to a given call.
|
|
104
|
+
* Model specificity: exact match > prefix "vendor-*" > wildcard "*", each pass
|
|
105
|
+
* first-wins in array order. Disabled rules are skipped. A rule with `purpose`
|
|
106
|
+
* set only matches calls carrying the same purpose; rules without one match all.
|
|
107
|
+
*
|
|
108
|
+
* `composeUrl` lets the caller transform the matched rule into an effective
|
|
109
|
+
* URL (e.g. credentialRef composition) — its empty/undefined result means
|
|
110
|
+
* direct exemption, same as `""`.
|
|
111
|
+
*/
|
|
112
|
+
export function resolveProxy(config, provider, model, purpose, composeUrl) {
|
|
113
|
+
if (!config.enabled)
|
|
114
|
+
return undefined;
|
|
115
|
+
const purposeMatches = (r) => r.purpose === undefined || r.purpose === '' || r.purpose === purpose;
|
|
116
|
+
for (const pass of ['exact', 'prefix', 'wildcard']) {
|
|
117
|
+
for (const r of config.rules) {
|
|
118
|
+
if (r.enabled === false)
|
|
119
|
+
continue;
|
|
120
|
+
if (r.provider !== provider)
|
|
121
|
+
continue;
|
|
122
|
+
if (!purposeMatches(r))
|
|
123
|
+
continue;
|
|
124
|
+
if (pass === 'exact' && r.model !== model)
|
|
125
|
+
continue;
|
|
126
|
+
if (pass === 'wildcard' && r.model !== '*')
|
|
127
|
+
continue;
|
|
128
|
+
if (pass === 'prefix') {
|
|
129
|
+
if (!r.model.endsWith('*') || r.model === '*')
|
|
130
|
+
continue;
|
|
131
|
+
if (!model.startsWith(r.model.slice(0, -1)))
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const url = composeUrl ? composeUrl(r) : r.proxyUrl;
|
|
135
|
+
// "" / undefined mean direct exemption
|
|
136
|
+
return url || undefined;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return config.defaultProxy || undefined;
|
|
140
|
+
}
|
|
141
|
+
/** Redact password for logging: socks5://user:pass@host -> socks5://user:***@host */
|
|
142
|
+
export function redactProxyUrl(url) {
|
|
143
|
+
try {
|
|
144
|
+
const u = new URL(url);
|
|
145
|
+
if (u.password)
|
|
146
|
+
u.password = '***';
|
|
147
|
+
return u.toString();
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return url;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/host/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* credentialRef support: compose a proxyUrl with a secret fetched from the
|
|
3
|
+
* DSH credentials service, keeping plaintext passwords out of settings.yaml.
|
|
4
|
+
*
|
|
5
|
+
* The credentials service is a SOFT dependency (dsh-llm-deepseek follows the
|
|
6
|
+
* same pattern): it is looked up via ctx.get('credentials') at runtime, never
|
|
7
|
+
* declared in `inject`, so installs without the credentials package keep
|
|
8
|
+
* working as long as no rule references a credentialRef.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Merge `secret` into `baseUrl`'s userinfo. Secret format is
|
|
12
|
+
* "username:password" (split at the FIRST colon so passwords may contain
|
|
13
|
+
* colons). Returns undefined for unparseable input.
|
|
14
|
+
*/
|
|
15
|
+
export declare function composeProxyUrl(baseUrl: string, secret: string): string | undefined;
|
|
16
|
+
/** Minimal structural face of the DSH credentials service we rely on. */
|
|
17
|
+
export interface CredentialLookup {
|
|
18
|
+
resolve(ref: string): Promise<{
|
|
19
|
+
value: string;
|
|
20
|
+
} | undefined>;
|
|
21
|
+
}
|
|
22
|
+
/** Safely fetch the soft 'credentials' service; undefined when absent. */
|
|
23
|
+
export declare function getCredentialsService(ctx: unknown): CredentialLookup | undefined;
|
|
24
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/host/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYnF;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAA;CAC7D;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAchF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* credentialRef support: compose a proxyUrl with a secret fetched from the
|
|
3
|
+
* DSH credentials service, keeping plaintext passwords out of settings.yaml.
|
|
4
|
+
*
|
|
5
|
+
* The credentials service is a SOFT dependency (dsh-llm-deepseek follows the
|
|
6
|
+
* same pattern): it is looked up via ctx.get('credentials') at runtime, never
|
|
7
|
+
* declared in `inject`, so installs without the credentials package keep
|
|
8
|
+
* working as long as no rule references a credentialRef.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Merge `secret` into `baseUrl`'s userinfo. Secret format is
|
|
12
|
+
* "username:password" (split at the FIRST colon so passwords may contain
|
|
13
|
+
* colons). Returns undefined for unparseable input.
|
|
14
|
+
*/
|
|
15
|
+
export function composeProxyUrl(baseUrl, secret) {
|
|
16
|
+
let u;
|
|
17
|
+
try {
|
|
18
|
+
u = new URL(baseUrl);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const sep = secret.indexOf(':');
|
|
24
|
+
if (sep <= 0)
|
|
25
|
+
return undefined; // requires "user:pass"; empty user is ambiguous
|
|
26
|
+
u.username = secret.slice(0, sep);
|
|
27
|
+
u.password = secret.slice(sep + 1);
|
|
28
|
+
return u.toString();
|
|
29
|
+
}
|
|
30
|
+
/** Safely fetch the soft 'credentials' service; undefined when absent. */
|
|
31
|
+
export function getCredentialsService(ctx) {
|
|
32
|
+
try {
|
|
33
|
+
const svc = ctx.get?.('credentials');
|
|
34
|
+
if (svc !== undefined &&
|
|
35
|
+
svc !== null &&
|
|
36
|
+
typeof svc.resolve === 'function') {
|
|
37
|
+
return svc;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/host/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,MAAc;IAC7D,IAAI,CAAM,CAAA;IACV,IAAI,CAAC;QACH,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC/B,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA,CAAC,gDAAgD;IAC/E,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACjC,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;IAClC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAA;AACrB,CAAC;AAOD,0EAA0E;AAC1E,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAI,GAAuC,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,CAAA;QACzE,IACE,GAAG,KAAK,SAAS;YACjB,GAAG,KAAK,IAAI;YACZ,OAAQ,GAAwB,CAAC,OAAO,KAAK,UAAU,EACvD,CAAC;YACD,OAAO,GAAuB,CAAA;QAChC,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatcher factory: proxyUrl -> undici Dispatcher.
|
|
3
|
+
* One instance per proxyUrl (connection pooling), lazy-created.
|
|
4
|
+
*
|
|
5
|
+
* http(s) proxies use undici's native ProxyAgent.
|
|
6
|
+
* socks5/socks5h proxies need a bespoke undici Dispatcher: undici's fetch
|
|
7
|
+
* "dispatcher" option requires an undici Dispatcher (it only accepts
|
|
8
|
+
* http:// and https:// URLs in ProxyAgent), but a Node http.Agent such as
|
|
9
|
+
* socks-proxy-agent's SocksProxyAgent is NOT a valid undici Dispatcher
|
|
10
|
+
* ("agent.dispatch is not a function" -> Connection error). We therefore
|
|
11
|
+
* build an undici Agent whose `connect` tunnels the TCP socket through the
|
|
12
|
+
* socks server and returns the final socket (TLS-wrapped for https).
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Cheap availability probe for the optional `socks` dependency, memoized.
|
|
16
|
+
* Lets the host warn at configuration time instead of failing at first request.
|
|
17
|
+
*/
|
|
18
|
+
export declare function socksDependencyAvailable(): boolean;
|
|
19
|
+
/** A proxy URL may be reused by many rules; cache one Dispatcher per URL. */
|
|
20
|
+
export declare function getOrCreateDispatcher(proxyUrl: string): unknown;
|
|
21
|
+
/**
|
|
22
|
+
* Retire dispatchers: remove them from the cache and close their pooled
|
|
23
|
+
* sockets. `close()` is graceful — undici finishes in-flight requests before
|
|
24
|
+
* tearing down, so evicting a URL mid-config-change never kills an active
|
|
25
|
+
* stream. Best-effort: dispatcher instances without `close` are just dropped.
|
|
26
|
+
*
|
|
27
|
+
* Returns how many entries were removed (test seam).
|
|
28
|
+
*/
|
|
29
|
+
export declare function clearDispatcherCache(proxyUrl?: string): number;
|
|
30
|
+
//# sourceMappingURL=dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/host/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAwCH;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAUlD;AA4DD,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAwB/D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAe9D"}
|