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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/cordis.patch.yml +15 -0
  4. package/lib/client/ModelProxyCard.d.ts +14 -0
  5. package/lib/client/ModelProxyCard.d.ts.map +1 -0
  6. package/lib/client/catalog.d.ts +140 -0
  7. package/lib/client/catalog.d.ts.map +1 -0
  8. package/lib/client/controller.d.ts +107 -0
  9. package/lib/client/controller.d.ts.map +1 -0
  10. package/lib/client/index.d.ts +8 -0
  11. package/lib/client/index.d.ts.map +1 -0
  12. package/lib/client/locales.d.ts +106 -0
  13. package/lib/client/locales.d.ts.map +1 -0
  14. package/lib/client.js +1129 -0
  15. package/lib/client.js.map +7 -0
  16. package/lib/host/config.d.ts +47 -0
  17. package/lib/host/config.d.ts.map +1 -0
  18. package/lib/host/config.js +153 -0
  19. package/lib/host/config.js.map +1 -0
  20. package/lib/host/credentials.d.ts +24 -0
  21. package/lib/host/credentials.d.ts.map +1 -0
  22. package/lib/host/credentials.js +45 -0
  23. package/lib/host/credentials.js.map +1 -0
  24. package/lib/host/dispatcher.d.ts +30 -0
  25. package/lib/host/dispatcher.d.ts.map +1 -0
  26. package/lib/host/dispatcher.js +167 -0
  27. package/lib/host/dispatcher.js.map +1 -0
  28. package/lib/host/fetch-wrap.d.ts +45 -0
  29. package/lib/host/fetch-wrap.d.ts.map +1 -0
  30. package/lib/host/fetch-wrap.js +100 -0
  31. package/lib/host/fetch-wrap.js.map +1 -0
  32. package/lib/host/index.d.ts +18 -0
  33. package/lib/host/index.d.ts.map +1 -0
  34. package/lib/host/index.js +262 -0
  35. package/lib/host/index.js.map +1 -0
  36. package/lib/host/probe.d.ts +30 -0
  37. package/lib/host/probe.d.ts.map +1 -0
  38. package/lib/host/probe.js +43 -0
  39. package/lib/host/probe.js.map +1 -0
  40. package/lib/invariant.d.ts +5 -0
  41. package/lib/invariant.d.ts.map +1 -0
  42. package/lib/invariant.js +7 -0
  43. package/lib/invariant.js.map +1 -0
  44. package/package.json +95 -0
@@ -0,0 +1,167 @@
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
+ import { ProxyAgent, Agent } from 'undici';
15
+ import { createRequire } from 'node:module';
16
+ import tls from 'node:tls';
17
+ import { redactProxyUrl } from './config.js';
18
+ const cache = new Map();
19
+ /** IANA-assigned default port for SOCKS when the proxy URL omits one. */
20
+ const DEFAULT_SOCKS_PORT = 1080;
21
+ // The `connect` option undici passes to Agent is a buildConnector-like fn:
22
+ // connect(opts: { host, hostname, protocol, port, servername }, cb)
23
+ // undici gives `port` as '' for default scheme ports, so we fill it in.
24
+ function defaultPortFor(protocol) {
25
+ if (protocol === 'http:')
26
+ return 80;
27
+ if (protocol === 'https:')
28
+ return 443;
29
+ return 443;
30
+ }
31
+ /** Lazily require the optional `socks` dependency; throws with install guidance. */
32
+ function loadSocksClient() {
33
+ try {
34
+ const require = createRequire(import.meta.url);
35
+ const mod = require('socks');
36
+ if (mod === null || typeof mod !== 'object')
37
+ throw new Error('no exports');
38
+ const SocksClient = mod.SocksClient;
39
+ if (typeof SocksClient?.createConnection !== 'function')
40
+ throw new Error('SocksClient.createConnection missing');
41
+ return SocksClient;
42
+ }
43
+ catch (e) {
44
+ // Deliberately no proxyUrl in this message: it may carry credentials.
45
+ throw new Error(`socks proxy requires dependency "socks" (pnpm add socks). Original: ${String(e)}`);
46
+ }
47
+ }
48
+ let socksProbe;
49
+ /**
50
+ * Cheap availability probe for the optional `socks` dependency, memoized.
51
+ * Lets the host warn at configuration time instead of failing at first request.
52
+ */
53
+ export function socksDependencyAvailable() {
54
+ if (socksProbe === undefined) {
55
+ try {
56
+ loadSocksClient();
57
+ socksProbe = true;
58
+ }
59
+ catch {
60
+ socksProbe = false;
61
+ }
62
+ }
63
+ return socksProbe;
64
+ }
65
+ /**
66
+ * Build an undici Dispatcher whose connections are tunnelled through a socks
67
+ * proxy. `socks` is an optional dependency loaded lazily so http-only installs
68
+ * still work.
69
+ *
70
+ * undici's custom `connect` connector is expected to return the FINAL socket
71
+ * (including TLS for https destinations), so we wrap the raw socks socket in
72
+ * TLS ourselves for https targets.
73
+ */
74
+ function createSocksDispatcher(proxyUrl) {
75
+ const SocksClient = loadSocksClient();
76
+ const u = new URL(proxyUrl);
77
+ const proxyHost = u.hostname;
78
+ // A URL like `socks5://127.0.0.1` yields port '' -> Number('') === 0, which
79
+ // would dial port 0. Fall back to the IANA SOCKS default instead.
80
+ const proxyPort = Number(u.port) || DEFAULT_SOCKS_PORT;
81
+ // Returns a socket (TLS-wrapped for https) already connected to the target
82
+ // through the socks proxy.
83
+ const connectThroughSocks = (opts, callback) => {
84
+ const host = opts.hostname ?? opts.host ?? '';
85
+ const port = opts.port ? Number(opts.port) : defaultPortFor(opts.protocol ?? 'https:');
86
+ if (!host) {
87
+ callback(new Error('socks connect: missing destination host'));
88
+ return;
89
+ }
90
+ const finishRaw = (raw) => {
91
+ if (opts.protocol === 'https:' || port === 443) {
92
+ const secure = tls.connect({
93
+ socket: raw,
94
+ servername: opts.servername ?? host,
95
+ host,
96
+ port,
97
+ });
98
+ secure.once('secureConnect', () => callback(null, secure));
99
+ secure.once('error', (err) => callback(err));
100
+ }
101
+ else {
102
+ callback(null, raw);
103
+ }
104
+ };
105
+ SocksClient.createConnection({
106
+ proxy: { host: proxyHost, port: proxyPort, type: 5 },
107
+ command: 'connect',
108
+ destination: { host, port },
109
+ })
110
+ .then((info) => finishRaw(info.socket))
111
+ .catch((err) => callback(err));
112
+ };
113
+ return new Agent({ connect: connectThroughSocks });
114
+ }
115
+ /** A proxy URL may be reused by many rules; cache one Dispatcher per URL. */
116
+ export function getOrCreateDispatcher(proxyUrl) {
117
+ const cached = cache.get(proxyUrl);
118
+ if (cached)
119
+ return cached;
120
+ let u;
121
+ try {
122
+ u = new URL(proxyUrl);
123
+ }
124
+ catch {
125
+ // Unparseable input: echo only a truncated prefix so a mistyped credential
126
+ // cannot land whole in logs, while still being recognizable to its author.
127
+ throw new Error(`invalid proxyUrl: ${JSON.stringify(proxyUrl.slice(0, 32))}…(truncated)`);
128
+ }
129
+ let d;
130
+ if (u.protocol === 'http:' || u.protocol === 'https:') {
131
+ d = new ProxyAgent(proxyUrl);
132
+ }
133
+ else if (u.protocol === 'socks5:' || u.protocol === 'socks5h:' || u.protocol === 'socks:') {
134
+ d = createSocksDispatcher(proxyUrl);
135
+ }
136
+ else {
137
+ throw new Error(`unsupported proxy protocol "${u.protocol}" for ${redactProxyUrl(proxyUrl)}`);
138
+ }
139
+ cache.set(proxyUrl, d);
140
+ return d;
141
+ }
142
+ /**
143
+ * Retire dispatchers: remove them from the cache and close their pooled
144
+ * sockets. `close()` is graceful — undici finishes in-flight requests before
145
+ * tearing down, so evicting a URL mid-config-change never kills an active
146
+ * stream. Best-effort: dispatcher instances without `close` are just dropped.
147
+ *
148
+ * Returns how many entries were removed (test seam).
149
+ */
150
+ export function clearDispatcherCache(proxyUrl) {
151
+ const keys = proxyUrl !== undefined ? [proxyUrl] : [...cache.keys()];
152
+ let removed = 0;
153
+ for (const key of keys) {
154
+ const d = cache.get(key);
155
+ if (!cache.delete(key))
156
+ continue;
157
+ removed++;
158
+ const closer = d?.close;
159
+ if (typeof closer === 'function') {
160
+ // Fire-and-forget: teardown must stay synchronous; failures at pool
161
+ // retirement are irrelevant (sockets die with the process anyway).
162
+ void closer.call(d).catch(() => { });
163
+ }
164
+ }
165
+ return removed;
166
+ }
167
+ //# sourceMappingURL=dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/host/dispatcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Reversible global fetch wrapper.
3
+ * Only injects dispatcher when AsyncLocalStorage holds a proxyUrl.
4
+ */
5
+ import { AsyncLocalStorage } from 'node:async_hooks';
6
+ export interface ProxyContext {
7
+ proxyUrl?: string;
8
+ provider: string;
9
+ model: string;
10
+ }
11
+ export declare const als: AsyncLocalStorage<ProxyContext>;
12
+ /**
13
+ * Whether the config state justifies touching globalThis.fetch at all.
14
+ *
15
+ * The wrapper is a process-wide side effect, so it must only be installed
16
+ * while some rule (or the default fallback) could actually route a request
17
+ * through a proxy. Globally disabled or fully-empty configs keep the global
18
+ * fetch untouched.
19
+ */
20
+ export declare function shouldWrapFetch(config: {
21
+ enabled: boolean;
22
+ defaultProxy?: string;
23
+ rules?: ReadonlyArray<{
24
+ proxyUrl: string;
25
+ enabled?: boolean;
26
+ }>;
27
+ }): boolean;
28
+ /** Minimal logger face so the host can route diagnostics through ctx.logger. */
29
+ export interface FetchWrapLogger {
30
+ error?(msg: string): void;
31
+ }
32
+ /**
33
+ * Install the wrapper around globalThis.fetch and return its disposer.
34
+ *
35
+ * Installation always wraps whatever is currently installed, even if that is
36
+ * already a model-proxy wrapper from another plugin instance. This matters for
37
+ * cordis-plugin-hmr: `partialReload` re-applies a plugin BEFORE the old fiber's
38
+ * disposers finish (they are started but not awaited), so a "detect and no-op"
39
+ * policy would let the dying twin's restore run last and leave fetch unwrapped
40
+ * entirely. Layered wrappers degrade gracefully instead: each layer only
41
+ * restores itself if it is still the outermost function at dispose time, and a
42
+ * superseded inner layer simply passes through (its ALS store is never set).
43
+ */
44
+ export declare function installFetchWrapper(log?: FetchWrapLogger): () => void;
45
+ //# sourceMappingURL=fetch-wrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-wrap.d.ts","sourceRoot":"","sources":["../../src/host/fetch-wrap.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAKpD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,GAAG,iCAAwC,CAAA;AAIxD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CAC/D,GAAG,OAAO,CAIV;AAED,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAQD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,eAA+B,GAAG,MAAM,IAAI,CAmEpF"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Reversible global fetch wrapper.
3
+ * Only injects dispatcher when AsyncLocalStorage holds a proxyUrl.
4
+ */
5
+ import { AsyncLocalStorage } from 'node:async_hooks';
6
+ import { fetch as undiciFetch, Request as UndiciRequest } from 'undici';
7
+ import { getOrCreateDispatcher } from './dispatcher.js';
8
+ import { redactProxyUrl } from './config.js';
9
+ export const als = new AsyncLocalStorage();
10
+ /**
11
+ * Whether the config state justifies touching globalThis.fetch at all.
12
+ *
13
+ * The wrapper is a process-wide side effect, so it must only be installed
14
+ * while some rule (or the default fallback) could actually route a request
15
+ * through a proxy. Globally disabled or fully-empty configs keep the global
16
+ * fetch untouched.
17
+ */
18
+ export function shouldWrapFetch(config) {
19
+ if (!config.enabled)
20
+ return false;
21
+ if (config.defaultProxy)
22
+ return true;
23
+ return (config.rules ?? []).some((r) => r.enabled !== false && r.proxyUrl !== '');
24
+ }
25
+ /** Fallback when no host logger is wired in (standalone smoke tests). */
26
+ const consoleLogger = {
27
+ // eslint-disable-next-line no-console
28
+ error: (msg) => console.error(msg),
29
+ };
30
+ /**
31
+ * Install the wrapper around globalThis.fetch and return its disposer.
32
+ *
33
+ * Installation always wraps whatever is currently installed, even if that is
34
+ * already a model-proxy wrapper from another plugin instance. This matters for
35
+ * cordis-plugin-hmr: `partialReload` re-applies a plugin BEFORE the old fiber's
36
+ * disposers finish (they are started but not awaited), so a "detect and no-op"
37
+ * policy would let the dying twin's restore run last and leave fetch unwrapped
38
+ * entirely. Layered wrappers degrade gracefully instead: each layer only
39
+ * restores itself if it is still the outermost function at dispose time, and a
40
+ * superseded inner layer simply passes through (its ALS store is never set).
41
+ */
42
+ export function installFetchWrapper(log = consoleLogger) {
43
+ const original = globalThis.fetch;
44
+ const wrapped = (async (input, init) => {
45
+ const store = als.getStore();
46
+ if (!store?.proxyUrl) {
47
+ return original(input, init);
48
+ }
49
+ // Only called inside llm/stream — safe to proxy all fetches in this context.
50
+ // If needed, add URL allowlist (e.g. only https://example.com etc.), but
51
+ // context-scoping already isolates from non-LLM fetches.
52
+ try {
53
+ const dispatcher = getOrCreateDispatcher(store.proxyUrl);
54
+ // undici's fetch brand-checks ITS OWN Request class, so a Request built
55
+ // by another realm (e.g. Node's global constructor) throws
56
+ // "Failed to parse URL from [object Request]". Adapters currently pass
57
+ // string URLs, but rebuild defensively so a future Request-passing
58
+ // caller degrades to working proxying instead of an opaque TypeError.
59
+ // Verified: `new undici.Request(foreignReq.url, foreignReq)` carries
60
+ // method/headers/body across realms; init.signal below still overrides
61
+ // per spec.
62
+ let proxiedInput = input;
63
+ if (typeof Request !== 'undefined' && input instanceof Request) {
64
+ proxiedInput = new UndiciRequest(input.url, input);
65
+ }
66
+ // NOTE: Node's native (built-in) global fetch forwards `dispatcher` to
67
+ // its internal undici copy, whose Dispatcher/handler protocol does not
68
+ // match instances of the standalone undici package ("invalid onError
69
+ // method"), so for proxied requests we delegate to undici's own fetch,
70
+ // which fully supports `dispatcher` (including the socks Agent built in
71
+ // dispatcher.ts). Direct (no-proxy) requests keep using the original
72
+ // global fetch unchanged.
73
+ const nextInit = { ...(init ?? {}), dispatcher };
74
+ return await undiciFetch(proxiedInput, nextInit);
75
+ }
76
+ catch (err) {
77
+ // Sync failures only (bad proxy URL scheme / missing socks dep);
78
+ // async transport errors propagate to the adapter untouched.
79
+ // Log the full cause chain so "Connection error" isn't opaque — with a
80
+ // REDACTED proxyUrl: credentials must never reach logs.
81
+ const causeChain = [];
82
+ let c = err;
83
+ while (c && causeChain.length < 6) {
84
+ causeChain.push(c?.message ?? String(c));
85
+ c = c?.cause;
86
+ }
87
+ log.error?.(`[model-proxy] proxied fetch failed (${store.provider}/${store.model} via ${redactProxyUrl(store.proxyUrl)}): ${causeChain.join(' → ')}`);
88
+ throw err;
89
+ }
90
+ });
91
+ globalThis.fetch = wrapped;
92
+ return () => {
93
+ // Only unwrap when we are still the outermost wrapper; otherwise another
94
+ // (newer) wrapper sits on top and owns the restore.
95
+ if (globalThis.fetch === wrapped) {
96
+ globalThis.fetch = original;
97
+ }
98
+ };
99
+ }
100
+ //# sourceMappingURL=fetch-wrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-wrap.js","sourceRoot":"","sources":["../../src/host/fetch-wrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Host plugin: dsh-plugin-model-proxy
3
+ *
4
+ * - Registers `model-proxy` settings namespace (live, no restart)
5
+ * - Wraps global fetch (reversible)
6
+ * - Intercepts `llm/stream` waterfall to route per (provider, model, purpose)
7
+ * - Composes credentialRef entries over rule proxyUrls (soft credentials dep)
8
+ * - Probes newly configured proxies without consuming model quota
9
+ *
10
+ * Zero invasion: only uses public seams (ctx.settings, ctx.llm waterfall,
11
+ * ctx.get('credentials'), global fetch dispatcher).
12
+ */
13
+ import type { Context } from '@deepseek-ai/cordis';
14
+ import { type ModelProxyConfig as ConfigType } from './config.js';
15
+ export declare const name = "dsh-plugin-model-proxy";
16
+ export declare const inject: string[];
17
+ export declare function apply(ctx: Context, entry: ConfigType): void;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAqE,KAAK,gBAAgB,IAAI,UAAU,EAAkB,MAAM,aAAa,CAAA;AAMpJ,eAAO,MAAM,IAAI,2BAA2B,CAAA;AAC5C,eAAO,MAAM,MAAM,UAAsB,CAAA;AAIzC,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CA2P3D"}
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Host plugin: dsh-plugin-model-proxy
3
+ *
4
+ * - Registers `model-proxy` settings namespace (live, no restart)
5
+ * - Wraps global fetch (reversible)
6
+ * - Intercepts `llm/stream` waterfall to route per (provider, model, purpose)
7
+ * - Composes credentialRef entries over rule proxyUrls (soft credentials dep)
8
+ * - Probes newly configured proxies without consuming model quota
9
+ *
10
+ * Zero invasion: only uses public seams (ctx.settings, ctx.llm waterfall,
11
+ * ctx.get('credentials'), global fetch dispatcher).
12
+ */
13
+ import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings';
14
+ import { ModelProxyConfig, assertServiceable, resolveProxy, redactProxyUrl } from './config.js';
15
+ import { als, installFetchWrapper, shouldWrapFetch } from './fetch-wrap.js';
16
+ import { clearDispatcherCache, socksDependencyAvailable } from './dispatcher.js';
17
+ import { composeProxyUrl, getCredentialsService } from './credentials.js';
18
+ import { probeProxy } from './probe.js';
19
+ export const name = 'dsh-plugin-model-proxy';
20
+ export const inject = ['settings', 'llm'];
21
+ const NS = settingsNamespace('model-proxy');
22
+ export function apply(ctx, entry) {
23
+ // Normalize entry through schema defaults so bare {} works
24
+ let normalizedEntry;
25
+ try {
26
+ normalizedEntry = ModelProxyConfig(entry ?? {});
27
+ assertServiceable(normalizedEntry);
28
+ }
29
+ catch (err) {
30
+ ctx.logger.warn(`[model-proxy] composition config invalid: ${String(err)}`);
31
+ normalizedEntry = { enabled: true, rules: [], defaultProxy: '', debug: false };
32
+ }
33
+ // Authoritative config source: the composition entry until the settings
34
+ // layer attaches, then the resolved scope. installSettingsSection calls
35
+ // setSource before the matching onChange, so no polling or timers needed.
36
+ let current = () => normalizedEntry;
37
+ const credCache = new Map();
38
+ const warnedCredRefs = new Set();
39
+ function refreshCredential(lookup, ref) {
40
+ if (!lookup) {
41
+ credCache.set(ref, { status: 'error', message: 'credentials service not installed' });
42
+ return Promise.resolve();
43
+ }
44
+ return lookup
45
+ .resolve(ref)
46
+ .then((hit) => {
47
+ if (hit === undefined || typeof hit.value !== 'string' || hit.value.length === 0) {
48
+ credCache.set(ref, { status: 'error', message: `credential "${ref}" not found` });
49
+ }
50
+ else if (!hit.value.includes(':')) {
51
+ credCache.set(ref, { status: 'error', message: `credential "${ref}" must hold "user:password"` });
52
+ }
53
+ else {
54
+ credCache.set(ref, { status: 'ok', value: hit.value });
55
+ }
56
+ })
57
+ .catch((err) => {
58
+ credCache.set(ref, { status: 'error', message: String(err) });
59
+ });
60
+ }
61
+ /** Effective URL for a matched rule; sync — reads only the resolved cache. */
62
+ const effectiveRuleUrl = (r) => {
63
+ if (!r.proxyUrl)
64
+ return undefined;
65
+ const ref = r.credentialRef?.trim() || undefined;
66
+ if (!ref)
67
+ return r.proxyUrl;
68
+ const hit = credCache.get(ref);
69
+ if (hit?.status !== 'ok') {
70
+ if (!warnedCredRefs.has(ref)) {
71
+ warnedCredRefs.add(ref);
72
+ const why = hit?.status === 'error' ? hit.message : 'still resolving';
73
+ ctx.logger.warn(`[model-proxy] credential ${ref} unavailable (${why}); routing ${r.provider}/${r.model} with inline proxyUrl until it resolves`);
74
+ }
75
+ return r.proxyUrl;
76
+ }
77
+ return composeProxyUrl(r.proxyUrl, hit.value) ?? r.proxyUrl;
78
+ };
79
+ // ── dispatcher cache + socks warning lifecycle ──────────────────────────
80
+ let activeProxyUrls = new Set();
81
+ const warnedUnusableSchemes = new Set();
82
+ // ── probe scheduling (P3-a): one pass per never-probed URL, serialized ──
83
+ const probedUrls = new Set();
84
+ let probeChain = Promise.resolve();
85
+ const scheduleProbes = (urls) => {
86
+ for (const url of urls) {
87
+ if (probedUrls.has(url))
88
+ continue;
89
+ const isSocks = url.startsWith('socks5') || url.startsWith('socks:');
90
+ if (isSocks && !socksDependencyAvailable())
91
+ continue; // warn already emitted
92
+ probedUrls.add(url);
93
+ probeChain = probeChain.then(async () => {
94
+ const result = await probeProxy(url);
95
+ const via = redactProxyUrl(url);
96
+ if (result.ok) {
97
+ ctx.logger.info(`[model-proxy] probe ${via} OK (${result.latencyMs}ms, ${result.detail})`);
98
+ }
99
+ else {
100
+ ctx.logger.warn(`[model-proxy] probe ${via} FAILED (${result.latencyMs}ms): ${result.detail}`);
101
+ }
102
+ }).catch(() => { });
103
+ }
104
+ };
105
+ // Reconcile is async (credential resolution), onChange is sync — serialize
106
+ // runs and coalesce bursts of config writes into one trailing run.
107
+ let reconciling = false;
108
+ let pendingReconcile = false;
109
+ const runReconcile = async () => {
110
+ if (reconciling) {
111
+ pendingReconcile = true;
112
+ return;
113
+ }
114
+ reconciling = true;
115
+ try {
116
+ const cfg = current();
117
+ // refresh every referenced credential before computing effective URLs
118
+ const lookup = getCredentialsService(ctx);
119
+ const refs = [...new Set(cfg.rules.map((r) => r.credentialRef?.trim()).filter((v) => !!v))];
120
+ await Promise.all(refs.map((ref) => refreshCredential(lookup, ref)));
121
+ // eviction baseline must use EFFECTIVE urls: the dispatcher cache keys
122
+ // on composed URLs, so a rotated credential must retire the old pool
123
+ const next = new Set();
124
+ for (const r of cfg.rules) {
125
+ const u = effectiveRuleUrl(r);
126
+ if (u)
127
+ next.add(u);
128
+ }
129
+ if (cfg.defaultProxy)
130
+ next.add(cfg.defaultProxy);
131
+ for (const url of activeProxyUrls) {
132
+ if (!next.has(url))
133
+ clearDispatcherCache(url);
134
+ }
135
+ activeProxyUrls = next;
136
+ for (const url of activeProxyUrls) {
137
+ if (!url.startsWith('socks5') && !url.startsWith('socks:'))
138
+ continue;
139
+ if (socksDependencyAvailable() || warnedUnusableSchemes.has(url))
140
+ continue;
141
+ warnedUnusableSchemes.add(url);
142
+ ctx.logger.warn(`[model-proxy] ${redactProxyUrl(url)} needs the optional dependency "socks" — run: pnpm add socks`);
143
+ }
144
+ scheduleProbes(activeProxyUrls);
145
+ }
146
+ finally {
147
+ reconciling = false;
148
+ if (pendingReconcile) {
149
+ pendingReconcile = false;
150
+ void runReconcile();
151
+ }
152
+ }
153
+ };
154
+ const reconcileConfigSideEffects = () => {
155
+ void runReconcile();
156
+ };
157
+ // ── fetch wrapper lifecycle: installed only while routing is possible ──
158
+ // Wrapping globalThis.fetch is a process-wide side effect, so the wrapper
159
+ // exists exactly while shouldWrapFetch(config) holds: disabled or fully
160
+ // direct configs keep the global untouched. Kept in sync from the entry
161
+ // config and every settings change; the fiber disposer always unwinds.
162
+ let uninstallFetch;
163
+ const fetchLogger = { error: (msg) => ctx.logger.error(msg) };
164
+ const syncFetchWrapper = () => {
165
+ if (shouldWrapFetch(current())) {
166
+ if (!uninstallFetch) {
167
+ uninstallFetch = installFetchWrapper(fetchLogger);
168
+ ctx.logger.info('[model-proxy] fetch wrapper installed');
169
+ }
170
+ }
171
+ else if (uninstallFetch) {
172
+ try {
173
+ uninstallFetch();
174
+ }
175
+ catch { }
176
+ uninstallFetch = undefined;
177
+ ctx.logger.info('[model-proxy] fetch wrapper removed');
178
+ }
179
+ };
180
+ ctx.effect(() => {
181
+ syncFetchWrapper();
182
+ return () => {
183
+ try {
184
+ uninstallFetch?.();
185
+ }
186
+ catch { }
187
+ uninstallFetch = undefined;
188
+ ctx.logger.info('[model-proxy] fetch wrapper removed');
189
+ };
190
+ }, 'model-proxy: fetch wrapper');
191
+ // Dispatcher pools must not outlive the plugin fiber: on unload/HMR reload
192
+ // the reconcile loop stops running, so a full close-and-evict here is the
193
+ // only guarantee the sockets are retired.
194
+ ctx.effect(() => () => {
195
+ const n = clearDispatcherCache();
196
+ if (n > 0)
197
+ ctx.logger.info(`[model-proxy] closed ${n} dispatcher pool(s)`);
198
+ }, 'model-proxy: dispatcher cache');
199
+ // 1) Settings namespace — live, validated, layered over entry.
200
+ // Registered AFTER the wrapper/disposer effects above so a synchronous
201
+ // first onChange cannot touch a not-yet-initialized binding.
202
+ installSettingsSection(ctx, NS, ModelProxyConfig, entry, {
203
+ validate(value) {
204
+ assertServiceable(value);
205
+ },
206
+ setSource(next) {
207
+ current = next;
208
+ },
209
+ onChange() {
210
+ syncFetchWrapper();
211
+ reconcileConfigSideEffects();
212
+ const cfg = current();
213
+ if (!cfg.debug)
214
+ return;
215
+ const summary = cfg.rules
216
+ .map((r) => `${r.provider}/${r.model}→${r.proxyUrl ? redactProxyUrl(r.proxyUrl) : 'direct'}${r.purpose ? `@${r.purpose}` : ''}`)
217
+ .join(', ');
218
+ ctx.logger.info(`[model-proxy] config applied: enabled=${cfg.enabled} rules=${cfg.rules.length}${summary ? ` [${summary}]` : ''} defaultProxy=${cfg.defaultProxy ? redactProxyUrl(cfg.defaultProxy) : 'direct'}`);
219
+ },
220
+ });
221
+ // Kick initial reconciliation (entry-level config, credential cache warmup,
222
+ // first probes). Fire-and-forget: failures are logged inside. The fetch
223
+ // wrapper itself was already synced from the entry config by its effect.
224
+ reconcileConfigSideEffects();
225
+ // 3) llm/stream waterfall — per-request proxy decision
226
+ // We use waterfall mode: decide, stash in ALS, then delegate.
227
+ // The listener must be SYNC and return an AsyncIterable: cordis waterfall
228
+ // composition does not await listener results, and downstream listeners
229
+ // (e.g. session-checkpoint-policy) `yield* next()` — an `async` listener
230
+ // hands them a Promise and iteration throws "not async iterable".
231
+ ctx.on('llm/stream', (opts, next) => {
232
+ const cfg = current();
233
+ const proxyUrl = resolveProxy(cfg, opts.provider, opts.model, opts.purpose, effectiveRuleUrl);
234
+ const label = `${opts.provider}/${opts.model}${opts.purpose ? `@${opts.purpose}` : ''}`;
235
+ // Routing decision is logged only when the user asked for debug output,
236
+ // and always redacted.
237
+ if (cfg.debug) {
238
+ ctx.logger.info(`[model-proxy] ${label} → ${proxyUrl ? redactProxyUrl(proxyUrl) : 'direct'}`);
239
+ }
240
+ // Fast path: globally disabled, nothing configured, or no match — pass
241
+ // through untouched without entering the ALS context.
242
+ if (!proxyUrl)
243
+ return next();
244
+ // AsyncLocalStorage does NOT propagate into a deferred async generator:
245
+ // the consumer resumes it from its own context, so a store set here would
246
+ // be gone by the time the adapter's fetch runs. Instead we re-enter the
247
+ // store per protocol call (next/return/throw), which keeps it alive across
248
+ // every await inside the adapter's stream.
249
+ const ctxData = { proxyUrl, provider: opts.provider, model: opts.model };
250
+ const iterator = next()[Symbol.asyncIterator]();
251
+ return {
252
+ [Symbol.asyncIterator]() {
253
+ return {
254
+ next: () => als.run(ctxData, () => iterator.next()),
255
+ return: (value) => als.run(ctxData, () => iterator.return ? iterator.return(value) : Promise.resolve({ done: true, value })),
256
+ throw: (error) => als.run(ctxData, () => iterator.throw ? iterator.throw(error) : Promise.reject(error)),
257
+ };
258
+ },
259
+ };
260
+ });
261
+ }
262
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAErF,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAuD,MAAM,aAAa,CAAA;AACpJ,OAAO,EAAE,GAAG,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC3E,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAyB,MAAM,kBAAkB,CAAA;AAChG,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,MAAM,CAAC,MAAM,IAAI,GAAG,wBAAwB,CAAA;AAC5C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAA;AAEzC,MAAM,EAAE,GAAG,iBAAiB,CAAC,aAAa,CAAC,CAAA;AAE3C,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,KAAiB;IACnD,2DAA2D;IAC3D,IAAI,eAA2B,CAAA;IAC/B,IAAI,CAAC;QACH,eAAe,GAAG,gBAAgB,CAAC,KAAK,IAAK,EAAc,CAAe,CAAA;QAC1E,iBAAiB,CAAC,eAAe,CAAC,CAAA;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,6CAA6C,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC3E,eAAe,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAgB,CAAA;IAC9F,CAAC;IAED,wEAAwE;IACxE,wEAAwE;IACxE,0EAA0E;IAC1E,IAAI,OAAO,GAAqB,GAAG,EAAE,CAAC,eAAe,CAAA;IAOrD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAmB,CAAA;IAC5C,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAA;IAExC,SAAS,iBAAiB,CAAC,MAAoC,EAAE,GAAW;QAC1E,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,mCAAmC,EAAE,CAAC,CAAA;YACrF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;QAC1B,CAAC;QACD,OAAO,MAAM;aACV,OAAO,CAAC,GAAG,CAAC;aACZ,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACZ,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjF,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,GAAG,aAAa,EAAE,CAAC,CAAA;YACnF,CAAC;iBAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,GAAG,6BAA6B,EAAE,CAAC,CAAA;YACnG,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAA;YACxD,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACtB,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC/D,CAAC,CAAC,CAAA;IACN,CAAC;IAED,8EAA8E;IAC9E,MAAM,gBAAgB,GAAG,CAAC,CAAY,EAAsB,EAAE;QAC5D,IAAI,CAAC,CAAC,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAA;QACjC,MAAM,GAAG,GAAG,CAAC,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,SAAS,CAAA;QAChD,IAAI,CAAC,GAAG;YAAE,OAAO,CAAC,CAAC,QAAQ,CAAA;QAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,GAAG,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;gBACvB,MAAM,GAAG,GAAG,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAA;gBACrE,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,4BAA4B,GAAG,iBAAiB,GAAG,cAAc,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,yCAAyC,CAChI,CAAA;YACH,CAAC;YACD,OAAO,CAAC,CAAC,QAAQ,CAAA;QACnB,CAAC;QACD,OAAO,eAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAA;IAC7D,CAAC,CAAA;IAED,2EAA2E;IAC3E,IAAI,eAAe,GAAG,IAAI,GAAG,EAAU,CAAA;IACvC,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAU,CAAA;IAE/C,2EAA2E;IAC3E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAA;IACpC,IAAI,UAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;IACjD,MAAM,cAAc,GAAG,CAAC,IAAsB,EAAQ,EAAE;QACtD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAQ;YACjC,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;YACpE,IAAI,OAAO,IAAI,CAAC,wBAAwB,EAAE;gBAAE,SAAQ,CAAC,uBAAuB;YAC5E,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YACnB,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBACtC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAA;gBACpC,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;gBAC/B,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;oBACd,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,GAAG,QAAQ,MAAM,CAAC,SAAS,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;gBAC5F,CAAC;qBAAM,CAAC;oBACN,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,GAAG,YAAY,MAAM,CAAC,SAAS,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;gBAChG,CAAC;YACH,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACpB,CAAC;IACH,CAAC,CAAA;IAED,2EAA2E;IAC3E,mEAAmE;IACnE,IAAI,WAAW,GAAG,KAAK,CAAA;IACvB,IAAI,gBAAgB,GAAG,KAAK,CAAA;IAC5B,MAAM,YAAY,GAAG,KAAK,IAAmB,EAAE;QAC7C,IAAI,WAAW,EAAE,CAAC;YAChB,gBAAgB,GAAG,IAAI,CAAA;YACvB,OAAM;QACR,CAAC;QACD,WAAW,GAAG,IAAI,CAAA;QAClB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,OAAO,EAAE,CAAA;YAErB,sEAAsE;YACtE,MAAM,MAAM,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAA;YACzC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACxG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;YAEpE,uEAAuE;YACvE,qEAAqE;YACrE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;YAC9B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;gBAC1B,MAAM,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAA;gBAC7B,IAAI,CAAC;oBAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;YACpB,CAAC;YACD,IAAI,GAAG,CAAC,YAAY;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;YAEhD,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;gBAClC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,oBAAoB,CAAC,GAAG,CAAC,CAAA;YAC/C,CAAC;YACD,eAAe,GAAG,IAAI,CAAA;YAEtB,KAAK,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;gBAClC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;oBAAE,SAAQ;gBACpE,IAAI,wBAAwB,EAAE,IAAI,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAQ;gBAC1E,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;gBAC9B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,cAAc,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAA;YACrH,CAAC;YAED,cAAc,CAAC,eAAe,CAAC,CAAA;QACjC,CAAC;gBAAS,CAAC;YACT,WAAW,GAAG,KAAK,CAAA;YACnB,IAAI,gBAAgB,EAAE,CAAC;gBACrB,gBAAgB,GAAG,KAAK,CAAA;gBACxB,KAAK,YAAY,EAAE,CAAA;YACrB,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IACD,MAAM,0BAA0B,GAAG,GAAS,EAAE;QAC5C,KAAK,YAAY,EAAE,CAAA;IACrB,CAAC,CAAA;IAED,0EAA0E;IAC1E,0EAA0E;IAC1E,wEAAwE;IACxE,wEAAwE;IACxE,uEAAuE;IACvE,IAAI,cAAwC,CAAA;IAC5C,MAAM,WAAW,GAAG,EAAE,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAA;IACrE,MAAM,gBAAgB,GAAG,GAAS,EAAE;QAClC,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,cAAc,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAA;gBACjD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAA;YAC1D,CAAC;QACH,CAAC;aAAM,IAAI,cAAc,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,cAAc,EAAE,CAAA;YAClB,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,cAAc,GAAG,SAAS,CAAA;YAC1B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;IACH,CAAC,CAAA;IAED,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE;QACd,gBAAgB,EAAE,CAAA;QAClB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC;gBACH,cAAc,EAAE,EAAE,CAAA;YACpB,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,cAAc,GAAG,SAAS,CAAA;YAC1B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC,CAAA;IACH,CAAC,EAAE,4BAA4B,CAAC,CAAA;IAEhC,2EAA2E;IAC3E,0EAA0E;IAC1E,0CAA0C;IAC1C,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE;QACpB,MAAM,CAAC,GAAG,oBAAoB,EAAE,CAAA;QAChC,IAAI,CAAC,GAAG,CAAC;YAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,qBAAqB,CAAC,CAAA;IAC5E,CAAC,EAAE,+BAA+B,CAAC,CAAA;IAEnC,+DAA+D;IAC/D,uEAAuE;IACvE,6DAA6D;IAC7D,sBAAsB,CAAC,GAAG,EAAE,EAAE,EAAE,gBAA2E,EAAE,KAAgB,EAAE;QAC7H,QAAQ,CAAC,KAAc;YACrB,iBAAiB,CAAC,KAAmB,CAAC,CAAA;QACxC,CAAC;QACD,SAAS,CAAC,IAAsB;YAC9B,OAAO,GAAG,IAAwB,CAAA;QACpC,CAAC;QACD,QAAQ;YACN,gBAAgB,EAAE,CAAA;YAClB,0BAA0B,EAAE,CAAA;YAC5B,MAAM,GAAG,GAAG,OAAO,EAAE,CAAA;YACrB,IAAI,CAAC,GAAG,CAAC,KAAK;gBAAE,OAAM;YACtB,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK;iBACtB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;iBAC/H,IAAI,CAAC,IAAI,CAAC,CAAA;YACb,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,yCAAyC,GAAG,CAAC,OAAO,UAAU,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,iBAAiB,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CACjM,CAAA;QACH,CAAC;KACF,CAAC,CAAA;IAEF,4EAA4E;IAC5E,wEAAwE;IACxE,yEAAyE;IACzE,0BAA0B,EAAE,CAAA;IAE5B,uDAAuD;IACvD,iEAAiE;IACjE,6EAA6E;IAC7E,2EAA2E;IAC3E,4EAA4E;IAC5E,qEAAqE;IACrE,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,IAAqB,EAAE,IAAkC,EAA0B,EAAE;QACzG,MAAM,GAAG,GAAG,OAAO,EAAE,CAAA;QACrB,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAA;QAC7F,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;QAEvF,wEAAwE;QACxE,uBAAuB;QACvB,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACd,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC/F,CAAC;QAED,uEAAuE;QACvE,sDAAsD;QACtD,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,EAA0B,CAAA;QAEpD,wEAAwE;QACxE,0EAA0E;QAC1E,wEAAwE;QACxE,2EAA2E;QAC3E,2CAA2C;QAC3C,MAAM,OAAO,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAA;QACxE,MAAM,QAAQ,GAAI,IAAI,EAA6B,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAA;QAC3E,OAAO;YACL,CAAC,MAAM,CAAC,aAAa,CAAC;gBACpB,OAAO;oBACL,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACnD,MAAM,EAAE,CAAC,KAAe,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CACjD,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;oBACpF,KAAK,EAAE,CAAC,KAAe,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAChD,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;iBAClE,CAAA;YACH,CAAC;SACsB,CAAA;IAC3B,CAAC,CAAC,CAAA;AACJ,CAAC"}