@duckmind/dm-windows-x64 0.60.6 → 0.60.9
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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +70 -123
- package/extensions/dm-9router-ext/src/index.js +410 -5
- package/extensions/dm-caveman/extensions/caveman.js +283 -12
- package/extensions/dm-cliproxy/index.js +182 -2
- package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
- package/extensions/dm-cliproxy/src/apply.js +228 -1
- package/extensions/dm-cliproxy/src/cache.js +42 -1
- package/extensions/dm-cliproxy/src/commands.js +50 -2
- package/extensions/dm-cliproxy/src/compat.js +81 -1
- package/extensions/dm-cliproxy/src/config.js +192 -2
- package/extensions/dm-cliproxy/src/conflicts.js +46 -1
- package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
- package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
- package/extensions/dm-cliproxy/src/log.js +23 -1
- package/extensions/dm-cliproxy/src/status-quota.js +77 -1
- package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
- package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
- package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
- package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
- package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
- package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
- package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
- package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
- package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
- package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
- package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
- package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
- package/extensions/dm-context/src/context.js +144 -1
- package/extensions/dm-context/src/index.js +339 -7
- package/extensions/dm-context/src/utils.js +9 -1
- package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
- package/extensions/dm-cua/index.js +75 -6
- package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
- package/extensions/dm-cua/src/browser-install.mjs +331 -2
- package/extensions/dm-fff/src/index.js +688 -12
- package/extensions/dm-fff/src/query.js +60 -1
- package/extensions/dm-goal/src/goal.js +894 -23
- package/extensions/dm-image2/index.js +103 -9
- package/extensions/dm-image2/src/image-lib.mjs +1275 -8
- package/extensions/dm-subagents/install.mjs +62 -8
- package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
- package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
- package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
- package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
- package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
- package/extensions/dm-subagents/src/agents/agents.js +1139 -11
- package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
- package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
- package/extensions/dm-subagents/src/agents/identity.js +29 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
- package/extensions/dm-subagents/src/agents/skills.js +614 -6
- package/extensions/dm-subagents/src/extension/config.js +35 -2
- package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
- package/extensions/dm-subagents/src/extension/doctor.js +172 -15
- package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
- package/extensions/dm-subagents/src/extension/index.js +521 -359
- package/extensions/dm-subagents/src/extension/rpc.js +266 -7
- package/extensions/dm-subagents/src/extension/schemas.js +275 -1
- package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
- package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
- package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
- package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
- package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
- package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
- package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
- package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
- package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
- package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
- package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
- package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
- package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
- package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
- package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
- package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
- package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
- package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
- package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
- package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
- package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
- package/extensions/dm-subagents/src/shared/formatters.js +98 -7
- package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
- package/extensions/dm-subagents/src/shared/model-info.js +62 -1
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
- package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
- package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
- package/extensions/dm-subagents/src/shared/settings.js +198 -11
- package/extensions/dm-subagents/src/shared/status-format.js +53 -1
- package/extensions/dm-subagents/src/shared/types.js +184 -6
- package/extensions/dm-subagents/src/shared/utils.js +462 -2
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
- package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
- package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
- package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
- package/extensions/dm-subagents/src/tui/render.js +1542 -4
- package/extensions/dm-usage/index.js +1294 -9
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
- package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
- package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
- package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
- package/extensions/greedysearch-dm/bin/search.mjs +620 -540
- package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
- package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
- package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
- package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
- package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
- package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
- package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
- package/extensions/greedysearch-dm/index.js +123 -23
- package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
- package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
- package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
- package/extensions/greedysearch-dm/src/github.mjs +222 -7
- package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
- package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
- package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
- package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
- package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
- package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
- package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
- package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
- package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
- package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
- package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
- package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
- package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
- package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
- package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
- package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
- package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
- package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
- package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
- package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
- package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
- package/package.json +1 -1
|
@@ -1,2 +1,199 @@
|
|
|
1
|
-
import
|
|
2
|
-
`,{encoding:"utf8"}),Nj(Q),D0(Q),Q=void 0,Bj(J,j),Z$(j)}finally{if(Q!==void 0)D0(Q);v$(J,{force:!0})}}function B$($){bj($),K.info("config saved to",l)}function nj($){if(!$||typeof $!=="object")return structuredClone(W$);let j=$,J=structuredClone(W$),Q=j.proxy??{};if(J.proxy.endpoint=typeof Q.endpoint==="string"?Q.endpoint:J.proxy.endpoint,J.proxy.apiKey=typeof Q.apiKey==="string"?Q.apiKey:"",typeof Q.usageKey==="string")J.proxy.usageKey=Q.usageKey;if(typeof Q.providerPrefix==="string"&&Q.providerPrefix.trim())J.proxy.providerPrefix=Q.providerPrefix.trim();if(j.builtinProviders&&typeof j.builtinProviders==="object")J.builtinProviders=j.builtinProviders;if(j.customProviders&&typeof j.customProviders==="object")J.customProviders=j.customProviders;if(Array.isArray(j.discoveryExcludes))J.discoveryExcludes=j.discoveryExcludes.filter((Y)=>typeof Y==="string");if(j.overrides&&typeof j.overrides==="object")J.overrides=j.overrides;if(typeof j.refreshIntervalMinutes==="number")J.refreshIntervalMinutes=j.refreshIntervalMinutes;if(typeof j.usageCacheTtlMs==="number")J.usageCacheTtlMs=j.usageCacheTtlMs;return J}function h($){if(!$)return"";let j=$.trim();if(j.startsWith("!"))try{return Hj(j.slice(1),{encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()}catch(J){let Q=J?.status,Y=typeof Q==="number"?` (exit ${Q})`:"";return K.warn(`failed to resolve "!" config value${Y}`),""}if(j.startsWith("$"))return process.env[j.slice(1)]??"";return j}import{createHash as Pj}from"node:crypto";import{existsSync as c1,readFileSync as l1,writeFileSync as yj}from"node:fs";import{dirname as Cj,join as wj}from"node:path";var uj=wj(f$,"discovery-cache.json");function A0($,j){return Pj("sha256").update($.trim().replace(/\/+$/,"")).update("\x00").update(j).digest("hex")}function h$($,j,J=uj){try{U$(Cj(J));let Q={savedAt:Date.now(),scope:j,discovery:$};yj(J,JSON.stringify(Q),{encoding:"utf8",mode:384}),Z$(J)}catch(Q){K.warn("failed to write discovery cache:",Q.message)}}var X$="dm-cliproxy/0.3.5",vj=5000;async function G0($,j){let J=new AbortController,Q=setTimeout(()=>J.abort(Error("timeout")),vj);try{return await fetch($,{...j,signal:J.signal})}finally{clearTimeout(Q)}}function gj($){return new URL("/.well-known/pi",new URL($).origin).toString()}async function fj($){let j=gj($.proxy.endpoint),J;try{J=await G0(j,{headers:{"User-Agent":X$,Accept:"application/json"}})}catch(X){return K.debug("well-known fetch failed:",X.message,"— falling back to /v1/models"),null}if(!J.ok)return K.debug(`well-known returned ${J.status} — falling back to /v1/models`),null;let Q;try{Q=await J.json()}catch{return K.debug("well-known returned non-JSON — falling back to /v1/models"),null}if(!Q||Q.schemaVersion!==1)return K.debug("well-known schemaVersion != 1 — falling back to /v1/models"),null;let Y=[],Z=Q.builtinProviders??{};for(let[X,q]of Object.entries(Z)){if(!q||!Array.isArray(q.models))continue;let V=q.models.map((M)=>({id:String(M.id),name:typeof M.name==="string"?M.name:String(M.id),reasoning:Boolean(M.reasoning??O$(String(M.id))),contextWindow:typeof M.contextWindow==="number"?M.contextWindow:200000,maxTokens:typeof M.maxTokens==="number"?M.maxTokens:16000,cost:M.cost??{input:0,output:0,cacheRead:0,cacheWrite:0}}));Y.push({name:X,api:q.api??"openai-responses",models:V})}let z=(Array.isArray(Q.customModelPool)?Q.customModelPool:[]).map((X)=>({id:String(X.id),name:typeof X.name==="string"?X.name:String(X.id),reasoning:Boolean(X.reasoning??O$(String(X.id))),contextWindow:typeof X.contextWindow==="number"?X.contextWindow:128000,maxTokens:typeof X.maxTokens==="number"?X.maxTokens:16000,cost:X.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},api:X.api??"openai-completions",suggestedProvider:U0(typeof X.suggestedProviderName==="string"?X.suggestedProviderName:"misc",$.proxy.providerPrefix),ownedBy:typeof X.owned_by==="string"?X.owned_by:""}));return{source:"well-known",upstreamVersion:typeof Q?.upstream?.upstreamVersion==="string"?Q.upstream.upstreamVersion:null,builtinProviders:Y,customPool:z,serverDiscoveryExcludes:Array.isArray(Q.discoveryExcludes)?Q.discoveryExcludes.filter((X)=>typeof X==="string"):[],upstreamTotal:typeof Q?.counts?.upstreamTotal==="number"?Q.counts.upstreamTotal:0}}async function hj($,j){let J=new URL("/v1/models",new URL($.proxy.endpoint).origin).toString(),Q=await G0(J,{headers:{Authorization:`Bearer ${j}`,Accept:"application/json","User-Agent":X$}});if(!Q.ok)throw Error(`/v1/models returned ${Q.status}`);let Y=await Q.json();if(!Y?.data||!Array.isArray(Y.data))return[];return Y.data.map((Z)=>({id:typeof Z.id==="string"?Z.id:"",owned_by:typeof Z.owned_by==="string"?Z.owned_by:""})).filter((Z)=>Z.id)}function pj($,j){let J=j.discoveryExcludes,Q=new Map,Y=[],Z=0;for(let z of $){if(Z++,V0(z.id,J))continue;if(z.owned_by==="openai"){let M=Y$(z.id);H0(Q,"openai","openai-responses",O0(M));continue}if(z.owned_by==="anthropic"){let M=Y$(z.id);H0(Q,"anthropic","anthropic-messages",O0(M));continue}let{slug:X,api:q}=E0(z.owned_by,j.proxy.providerPrefix),V=Y$(z.id);Y.push({id:z.id,name:V.name??z.id,reasoning:V.reasoning??!1,contextWindow:V.contextWindow??128000,maxTokens:V.maxTokens??16000,cost:V.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},api:q,suggestedProvider:X,ownedBy:z.owned_by})}return{source:"v1-models",upstreamVersion:null,builtinProviders:Array.from(Q.values()),customPool:Y,serverDiscoveryExcludes:[],upstreamTotal:Z}}function H0($,j,J,Q){let Y=$.get(j);if(!Y)Y={name:j,api:J,models:[]},$.set(j,Y);Y.models.push(Q)}function O0($){return{id:$.id,name:$.name??$.id,reasoning:$.reasoning??!1,contextWindow:$.contextWindow??128000,maxTokens:$.maxTokens??16000,cost:$.cost??{input:0,output:0,cacheRead:0,cacheWrite:0}}}async function N0($,j){let J=A0($.proxy.endpoint,j),Q=await fj($);if(Q)return K.debug(`discovery via /.well-known/pi: ${Q.builtinProviders.length} builtin, ${Q.customPool.length} custom`),h$(Q,J),Q;if(!j)throw Error("well-known unavailable AND proxy apiKey is empty — cannot fall back to /v1/models. Set proxy.apiKey in config.");let Y=await hj($,j),Z=pj(Y,$);return K.debug(`discovery via /v1/models: ${Z.builtinProviders.length} builtin, ${Z.customPool.length} custom`),h$(Z,J),Z}function W0($){let j=new Set;for(let J of $.builtinProviders)for(let Q of J.models)j.add(Q.id);for(let J of $.customPool)j.add(J.id);return j}var k0=new WeakMap,p$=new WeakMap;function c$($){let j=(p$.get($)??0)+1;return p$.set($,j),j}function L$($,j){return j===void 0||j===(p$.get($)??0)}function T0($,j,J){let Q=k0.get($)??new Set;for(let Y of Q){if(j.has(Y))continue;$.unregisterProvider(Y),J.unregistered.push(Y)}k0.set($,new Set(j))}async function l$($,j,J,Q){let Y={registered:[],skipped:[],unregistered:[]};if(!L$($,Q))return Y.skipped.push({provider:"*",reason:"stale refresh generation"}),Y;let Z=new Set,z=W0(J),X=h(j.proxy.apiKey);if(!X)return K.warn("proxy apiKey is empty — dm.registerProvider calls will be skipped"),T0($,Z,Y),Y;let q=new Map;for(let M of J.builtinProviders)q.set(M.name,M.models.map((U)=>({id:U.id,name:U.name,reasoning:U.reasoning,contextWindow:U.contextWindow,maxTokens:U.maxTokens,cost:U.cost})));for(let[M,U]of Object.entries(j.builtinProviders)){if(!L$($,Q))return Y;if(!U?.enabled){Y.skipped.push({provider:M,reason:"disabled"});continue}if(!Array.isArray(U.models)||U.models.length===0){Y.skipped.push({provider:M,reason:"empty whitelist"});continue}let F=[];try{F=cj(M)}catch{}let T=new Map(F.map((N)=>[N.id,N])),H=new Map((q.get(M)??[]).map((N)=>[N.id,N])),D=[];for(let N of U.models){if(!z.has(N))continue;let G=T.get(N),n=H.get(N);if(G){D.push({id:G.id,name:G.name,reasoning:G.reasoning,contextWindow:G.contextWindow,maxTokens:G.maxTokens,cost:G.cost,input:G.input,api:G.api,thinkingLevelMap:G.thinkingLevelMap});continue}if(!n)continue;let j$=M==="anthropic"?"anthropic-messages":U.apiOverride??"openai-responses";D.push({id:n.id,name:n.name,reasoning:n.reasoning,contextWindow:n.contextWindow,maxTokens:n.maxTokens,cost:n.cost,input:["text"],api:j$})}if(D.length===0){Y.skipped.push({provider:M,reason:"no whitelisted models present on proxy"});continue}let A=U.apiOverride??D[0].api;if(!w$.has(A)){Y.skipped.push({provider:M,reason:`api "${A}" not in allowlist`});continue}let B=D.map((N)=>{let G=j.overrides[N.id]??{},n={id:N.id,name:typeof G.name==="string"?G.name:N.name,api:A,reasoning:typeof G.reasoning==="boolean"?G.reasoning:N.reasoning,input:N.input,cost:G.cost??N.cost,contextWindow:typeof G.contextWindow==="number"?G.contextWindow:N.contextWindow,maxTokens:typeof G.maxTokens==="number"?G.maxTokens:N.maxTokens};if(N.thinkingLevelMap)n.thinkingLevelMap=N.thinkingLevelMap;return n}),C={name:M,baseUrl:u$(A,j.proxy.endpoint),apiKey:X,authHeader:!0,api:A,models:B};$.registerProvider(M,C),Z.add(M),Y.registered.push({provider:M,modelCount:B.length,api:A})}let V=new Map(J.customPool.map((M)=>[M.id,M]));for(let[M,U]of Object.entries(j.customProviders)){if(!L$($,Q))return Y;if(!w$.has(U.api)){Y.skipped.push({provider:M,reason:`api "${U.api}" not in allowlist`});continue}let F=U.models.filter((D)=>z.has(D.id));if(F.length===0){Y.skipped.push({provider:M,reason:"no configured models present on proxy"});continue}let T=F.map((D)=>{let A=V.get(D.id),B=Y$(D.id),C=j.overrides[D.id]??{};return{id:D.id,name:D.name??A?.name??B.name??D.id,api:U.api,reasoning:lj(D.reasoning,A?.reasoning,B.reasoning,C.reasoning)??!1,input:["text"],cost:D.cost??A?.cost??B.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:B0(D.contextWindow,A?.contextWindow,B.contextWindow,C.contextWindow)??128000,maxTokens:B0(D.maxTokens,A?.maxTokens,B.maxTokens,C.maxTokens)??16000}}),H={name:M,baseUrl:u$(U.api,j.proxy.endpoint),apiKey:X,authHeader:!0,api:U.api,models:T};$.registerProvider(M,H),Z.add(M),Y.registered.push({provider:M,modelCount:T.length,api:U.api})}if(L$($,Q))T0($,Z,Y);return K.debug(`applyAll: registered ${Y.registered.length} providers, skipped ${Y.skipped.length}, unregistered ${Y.unregistered.length}`),Y}function B0(...$){for(let j of $)if(typeof j==="number")return j;return}function lj(...$){for(let j of $)if(typeof j==="boolean")return j;return}var mj=15000,E$=null;function K$(){E$=null}async function L0($,j,J={}){if(!J.force&&E$&&Date.now()-E$.fetchedAt<$.usageCacheTtlMs)return E$.doc;if(!j)throw Error("usage key not configured; set proxy.usageKey in config and rerun");let Q=new URL("/api/usage",new URL($.proxy.endpoint).origin).toString(),Y=new AbortController,Z=setTimeout(()=>Y.abort(Error("timeout")),mj),z;try{z=await fetch(Q,{headers:{"X-Plugin-Key":j,Accept:"application/json","User-Agent":X$},signal:Y.signal})}finally{clearTimeout(Z)}if(!z.ok)throw Error(`/api/usage returned ${z.status}`);let X=await z.json();if(!X||X.schemaVersion!==1||!Array.isArray(X.accounts))throw Error("/api/usage returned unexpected payload shape");return E$={fetchedAt:Date.now(),doc:X},K.debug("usage fetched, accounts:",X.accounts.length),X}import{visibleWidth as s$}from"@duckmind/dm-tui";import{visibleWidth as K0}from"@duckmind/dm-tui";function S($,j){let J=K0($);if(J===j)return $;if(J<j)return $+" ".repeat(j-J);return m$($,j)}function m$($,j){if(j<=0)return"\x1B[0m";let J="",Q=0,Y=0;while(Y<$.length){let Z=$[Y];if(Z==="\x1B"&&$[Y+1]==="["){let X=$.indexOf("m",Y+2);if(X>=0){J+=$.slice(Y,X+1),Y=X+1;continue}}let z=K0(Z);if(Q+z>j)break;J+=Z,Q+=z,Y++}return`${J}\x1B[0m`}var sj="│",oj="╭",ij="╮",rj="╰",dj="╯",D$="─";function r($,j){let J=Math.max(4,j.width);return[tj($,j.title,j.titleColor??"accent",J),...j.lines.map((Q)=>aj($,Q,J)),ej($,j.footer,J)]}function tj($,j,J,Q){let Y=j??"",Z=s$(Y),z=Math.max(0,Q-3-Z),X=Y?$.bold($.fg(J,Y)):"",q=`${oj}${D$}${X}${D$.repeat(z)}${ij}`;return $.fg("borderAccent",q)}function aj($,j,J){let Q=Math.max(0,J-2),Y=$.fg("borderAccent",sj);return`${Y}${S(j,Q)}${Y}`}function ej($,j,J){let Q=`${rj}${D$}`,Y=`${dj}`,Z=3;if(!j||!j.hint&&!j.badge){let H=Math.max(0,J-3);return $.fg("borderAccent",`${Q}${D$.repeat(H)}${Y}`)}let z=j.hint??"",X=j.badge??"",q=s$(z),V=s$(X),M=Math.max(0,J-3-q-V),U=$.fg("dim",z),F=X?$.fg("muted",X):"",T=D$.repeat(M);return`${$.fg("borderAccent",Q)}${U}${$.fg("borderAccent",T)}${F}${$.fg("borderAccent",Y)}`}function F$($){return Math.max(0,$-2)}import{existsSync as F0}from"node:fs";import{homedir as $1}from"node:os";import{getKeybindings as j1,Input as o$,matchesKey as J1}from"@duckmind/dm-tui";var Q1=[{label:"endpoint",hint:"OpenAI-style base URL of your CliProxyAPI deployment, must include /v1",required:!0,validate:($)=>{try{if(!new URL($).pathname.endsWith("/v1"))return"must end with /v1";return null}catch{return"not a valid URL"}}},{label:"apiKey",hint:"CliProxyAPI bearer key. Input is masked; blank keeps an existing key",required:!0,secret:!0},{label:"providerPrefix",hint:"Prefix for your custom provider names. Suggested groups become <prefix>-glm, <prefix>-gemini, etc. Use any short slug (letters/digits/dashes).",required:!0,validate:($)=>/^[a-z0-9][a-z0-9-]*$/i.test($)?null:"letters/digits/dashes only"},{label:"usageKey",hint:"Optional X-Plugin-Key for /api/usage. Input is masked; blank keeps it, '-' clears it",required:!1,secret:!0}];async function S0($,j=!1){let J=T$(),Q={endpoint:J.proxy.endpoint??"",apiKey:J.proxy.apiKey??"",providerPrefix:J.proxy.providerPrefix??"",usageKey:J.proxy.usageKey??""},Y=!1;for(let z of Q1){let X=Q[z.label]??"",q=z.secret?"":j?Q[z.label]??z.initialValue??"":Q[z.label]??"";if(!j&&q)continue;let V=z.secret&&X?{...z,required:!1}:z,M=await X1($,V,q||z.initialValue||"");if(M===void 0){Y=!0;break}let U=Z1(z,X,M);if(U===null){if(z.required){$.ui.notify(`${z.label} is required — aborted`,"warning"),Y=!0;break}}Q[z.label]=U??""}if(Y)return!1;let Z={...J};if(Z.proxy={...J.proxy,endpoint:Q.endpoint??"",apiKey:Q.apiKey??"",providerPrefix:Q.providerPrefix??""},Q.usageKey)Z.proxy.usageKey=Q.usageKey;else delete Z.proxy.usageKey;return B$(Z),$.ui.notify(`saved to ${l}`,"info"),!0}function Y1($){if($.startsWith("!")||$.startsWith("$"))return $;if($.startsWith("~/")){let j=$.replace(/^~/,$1());return F0(j)?`!cat ${$}`:$}if($.startsWith("/"))return F0($)?`!cat ${$}`:$;return $}function Z1($,j,J){let Q=J.trim();if(!Q){if($.secret&&j)return j;return $.required?null:""}if($.secret&&!$.required&&Q==="-")return"";return $.label==="providerPrefix"||$.label==="endpoint"?Q:Y1(Q)}async function X1($,j,J){return $.ui.custom((Q,Y,Z,z)=>z1(Q,Y,j,J,z),{overlay:!0,overlayOptions:{width:100,maxHeight:"60%"}})}class I0{rawInput=new o$;displayInput=new o$;pasteActive=!1;get focused(){return this.rawInput.focused}set focused($){this.rawInput.focused=$,this.displayInput.focused=$}set onSubmit($){this.rawInput.onSubmit=$}set onEscape($){this.rawInput.onEscape=$}getValue(){return this.rawInput.getValue()}setValue($){this.rawInput.setValue($),this.displayInput.setValue("•".repeat($.length))}handleInput($){this.rawInput.handleInput($),this.displayInput.handleInput(this.maskInput($)),this.displayInput.setValue("•".repeat(this.rawInput.getValue().length))}render($){return this.displayInput.render($)}invalidate(){this.rawInput.invalidate(),this.displayInput.invalidate()}maskInput($){let j="";for(let J=0;J<$.length;){if($.startsWith("\x1B[200~",J)){this.pasteActive=!0,j+="\x1B[200~",J+=6;continue}if($.startsWith("\x1B[201~",J)){this.pasteActive=!1,j+="\x1B[201~",J+=6;continue}if(!this.pasteActive&&$.charCodeAt(J)===27)return $;let Q=$.codePointAt(J),Y=String.fromCodePoint(Q);j+=Q<32||Q===127?Y:"•",J+=Y.length}return j}}function z1($,j,J,Q,Y){let Z=J.secret?new I0:new o$;Z.setValue(Q),Z.focused=!0;let z=null,X=(q)=>{if(!q&&!J.required){Y("");return}if(!q&&J.required){z="required field",$.requestRender();return}if(J.validate){let V=q.trim();if(V&&!V.startsWith("!")&&!V.startsWith("$")){let M=J.validate(V);if(M){z=M,$.requestRender();return}}}Y(q)};return Z.onSubmit=X,Z.onEscape=()=>Y(void 0),{render(q){let V=F$(q),M=Z.render(V-4),U=["",` ${j.fg("dim",J.hint)}`,"",...M.map((F)=>` ${j.fg("accent",`❯ ${F}`)}`),"",z?` ${j.fg("error",`! ${z}`)}`:` ${j.fg("dim","enter = save · esc = cancel")}`];return r(j,{width:q,title:` setup: ${J.label} `,lines:U,footer:{hint:" enter = save · esc = cancel "}})},invalidate(){Z.invalidate()},handleInput(q){if(j1().matches(q,"tui.select.cancel")||J1(q,"escape")){Y(void 0);return}z=null,Z.handleInput(q),$.requestRender()}}}import{visibleWidth as S$}from"@duckmind/dm-tui";function x0($,j,J,Q){let Y=$.fg("borderAccent","│"),Z=j.map((z,X)=>{let q=`${X+1} ${z.label}`;return X===J?$.bold($.fg("accent",` ${q} `)):$.fg("dim",` ${q} `)});return S(` ${Z.join(Y)}`,Q)}function b0($,j,J){let Q=j.join($.fg("dim"," "));return S(` ${Q}`,J)}function n0($,j){return $.fg("borderAccent","─".repeat(Math.max(0,j)))}function d($,j,J){let Q=$.slice(j,j+J);while(Q.length<J)Q.push("");return Q}function I$($,j,J,Q,Y,Z){if(Y<=Q)return 0;let z=Math.max(0,Y-Q);if(j<Z)return 0;let X=Math.max(0,Math.min(J,z)),q=X+Z;if($<q)X=Math.max(0,$-Z);let V=X+Q-1;if(j>V)X=Math.min(z,j-(Q-1));return X}function x$($,j,J){let Q=Math.max(0,J-j);return Math.max(0,Math.min($,Q))}import{matchesKey as t}from"@duckmind/dm-tui";import{existsSync as q1,readFileSync as M1}from"node:fs";import{homedir as y0}from"node:os";import{join as C0}from"node:path";var V1=C0(y0(),".dm","models.json"),U1=C0(y0(),".dm","auth.json");function P0($){if(!q1($))return null;try{return JSON.parse(M1($,"utf8"))}catch{return null}}function w0($){let j=[],J=new Set([...Object.keys($.builtinProviders),...Object.keys($.customProviders)]),Q=P0(V1);if(Q&&typeof Q==="object"){for(let Z of Object.keys(Q))if(J.has(Z))j.push({kind:"models.json",provider:Z,detail:`provider "${Z}" has user overrides in ~/.dm/models.json which DM merges on top of our registration`})}let Y=P0(U1);if(Y&&typeof Y==="object"){for(let Z of Object.keys(Y))if(J.has(Z))j.push({kind:"auth.json",provider:Z,detail:`provider "${Z}" has user-set credentials in ~/.dm/auth.json which take priority over our apiKey`})}return j}function u0($){let{theme:j,cfg:J}=$,Q=0,Y=()=>{let X=$.getDiscovery(),q=(H)=>j.fg("success",H),V=(H)=>j.fg("error",H),M=(H)=>j.fg("dim",H),U=[];U.push(`${M("endpoint")} ${J.proxy.endpoint||V("(unset)")}`),U.push(`${M("apiKey")} ${h(J.proxy.apiKey)?q("resolves"):V("empty after resolution")}`),U.push(`${M("usageKey")} ${J.proxy.usageKey?h(J.proxy.usageKey)?q("resolves"):V("set but empty"):M("not configured")}`),U.push(`${M("user-agent")} ${X$}`),U.push(""),U.push(`${M("discovery")} source=${X.source}`),U.push(`${M("upstream")} v=${X.upstreamVersion??"(unknown)"} · ${X.upstreamTotal} ids`);let F=X.builtinProviders.map((H)=>`${H.name}=${H.models.length}`).join(", ")||M("(none)");U.push(`${M("built-in")} ${F}`),U.push(`${M("custom pool")} ${X.customPool.length} models`);let T=w0(J);if(U.push(""),T.length===0)U.push(`${M("conflicts")} ${q("none")}`);else{U.push(`${M("conflicts")}`);for(let H of T)U.push(` ${j.fg("warning",`[${H.kind}]`)} ${H.detail}`)}return U};return{id:"diagnostics",label:"Diagnostics",render:(X,q)=>{let V=Y();return Q=x$(Q,q,V.length),d(V,Q,q).map((M)=>S(` ${M}`,X))},handleInput:(X)=>{let q=Y().length;if(t(X,"up")||t(X,"k"))return Q=Math.max(0,Q-1),!0;if(t(X,"down")||t(X,"j"))return Q=Math.min(Math.max(0,q-1),Q+1),!0;if(t(X,"pageUp")||t(X,"b"))return Q=Math.max(0,Q-10),!0;if(t(X,"pageDown")||t(X,"f"))return Q=Math.min(Math.max(0,q-1),Q+10),!0;return!1},footerHint:()=>" ↑↓ scroll · r refresh discovery "}}import{matchesKey as k}from"@duckmind/dm-tui";function i$($){let j=new Map,J=new Map;for(let Y of $.builtinProviders){let Z=[];for(let z of Y.models)j.set(z.id,{id:z.id,name:z.name,suggestedApi:Y.api,subtitle:z.name&&z.name!==z.id?z.name:void 0,ownedBy:Y.name,reasoning:z.reasoning,contextWindow:z.contextWindow,maxTokens:z.maxTokens,cost:z.cost}),Z.push(z.id);J.set(Y.name,Z)}let Q=[];for(let Y of $.customPool){if(!j.has(Y.id))j.set(Y.id,{id:Y.id,name:Y.name,suggestedApi:Y.api,subtitle:Y.name&&Y.name!==Y.id?Y.name:void 0,origin:Y.suggestedProvider,ownedBy:Y.ownedBy,reasoning:Y.reasoning,contextWindow:Y.contextWindow,maxTokens:Y.maxTokens,cost:Y.cost});Q.push(Y.id)}return{byId:j,builtinModelIds:J,customPoolIds:Q}}function E1($){let j=new Map;for(let[J,Q]of Object.entries($.customProviders))for(let Y of Q.models)j.set(Y.id,J);return j}function $$($,j){if(j.kind==="builtin")return[...$.builtinProviders[j.name]?.models??[]];return $.customProviders[j.name]?.models.map((J)=>J.id)??[]}function v0($,j,J){if(j.kind==="builtin"){let Z=J.builtinModelIds.get(j.name)??[],z=new Set($$($,j));return Z.filter((X)=>!z.has(X))}let Q=E1($),Y=new Set($$($,j));return J.customPoolIds.filter((Z)=>{if(Y.has(Z))return!1;let z=Q.get(Z);return z===void 0||z===j.name})}function D1($){return{id:$.id,name:$.name,contextWindow:$.contextWindow,maxTokens:$.maxTokens,reasoning:$.reasoning,cost:$.cost}}function g0($,j,J){if(j.kind==="builtin"){let Y=$.builtinProviders[j.name]??{enabled:!0,models:[]},Z=new Set(Y.models);Z.add(J.id),$.builtinProviders[j.name]={enabled:!0,apiOverride:Y.apiOverride??null,models:Array.from(Z).sort()};return}for(let[Y,Z]of Object.entries($.customProviders)){if(Y===j.name)continue;let z=Z.models.findIndex((X)=>X.id===J.id);if(z>=0)Z.models.splice(z,1)}let Q=$.customProviders[j.name]??{api:j.api,models:[]};if(!Q.models.some((Y)=>Y.id===J.id))Q.models.push(D1(J));$.customProviders[j.name]=Q}function f0($,j,J){if(j.kind==="builtin"){let Y=$.builtinProviders[j.name];if(!Y)return;Y.models=Y.models.filter((Z)=>Z!==J),Y.enabled=Y.models.length>0;return}let Q=$.customProviders[j.name];if(!Q)return;Q.models=Q.models.filter((Y)=>Y.id!==J)}function r$($,j){if($===j)return!0;let J=["openai-completions","openai-responses"];if(J.includes($)&&J.includes(j))return!0;return!1}function h0($,j){let J=new Map;for(let Q of $){let Y=j.byId.get(Q),Z=Y?.ownedBy||Y?.origin||"misc",z=J.get(Z)??[];z.push(Q),J.set(Z,z)}return Array.from(J.entries()).sort((Q,Y)=>Q[0].localeCompare(Y[0])).map(([Q,Y])=>({label:Q,ids:Y}))}function p0($,j,J){let Q=J.trim().toLowerCase();if(!Q)return $;return $.filter((Y)=>{if(Y.toLowerCase().includes(Q))return!0;let Z=j.byId.get(Y)?.name;return Z?Z.toLowerCase().includes(Q):!1})}import{getModels as R1,getProviders as _1}from"@duckmind/dm-ai/compat";function d$($,j){let J=[],Q=new Set(j.builtinModelIds.keys()),Y=new Set(_1()),Z=new Set([...Q,...Y]);for(let z of Array.from(Z).sort()){let X=j.builtinModelIds.get(z);if(!X||X.length===0)continue;let q;try{let M=R1(z).find((U)=>X.includes(U.id));if(M)q=M.api}catch{}if(!q){let V=X[0];q=j.byId.get(V)?.suggestedApi??"openai-responses"}if(J.push({kind:"builtin",name:z,api:q,subtitle:`built-in · ${q}`}),!$.builtinProviders[z])$.builtinProviders[z]={enabled:!1,models:[]}}for(let z of Object.keys($.customProviders).sort()){let X=$.customProviders[z];J.push({kind:"custom",name:z,api:X.api,subtitle:`custom · ${X.api}`})}return J}import{getKeybindings as A1,matchesKey as z$}from"@duckmind/dm-tui";async function c0($,j){return $.ui.custom((J,Q,Y,Z)=>H1(Q,`Remove custom group “${j}”?`,"All model assignments in this group will be discarded.",Z),{overlay:!0,overlayOptions:{width:80,maxHeight:"40%"}})}function H1($,j,J,Q){return{render(Y){return r($,{width:Y,title:" confirm ",titleColor:"error",lines:["",` ${$.bold(j)}`,` ${$.fg("dim",J)}`,""],footer:{hint:" y / enter = remove · n / esc = cancel "}})},invalidate(){},handleInput(Y){if(A1().matches(Y,"tui.select.cancel")||z$(Y,"escape")||z$(Y,"n")||z$(Y,"q")){Q(!1);return}if(z$(Y,"y")||z$(Y,"enter")||z$(Y,"return")){Q(!0);return}}}}import{getKeybindings as O1,Input as G1,matchesKey as N1}from"@duckmind/dm-tui";async function l0($,j){let J=H$(j,"group");return $.ui.custom((Q,Y,Z,z)=>W1(Q,Y,J,z),{overlay:!0,overlayOptions:{width:80,maxHeight:"40%"}})}function W1($,j,J,Q){let Y=new G1;if(J)Y.setValue(J);Y.focused=!0;let Z=null;return Y.onSubmit=(z)=>{let X=z.trim();if(!/^[a-z0-9][a-z0-9._-]*$/i.test(X)){Z="letters / digits / dot / dash / underscore only",$.requestRender();return}Q(X)},Y.onEscape=()=>Q(null),{render(z){let X=F$(z),q=J?`name shown in /model picker, e.g. ${J}`:"letters / digits / dot / dash / underscore",V=Y.render(X-4),M=["",` ${j.fg("dim",q)}`,"",...V.map((U)=>` ${j.fg("accent",`❯ ${U}`)}`),"",Z?` ${j.fg("error",`! ${Z}`)}`:""];return r(j,{width:z,title:" new custom group ",lines:M,footer:{hint:" enter = create · esc = cancel "}})},invalidate(){Y.invalidate()},handleInput(z){if(O1().matches(z,"tui.select.cancel")||N1(z,"escape")){Q(null);return}Z=null,Y.handleInput(z),$.requestRender()}}}import{visibleWidth as k1}from"@duckmind/dm-tui";function t$($,j,J){if(!j)return" ";if(J)return $.fg("accent","▶ ");return $.fg("muted","▸ ")}function m0($,j){let J=k1($);if(J===j)return $;if(J>j)return m$($,j);return S($,j)}function s0($,j,J){let{theme:Q,width:Y,isCursor:Z,isFocused:z}=J,X=t$(Q,Z,z),q=$.kind==="builtin"?j.builtinProviders[$.name]?.models.length??0:j.customProviders[$.name]?.models.length??0,V=$.kind==="builtin"?Q.fg("accent","B"):Q.fg("warning","C"),M=`${Q.fg("dim","[")}${V}${Q.fg("dim","]")}`,U=q>0?Q.fg("success","●"):Q.fg("dim","○"),F=q>0?Q.fg("success",String(q).padStart(2)):Q.fg("dim"," 0"),T=Z&&z?Q.bold(Q.fg("accent",$.name)):Z?Q.bold($.name):$.name,H=Q.fg("dim",$.api),D=`${X}${M} ${T} ${U}${F} ${H}`;return m0(D,Y)}function o0($,j,J,Q){let Y=t$($,j,J),Z=j&&J?$.bold($.fg("accent","+ new custom group…")):$.fg("accent","+ new custom group…");return S(`${Y}${Z}`,Q)}function a$($,j,J,Q,Y){let{theme:Z,width:z,isCursor:X,isFocused:q}=Y,V=t$(Z,X,q),M=J==="assigned"?Z.fg("success","☑"):Z.fg("dim","☐"),U=Q?` ${Z.fg("warning","⚠")}`:"",F=X&&q?Z.fg("accent",$):$,T=j?.reasoning?` ${Z.fg("accent","✨")}`:"",H=j?Z.fg("muted",` · ${j.suggestedApi}`):Z.fg("error"," · not on proxy"),D=`${V}${M} ${F}${U}${T}${H}`;return m0(D,z)}function i0($,j,J){let Q=`${$.fg("borderAccent","──")} ${$.fg("warning",j)} ${$.fg("borderAccent","──")}`;return S(` ${Q}`,J)}function e$($,j,J){return S(` ${$.fg("dim",j)}`,J)}function r0($,j,J,Q){let Y=Q?$.bold($.fg("accent","❚ ")):$.fg("dim"," ");return S(`${Y}${j}`,J)}function d0($){let{tui:j,theme:J,ctx:Q,cfg:Y}=$,Z=$.onChange??(()=>{}),z=i$($.getDiscovery()),X=d$(Y,z),q="providers",V=0,M=0,U=0,F=0,T=0,H=0,D="",A=!1,B=()=>{if(X.length===0)return null;let E=Math.max(0,Math.min(V,X.length-1));return X[E]??null},C=(E)=>{let O=p0(v0(Y,E,z),z,D);return h0(O,z)},N=(E)=>C(E).flatMap((O)=>O.ids),G=()=>{X=d$(Y,z);let E=Math.max(0,X.length);if(V>E)V=E;let O=B();if(O){let b=$$(Y,O).length,W=N(O).length;if(M>=b)M=Math.max(0,b-1);if(U>=W)U=Math.max(0,W-1)}},n=()=>{z=i$($.getDiscovery()),G()},j$=(E)=>{let O=["providers","assigned","pool"],b=O.indexOf(q);q=E?O[(b-1+O.length)%O.length]:O[(b+1)%O.length]},_=(E)=>{if(q==="providers"){let W=X.length+1;V=Math.max(0,Math.min(V+E,W-1));return}let O=B();if(!O)return;if(q==="assigned"){let W=$$(Y,O).length;if(W===0)return;M=Math.max(0,Math.min(M+E,W-1));return}let b=N(O).length;if(b===0)return;U=Math.max(0,Math.min(U+E,b-1))},x=async()=>{if(q==="providers"){if(V===X.length){let W=await l0(Q,Y.proxy.providerPrefix);if(W&&!Y.customProviders[W]){if(Y.customProviders[W]={api:"openai-completions",models:[]},Z(),G(),V=X.findIndex((I)=>I.kind==="custom"&&I.name===W),V<0)V=X.length-1;q="pool"}j.requestRender();return}q="pool";return}let E=B();if(!E)return;if(q==="assigned"){let W=$$(Y,E)[M];if(!W)return;f0(Y,E,W),Z(),G();return}let O=N(E)[U];if(!O)return;let b=z.byId.get(O);if(!b)return;g0(Y,E,b),Z(),G()},p=async()=>{if(q!=="providers")return;let E=B();if(!E||E.kind!=="custom")return;if(!await c0(Q,E.name)){j.requestRender();return}if(delete Y.customProviders[E.name],Z(),G(),V>=X.length)V=Math.max(0,X.length-1);j.requestRender()},v=(E,O)=>c(E,O);function c(E,O){let b=Math.max(70,E),W=Math.min(56,Math.max(34,Math.floor(b*0.4))),I=b-W-1,s=Math.max(8,O),o=Math.max(5,Math.floor((s-1)/2)),$0=s-o,m=B(),q$=0,b$=0,n$=0,j0=0,P$=0,J0=0,a=[];a.push(J$(" providers ",W,q==="providers"));for(let L=0;L<X.length;L++){let g=X[L],w=q==="providers"&&L===V;if(w)q$=a.length,b$=q$;a.push(s0(g,Y,{theme:J,width:W,isCursor:w,isFocused:q==="providers"}))}{let L=q==="providers"&&V===X.length;if(L)q$=a.length,b$=q$;a.push(o0(J,L,q==="providers",W))}let e=[],Xj=m?`assigned to ${J.bold(m.name)} ${J.fg("dim",`· ${m.api}`)}`:J.fg("dim","no provider selected");if(e.push(J$(" assigned ",I,q==="assigned")),e.push(r0(J,Xj,I,q==="assigned")),m){let L=$$(Y,m);if(L.length===0)e.push(e$(J,"(nothing assigned yet)",I));for(let g=0;g<L.length;g++){let w=L[g],f=z.byId.get(w),A$=q==="assigned"&&g===M;if(A$)n$=e.length,j0=n$;let Q$=f?!r$(m.api,f.suggestedApi):!1;e.push(a$(w,f,"assigned",Q$,{theme:J,width:I,isCursor:A$,isFocused:q==="assigned"}))}}let i=[];if(i.push(P(I,q==="pool")),m){let L=C(m);if(L.reduce((w,f)=>w+f.ids.length,0)===0)i.push(e$(J,D?`(no matches for "${D}")`:"(no models available)",I));else{let w=0;for(let f of L){let A$=i.length;i.push(i0(J,f.label,I));for(let Q$=0;Q$<f.ids.length;Q$++){let q0=f.ids[Q$],C$=z.byId.get(q0),M0=q==="pool"&&w===U;if(M0)P$=i.length,J0=Q$===0?A$:P$;let Vj=C$?!r$(m.api,C$.suggestedApi):!1;i.push(a$(q0,C$,"pool",Vj,{theme:J,width:I,isCursor:M0,isFocused:q==="pool"})),w++}}}}let zj=Math.max(1,$0-1),Q0=I$(b$,q$,F,s,a.length,1),Y0=I$(q==="assigned"?j0:0,q==="assigned"?n$:0,T,o,e.length,2),Z0=I$(q==="pool"?J0:0,q==="pool"?P$:0,H,zj,i.length,1);F=Q0,T=Y0,H=Z0;let X0=d(a,Q0,s),qj=d(e,Y0,o),Mj=d(i,Z0,$0),z0=J.fg("borderAccent","│"),y$=[];for(let L=0;L<s;L++){let g=S(X0[L]??"",W),w=L<o?qj[L]:Mj[L-o],f=S(w,I);y$.push(`${g}${z0}${f}`)}let _$=o;if(_$>0&&_$<s){let L=S(X0[_$]??"",W),g=J.fg("borderAccent","─".repeat(I));y$[_$]=`${L}${z0}${g}`}return y$}function J$(E,O,b){let W=b?J.bold(J.fg("accent",E)):J.fg("muted",E),I=J.fg("borderAccent","─".repeat(Math.max(0,O-S$(E)-1)));return S(`${W}${I}`,O)}function P(E,O){let W=O?J.bold(J.fg("accent"," available pool ")):J.fg("muted"," available pool "),I="";if(A)I=`${J.fg("dim"," /")}${J.fg("accent",D)}${J.fg("accent","█")} `;else if(D)I=`${J.fg("dim"," /")}${J.fg("warning",D)} `;let s=S$(" available pool ")+S$(I),o=J.fg("borderAccent","─".repeat(Math.max(0,E-s-1)));return S(`${W}${I}${o}`,E)}function R$(E){if(A){if(k(E,"escape"))return D="",A=!1,U=0,!0;if(k(E,"enter")||k(E,"return"))return A=!1,!0;if(k(E,"backspace")||k(E,"delete"))return D=D.slice(0,-1),U=0,!0;if(E.length===1&&E>=" "&&E!=="")return D+=E,U=0,!0;return!0}if(k(E,"tab"))return j$(!1),!0;if(k(E,"shift+tab"))return j$(!0),!0;if(k(E,"/"))return q="pool",A=!0,!0;if(k(E,"enter")||k(E,"return")||k(E,"space"))return x().then(()=>!0);if(q==="providers"&&(k(E,"d")||k(E,"delete")||k(E,"backspace")))return p().then(()=>!0);if(k(E,"up")||k(E,"k"))return _(-1),!0;if(k(E,"down")||k(E,"j"))return _(1),!0;if(k(E,"pageUp")||k(E,"b"))return _(-8),!0;if(k(E,"pageDown")||k(E,"f"))return _(8),!0;if(k(E,"left")||k(E,"h"))return q="providers",!0;if(k(E,"right")||k(E,"l")){if(q==="providers")q="assigned";else if(q==="assigned")q="pool";return!0}return!1}function Zj(){if(A)return" type to filter · ↵ apply · esc clear ";return" tab ↔ panel · ↑↓ nav · ↵ move · / filter · d remove group "}return{id:"models",label:"Models",render:v,handleInput:R$,footerHint:Zj,rebuild:n}}import{matchesKey as y}from"@duckmind/dm-tui";var R={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",accent:"\x1B[38;5;111m",muted:"\x1B[38;5;245m",dim2:"\x1B[38;5;240m",green:"\x1B[38;5;114m",yellow:"\x1B[38;5;179m",red:"\x1B[38;5;167m",greenBg:"\x1B[48;5;22m",yellowBg:"\x1B[48;5;94m",redBg:"\x1B[48;5;52m"};function t0($,j={}){let J=[],Q=new Map,Y=0;for(let Z of $.accounts){if(!j.showDisabled&&Z.disabled){Y++;continue}let z=Q.get(Z.provider)??[];z.push(Z),Q.set(Z.provider,z)}if(J.push(`${R.dim}generated ${$.generatedAt}${R.reset}`),$.unsupportedProviders.length>0)J.push(`${R.dim}providers without quota lookup:${R.reset} ${$.unsupportedProviders.join(", ")}`);if(Y>0)J.push(`${R.dim}hidden disabled accounts:${R.reset} ${Y} ${R.dim2}(press d to show)${R.reset}`);for(let[Z,z]of Array.from(Q.entries()).sort()){J.push(""),J.push(`${R.bold}${R.accent}▌ ${Z}${R.reset}`);for(let X of z){if(J.push(B1(X)),!X.supported){J.push(` ${R.dim2}(provider not quota-aware)${R.reset}`);continue}if(X.error){if(j.verbose)for(let V of T1(X.error,88))J.push(` ${R.red}${V}${R.reset}`);continue}let q=X.groups??[];if(q.length===0){J.push(` ${R.dim2}(no active quota windows)${R.reset}`);continue}for(let V of q)J.push(K1(V))}}return J.push(""),J.push(`${R.dim}legend:${R.reset} ${a0} ok ${e0} error/unavailable ${$j} disabled ${R.green}✓N${R.reset}/${R.red}✗N${R.reset} request counters ${R.dim}v = verbose, d = show disabled${R.reset}`),J}function T1($,j){if($.length<=j)return[$];let J=[],Q=$;while(Q.length>j){let Y=Q.lastIndexOf(" ",j);if(Y<j*0.6)Y=j;J.push(Q.slice(0,Y)),Q=Q.slice(Y).replace(/^\s+/,"")}if(Q)J.push(Q);return J}function B1($){let j=L1($),J=`${R.green}✓${$.success}${R.reset} ${R.red}✗${$.failed}${R.reset}`,Q=Qj($.label,36);return` ${j} ${R.bold}${Q}${R.reset} ${J}`}var a0=`${R.green}●${R.reset}`,e0=`${R.yellow}⚠${R.reset}`,$j=`${R.dim2}⊘${R.reset}`;function L1($){if($.disabled)return $j;if($.unavailable||$.error||$.status&&$.status!=="active")return e0;return a0}function K1($){let j=Jj($.remainingFraction),J=Math.round(j*100),Q=F1(j),Y=$.resetTime?` ${R.dim}reset ${I1($.resetTime)}${R.reset}`:"",Z=Qj($.label,28).padEnd(28," ");return` ${Q} ${S1(J,j)} ${Z}${Y}`}function F1($){let j=Jj($),J=Math.round(j*18),Q=jj(j),Y=R.dim2,Z="█".repeat(J),z="░".repeat(18-J);return`${Y}[${Q}${Z}${Y}${z}]${R.reset}`}function S1($,j){return`${jj(j)}${String($).padStart(3)}%${R.reset}`}function jj($){if($>=0.4)return R.green;if($>=0.15)return R.yellow;return R.red}function I1($){let j=Date.parse($);if(!Number.isFinite(j))return $;let J=j-Date.now();if(J<=0)return"now";let Q=Math.round(J/60000);if(Q<60)return`${Q}m`;let Y=Math.round(Q/60);if(Y<48)return`${Y}h`;return`${Math.round(Y/24)}d`}function Jj($){return Math.max(0,Math.min(1,$))}function Qj($,j){if($.length<=j)return $;return`${$.slice(0,j-1)}…`}function Yj($){let{tui:j,theme:J,cfg:Q}=$,Y="idle",Z=null,z="",X=0,q=!1,V=!1,M=(D)=>{Y="loading",j.requestRender();let A=h(Q.proxy.usageKey);L0(Q,A,{force:D}).then((B)=>{Z=B,Y="ready",X=0,j.requestRender()}).catch((B)=>{z=B.message,Y="error",j.requestRender()})},U=()=>{if(Y==="idle"||Y==="loading")return[J.fg("dim"," loading usage…")];if(Y==="error")return[J.fg("error",` usage failed: ${z}`)];if(!Z)return[J.fg("dim"," (no data)")];return t0(Z,{showDisabled:q,verbose:V})};return{id:"usage",label:"Usage",render:(D,A)=>{let B=U();return X=x$(X,A,B.length),d(B,X,A).map((C)=>S(` ${C}`,D))},handleInput:(D)=>{if(y(D,"d"))return q=!q,X=0,!0;if(y(D,"v"))return V=!V,X=0,!0;let A=U().length;if(y(D,"up")||y(D,"k"))return X=Math.max(0,X-1),!0;if(y(D,"down")||y(D,"j"))return X=Math.min(Math.max(0,A-1),X+1),!0;if(y(D,"pageUp")||y(D,"b"))return X=Math.max(0,X-10),!0;if(y(D,"pageDown")||y(D,"f"))return X=Math.min(Math.max(0,A-1),X+10),!0;if(y(D,"home")||y(D,"g"))return X=0,!0;if(y(D,"end")||y(D,"shift+g"))return X=Math.max(0,A-1),!0;return!1},footerHint:()=>{let D=q?J.fg("success","[d disabled]"):"d disabled",A=V?J.fg("success","[v verbose]"):"v verbose";return` ↑↓ scroll · ${D} · ${A} `},onActivate:()=>{if(Y==="idle")M(!1)},reload:()=>{if(K$(),Y!=="idle")M(!0)}}}function kJ($){let{dm:j,ctx:J,tui:Q,theme:Y,cfg:Z,done:z}=$,X=$.discovery,q=!1,V="",M=d0({tui:Q,theme:Y,ctx:J,cfg:Z,getDiscovery:()=>X,onChange:()=>{q=!0,V=""}}),U=Yj({tui:Q,theme:Y,cfg:Z}),F=u0({theme:Y,cfg:Z,getDiscovery:()=>X}),T=[M,U,F],H=0,D=()=>T[H],A=(_)=>{if(_<0||_>=T.length||_===H)return;H=_,D().onActivate?.(),Q.requestRender()},B=()=>{V=Y.fg("dim","saving…"),B$(Z),Q.requestRender();let _=c$(j);l$(j,Z,X,_).then((x)=>{q=!1,V=Y.fg("success",`✓ settings saved · ${x.registered.length} registered, ${x.skipped.length} skipped`),Q.requestRender()}).catch((x)=>{V=Y.fg("error",`save failed: ${x.message}`),Q.requestRender()})},C=async()=>{V=Y.fg("dim","refreshing…"),Q.requestRender();try{let _=c$(j),x=h(Z.proxy.apiKey);X=await N0(Z,x),K$(),M.rebuild(),U.reload();let p=await l$(j,Z,X,_);V=Y.fg("success",`✓ refreshed · ${p.registered.length} providers (${X.source})`)}catch(_){V=Y.fg("error",`refresh failed: ${_.message}`)}Q.requestRender()},N=async()=>{if(!await S0(J,!0)){Q.requestRender();return}let x=T$();Z.proxy={...Z.proxy,...x.proxy},await C()},G=()=>{let _=Z.proxy.endpoint||"(unset)",x=_.length>40?`…${_.slice(-39)}`:_,p=Boolean(h(Z.proxy.apiKey)),v=0,c=0;for(let P of Object.values(Z.builtinProviders)){if(P.enabled&&P.models.length>0)v++;c+=P.models.length}for(let P of Object.values(Z.customProviders)){if(P.models.length>0)v++;c+=P.models.length}let J$=V?V:q?Y.fg("warning","● unsaved — press s to save"):Y.fg("dim","✓ saved");return[`${Y.fg("dim","endpoint")} ${x}`,`${Y.fg("dim","key")} ${p?Y.fg("success","✓"):Y.fg("error","✗")}`,`${Y.fg("dim","providers")} ${v}`,`${Y.fg("dim","models")} ${c}`,J$]},n=(_)=>{let x=Q.rows??40,v=Math.max(16,Math.min(x-6,38))-2,c=Math.max(72,_-2),J$=Math.max(6,v-3),P=[];return P.push(b0(Y,G(),c)),P.push(x0(Y,T.map((R$)=>({id:R$.id,label:R$.label})),H,c)),P.push(n0(Y,c)),P.push(...D().render(c,J$)),r(Y,{width:_,title:" /cliproxy ",lines:P,footer:{hint:D().footerHint(),badge:" [ ] tab · r refresh · e setup · s save · q close "}})},j$=(_)=>{if(x1().matches(_,"tui.select.cancel")||u(_,"q")||u(_,"escape"))return z(),!0;if(u(_,"]")||u(_,"tab"))return A((H+1)%T.length),!0;if(u(_,"[")||u(_,"shift+tab"))return A((H-1+T.length)%T.length),!0;if(u(_,"1"))return A(0),!0;if(u(_,"2"))return A(1),!0;if(u(_,"3"))return A(2),!0;if(u(_,"r"))return C().then(()=>!0);if(u(_,"e"))return N().then(()=>!0);if(u(_,"s"))return B(),!0;return!1};return{render:n,invalidate(){},handleInput(_){let x=D().handleInput(_);if(x instanceof Promise){V="",x.then((v)=>{if(v)Q.requestRender()});return}if(x){V="",Q.requestRender();return}let p=j$(_);if(p instanceof Promise)p.then((v)=>{if(v)Q.requestRender()});else if(p)Q.requestRender()}}}export{kJ as buildHub};
|
|
1
|
+
import {
|
|
2
|
+
getKeybindings,
|
|
3
|
+
matchesKey
|
|
4
|
+
} from "@duckmind/dm-tui";
|
|
5
|
+
import { applyAll, beginProviderRefresh } from "../apply.js";
|
|
6
|
+
import { loadConfig, resolveConfigValue, saveConfig } from "../config.js";
|
|
7
|
+
import { fetchDiscovery } from "../fetch-models.js";
|
|
8
|
+
import { clearUsageCache } from "../fetch-usage.js";
|
|
9
|
+
import { frame } from "../ui-frame.js";
|
|
10
|
+
import { runSetup } from "../ui-setup.js";
|
|
11
|
+
import { ruleLine, statusHeader, tabBar } from "./shell.js";
|
|
12
|
+
import { buildDiagnosticsView } from "./view-diagnostics.js";
|
|
13
|
+
import { buildModelsView } from "./view-models.js";
|
|
14
|
+
import { buildUsageView } from "./view-usage.js";
|
|
15
|
+
export function buildHub(deps) {
|
|
16
|
+
const { dm, ctx, tui, theme, cfg, done } = deps;
|
|
17
|
+
let discovery = deps.discovery;
|
|
18
|
+
let dirty = false;
|
|
19
|
+
let flash = "";
|
|
20
|
+
const models = buildModelsView({
|
|
21
|
+
tui,
|
|
22
|
+
theme,
|
|
23
|
+
ctx,
|
|
24
|
+
cfg,
|
|
25
|
+
getDiscovery: () => discovery,
|
|
26
|
+
onChange: () => {
|
|
27
|
+
dirty = true;
|
|
28
|
+
flash = "";
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const usage = buildUsageView({ tui, theme, cfg });
|
|
32
|
+
const diagnostics = buildDiagnosticsView({
|
|
33
|
+
theme,
|
|
34
|
+
cfg,
|
|
35
|
+
getDiscovery: () => discovery
|
|
36
|
+
});
|
|
37
|
+
const views = [models, usage, diagnostics];
|
|
38
|
+
let activeIdx = 0;
|
|
39
|
+
const active = () => views[activeIdx];
|
|
40
|
+
const switchTab = (idx) => {
|
|
41
|
+
if (idx < 0 || idx >= views.length || idx === activeIdx)
|
|
42
|
+
return;
|
|
43
|
+
activeIdx = idx;
|
|
44
|
+
active().onActivate?.();
|
|
45
|
+
tui.requestRender();
|
|
46
|
+
};
|
|
47
|
+
const save = () => {
|
|
48
|
+
flash = theme.fg("dim", "saving…");
|
|
49
|
+
saveConfig(cfg);
|
|
50
|
+
tui.requestRender();
|
|
51
|
+
const refreshGeneration = beginProviderRefresh(dm);
|
|
52
|
+
applyAll(dm, cfg, discovery, refreshGeneration).then((rep) => {
|
|
53
|
+
dirty = false;
|
|
54
|
+
flash = theme.fg("success", `✓ settings saved · ${rep.registered.length} registered, ${rep.skipped.length} skipped`);
|
|
55
|
+
tui.requestRender();
|
|
56
|
+
}).catch((e) => {
|
|
57
|
+
flash = theme.fg("error", `save failed: ${e.message}`);
|
|
58
|
+
tui.requestRender();
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const refresh = async () => {
|
|
62
|
+
flash = theme.fg("dim", "refreshing…");
|
|
63
|
+
tui.requestRender();
|
|
64
|
+
try {
|
|
65
|
+
const refreshGeneration = beginProviderRefresh(dm);
|
|
66
|
+
const key = resolveConfigValue(cfg.proxy.apiKey);
|
|
67
|
+
discovery = await fetchDiscovery(cfg, key);
|
|
68
|
+
clearUsageCache();
|
|
69
|
+
models.rebuild();
|
|
70
|
+
usage.reload();
|
|
71
|
+
const rep = await applyAll(dm, cfg, discovery, refreshGeneration);
|
|
72
|
+
flash = theme.fg("success", `✓ refreshed · ${rep.registered.length} providers (${discovery.source})`);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
flash = theme.fg("error", `refresh failed: ${e.message}`);
|
|
75
|
+
}
|
|
76
|
+
tui.requestRender();
|
|
77
|
+
};
|
|
78
|
+
const setup = async () => {
|
|
79
|
+
const ok = await runSetup(ctx, true);
|
|
80
|
+
if (!ok) {
|
|
81
|
+
tui.requestRender();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const reloaded = loadConfig();
|
|
85
|
+
cfg.proxy = { ...cfg.proxy, ...reloaded.proxy };
|
|
86
|
+
await refresh();
|
|
87
|
+
};
|
|
88
|
+
const statusParts = () => {
|
|
89
|
+
const ep = cfg.proxy.endpoint || "(unset)";
|
|
90
|
+
const epShort = ep.length > 40 ? `…${ep.slice(-39)}` : ep;
|
|
91
|
+
const keyOk = Boolean(resolveConfigValue(cfg.proxy.apiKey));
|
|
92
|
+
let provCount = 0;
|
|
93
|
+
let modelCount = 0;
|
|
94
|
+
for (const p of Object.values(cfg.builtinProviders)) {
|
|
95
|
+
if (p.enabled && p.models.length > 0)
|
|
96
|
+
provCount++;
|
|
97
|
+
modelCount += p.models.length;
|
|
98
|
+
}
|
|
99
|
+
for (const p of Object.values(cfg.customProviders)) {
|
|
100
|
+
if (p.models.length > 0)
|
|
101
|
+
provCount++;
|
|
102
|
+
modelCount += p.models.length;
|
|
103
|
+
}
|
|
104
|
+
const state = flash ? flash : dirty ? theme.fg("warning", "● unsaved — press s to save") : theme.fg("dim", "✓ saved");
|
|
105
|
+
return [
|
|
106
|
+
`${theme.fg("dim", "endpoint")} ${epShort}`,
|
|
107
|
+
`${theme.fg("dim", "key")} ${keyOk ? theme.fg("success", "✓") : theme.fg("error", "✗")}`,
|
|
108
|
+
`${theme.fg("dim", "providers")} ${provCount}`,
|
|
109
|
+
`${theme.fg("dim", "models")} ${modelCount}`,
|
|
110
|
+
state
|
|
111
|
+
];
|
|
112
|
+
};
|
|
113
|
+
const render = (width) => {
|
|
114
|
+
const totalRows = tui.rows ?? 40;
|
|
115
|
+
const frameTotal = Math.max(16, Math.min(totalRows - 6, 38));
|
|
116
|
+
const bodyRows = frameTotal - 2;
|
|
117
|
+
const inner = Math.max(72, width - 2);
|
|
118
|
+
const viewBodyH = Math.max(6, bodyRows - 3);
|
|
119
|
+
const lines = [];
|
|
120
|
+
lines.push(statusHeader(theme, statusParts(), inner));
|
|
121
|
+
lines.push(tabBar(theme, views.map((v) => ({ id: v.id, label: v.label })), activeIdx, inner));
|
|
122
|
+
lines.push(ruleLine(theme, inner));
|
|
123
|
+
lines.push(...active().render(inner, viewBodyH));
|
|
124
|
+
return frame(theme, {
|
|
125
|
+
width,
|
|
126
|
+
title: " /cliproxy ",
|
|
127
|
+
lines,
|
|
128
|
+
footer: {
|
|
129
|
+
hint: active().footerHint(),
|
|
130
|
+
badge: " [ ] tab · r refresh · e setup · s save · q close "
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
const globalInput = (data) => {
|
|
135
|
+
const kb = getKeybindings();
|
|
136
|
+
if (kb.matches(data, "tui.select.cancel") || matchesKey(data, "q") || matchesKey(data, "escape")) {
|
|
137
|
+
done();
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
if (matchesKey(data, "]") || matchesKey(data, "tab")) {
|
|
141
|
+
switchTab((activeIdx + 1) % views.length);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
if (matchesKey(data, "[") || matchesKey(data, "shift+tab")) {
|
|
145
|
+
switchTab((activeIdx - 1 + views.length) % views.length);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
if (matchesKey(data, "1")) {
|
|
149
|
+
switchTab(0);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
if (matchesKey(data, "2")) {
|
|
153
|
+
switchTab(1);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
if (matchesKey(data, "3")) {
|
|
157
|
+
switchTab(2);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
if (matchesKey(data, "r"))
|
|
161
|
+
return refresh().then(() => true);
|
|
162
|
+
if (matchesKey(data, "e"))
|
|
163
|
+
return setup().then(() => true);
|
|
164
|
+
if (matchesKey(data, "s")) {
|
|
165
|
+
save();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
render,
|
|
172
|
+
invalidate() {},
|
|
173
|
+
handleInput(data) {
|
|
174
|
+
const handled = active().handleInput(data);
|
|
175
|
+
if (handled instanceof Promise) {
|
|
176
|
+
flash = "";
|
|
177
|
+
handled.then((h) => {
|
|
178
|
+
if (h)
|
|
179
|
+
tui.requestRender();
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (handled) {
|
|
184
|
+
flash = "";
|
|
185
|
+
tui.requestRender();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const g = globalInput(data);
|
|
189
|
+
if (g instanceof Promise) {
|
|
190
|
+
g.then((h) => {
|
|
191
|
+
if (h)
|
|
192
|
+
tui.requestRender();
|
|
193
|
+
});
|
|
194
|
+
} else if (g) {
|
|
195
|
+
tui.requestRender();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -1,2 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
`,{encoding:"utf8"}),kj(Y),R0(Y),Y=void 0,Kj(J,j),X$(j)}finally{if(Y!==void 0)R0(Y);g$(J,{force:!0})}}function B$($){Pj($),K.info("config saved to",l)}function yj($){if(!$||typeof $!=="object")return structuredClone(W$);let j=$,J=structuredClone(W$),Y=j.proxy??{};if(J.proxy.endpoint=typeof Y.endpoint==="string"?Y.endpoint:J.proxy.endpoint,J.proxy.apiKey=typeof Y.apiKey==="string"?Y.apiKey:"",typeof Y.usageKey==="string")J.proxy.usageKey=Y.usageKey;if(typeof Y.providerPrefix==="string"&&Y.providerPrefix.trim())J.proxy.providerPrefix=Y.providerPrefix.trim();if(j.builtinProviders&&typeof j.builtinProviders==="object")J.builtinProviders=j.builtinProviders;if(j.customProviders&&typeof j.customProviders==="object")J.customProviders=j.customProviders;if(Array.isArray(j.discoveryExcludes))J.discoveryExcludes=j.discoveryExcludes.filter((Z)=>typeof Z==="string");if(j.overrides&&typeof j.overrides==="object")J.overrides=j.overrides;if(typeof j.refreshIntervalMinutes==="number")J.refreshIntervalMinutes=j.refreshIntervalMinutes;if(typeof j.usageCacheTtlMs==="number")J.usageCacheTtlMs=j.usageCacheTtlMs;return J}function h($){if(!$)return"";let j=$.trim();if(j.startsWith("!"))try{return Gj(j.slice(1),{encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()}catch(J){let Y=J?.status,Z=typeof Y==="number"?` (exit ${Y})`:"";return K.warn(`failed to resolve "!" config value${Z}`),""}if(j.startsWith("$"))return process.env[j.slice(1)]??"";return j}import{createHash as Cj}from"node:crypto";import{existsSync as m1,readFileSync as s1,writeFileSync as wj}from"node:fs";import{dirname as uj,join as vj}from"node:path";var gj=vj(h$,"discovery-cache.json");function O0($,j){return Cj("sha256").update($.trim().replace(/\/+$/,"")).update("\x00").update(j).digest("hex")}function p$($,j,J=gj){try{E$(uj(J));let Y={savedAt:Date.now(),scope:j,discovery:$};wj(J,JSON.stringify(Y),{encoding:"utf8",mode:384}),X$(J)}catch(Y){K.warn("failed to write discovery cache:",Y.message)}}var Q$="dm-cliproxy/0.3.5",fj=5000;async function N0($,j){let J=new AbortController,Y=setTimeout(()=>J.abort(Error("timeout")),fj);try{return await fetch($,{...j,signal:J.signal})}finally{clearTimeout(Y)}}function hj($){return new URL("/.well-known/pi",new URL($).origin).toString()}async function pj($){let j=hj($.proxy.endpoint),J;try{J=await N0(j,{headers:{"User-Agent":Q$,Accept:"application/json"}})}catch(Q){return K.debug("well-known fetch failed:",Q.message,"— falling back to /v1/models"),null}if(!J.ok)return K.debug(`well-known returned ${J.status} — falling back to /v1/models`),null;let Y;try{Y=await J.json()}catch{return K.debug("well-known returned non-JSON — falling back to /v1/models"),null}if(!Y||Y.schemaVersion!==1)return K.debug("well-known schemaVersion != 1 — falling back to /v1/models"),null;let Z=[],X=Y.builtinProviders??{};for(let[Q,q]of Object.entries(X)){if(!q||!Array.isArray(q.models))continue;let V=q.models.map((M)=>({id:String(M.id),name:typeof M.name==="string"?M.name:String(M.id),reasoning:Boolean(M.reasoning??G$(String(M.id))),contextWindow:typeof M.contextWindow==="number"?M.contextWindow:200000,maxTokens:typeof M.maxTokens==="number"?M.maxTokens:16000,cost:M.cost??{input:0,output:0,cacheRead:0,cacheWrite:0}}));Z.push({name:Q,api:q.api??"openai-responses",models:V})}let z=(Array.isArray(Y.customModelPool)?Y.customModelPool:[]).map((Q)=>({id:String(Q.id),name:typeof Q.name==="string"?Q.name:String(Q.id),reasoning:Boolean(Q.reasoning??G$(String(Q.id))),contextWindow:typeof Q.contextWindow==="number"?Q.contextWindow:128000,maxTokens:typeof Q.maxTokens==="number"?Q.maxTokens:16000,cost:Q.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},api:Q.api??"openai-completions",suggestedProvider:E0(typeof Q.suggestedProviderName==="string"?Q.suggestedProviderName:"misc",$.proxy.providerPrefix),ownedBy:typeof Q.owned_by==="string"?Q.owned_by:""}));return{source:"well-known",upstreamVersion:typeof Y?.upstream?.upstreamVersion==="string"?Y.upstream.upstreamVersion:null,builtinProviders:Z,customPool:z,serverDiscoveryExcludes:Array.isArray(Y.discoveryExcludes)?Y.discoveryExcludes.filter((Q)=>typeof Q==="string"):[],upstreamTotal:typeof Y?.counts?.upstreamTotal==="number"?Y.counts.upstreamTotal:0}}async function cj($,j){let J=new URL("/v1/models",new URL($.proxy.endpoint).origin).toString(),Y=await N0(J,{headers:{Authorization:`Bearer ${j}`,Accept:"application/json","User-Agent":Q$}});if(!Y.ok)throw Error(`/v1/models returned ${Y.status}`);let Z=await Y.json();if(!Z?.data||!Array.isArray(Z.data))return[];return Z.data.map((X)=>({id:typeof X.id==="string"?X.id:"",owned_by:typeof X.owned_by==="string"?X.owned_by:""})).filter((X)=>X.id)}function lj($,j){let J=j.discoveryExcludes,Y=new Map,Z=[],X=0;for(let z of $){if(X++,U0(z.id,J))continue;if(z.owned_by==="openai"){let M=Z$(z.id);H0(Y,"openai","openai-responses",G0(M));continue}if(z.owned_by==="anthropic"){let M=Z$(z.id);H0(Y,"anthropic","anthropic-messages",G0(M));continue}let{slug:Q,api:q}=D0(z.owned_by,j.proxy.providerPrefix),V=Z$(z.id);Z.push({id:z.id,name:V.name??z.id,reasoning:V.reasoning??!1,contextWindow:V.contextWindow??128000,maxTokens:V.maxTokens??16000,cost:V.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},api:q,suggestedProvider:Q,ownedBy:z.owned_by})}return{source:"v1-models",upstreamVersion:null,builtinProviders:Array.from(Y.values()),customPool:Z,serverDiscoveryExcludes:[],upstreamTotal:X}}function H0($,j,J,Y){let Z=$.get(j);if(!Z)Z={name:j,api:J,models:[]},$.set(j,Z);Z.models.push(Y)}function G0($){return{id:$.id,name:$.name??$.id,reasoning:$.reasoning??!1,contextWindow:$.contextWindow??128000,maxTokens:$.maxTokens??16000,cost:$.cost??{input:0,output:0,cacheRead:0,cacheWrite:0}}}async function W0($,j){let J=O0($.proxy.endpoint,j),Y=await pj($);if(Y)return K.debug(`discovery via /.well-known/pi: ${Y.builtinProviders.length} builtin, ${Y.customPool.length} custom`),p$(Y,J),Y;if(!j)throw Error("well-known unavailable AND proxy apiKey is empty — cannot fall back to /v1/models. Set proxy.apiKey in config.");let Z=await cj($,j),X=lj(Z,$);return K.debug(`discovery via /v1/models: ${X.builtinProviders.length} builtin, ${X.customPool.length} custom`),p$(X,J),X}function k0($){let j=new Set;for(let J of $.builtinProviders)for(let Y of J.models)j.add(Y.id);for(let J of $.customPool)j.add(J.id);return j}var T0=new WeakMap,c$=new WeakMap;function l$($){let j=(c$.get($)??0)+1;return c$.set($,j),j}function L$($,j){return j===void 0||j===(c$.get($)??0)}function B0($,j,J){let Y=T0.get($)??new Set;for(let Z of Y){if(j.has(Z))continue;$.unregisterProvider(Z),J.unregistered.push(Z)}T0.set($,new Set(j))}async function m$($,j,J,Y){let Z={registered:[],skipped:[],unregistered:[]};if(!L$($,Y))return Z.skipped.push({provider:"*",reason:"stale refresh generation"}),Z;let X=new Set,z=k0(J),Q=h(j.proxy.apiKey);if(!Q)return K.warn("proxy apiKey is empty — dm.registerProvider calls will be skipped"),B0($,X,Z),Z;let q=new Map;for(let M of J.builtinProviders)q.set(M.name,M.models.map((U)=>({id:U.id,name:U.name,reasoning:U.reasoning,contextWindow:U.contextWindow,maxTokens:U.maxTokens,cost:U.cost})));for(let[M,U]of Object.entries(j.builtinProviders)){if(!L$($,Y))return Z;if(!U?.enabled){Z.skipped.push({provider:M,reason:"disabled"});continue}if(!Array.isArray(U.models)||U.models.length===0){Z.skipped.push({provider:M,reason:"empty whitelist"});continue}let F=[];try{F=mj(M)}catch{}let T=new Map(F.map((N)=>[N.id,N])),O=new Map((q.get(M)??[]).map((N)=>[N.id,N])),D=[];for(let N of U.models){if(!z.has(N))continue;let G=T.get(N),n=O.get(N);if(G){D.push({id:G.id,name:G.name,reasoning:G.reasoning,contextWindow:G.contextWindow,maxTokens:G.maxTokens,cost:G.cost,input:G.input,api:G.api,thinkingLevelMap:G.thinkingLevelMap});continue}if(!n)continue;let j$=M==="anthropic"?"anthropic-messages":U.apiOverride??"openai-responses";D.push({id:n.id,name:n.name,reasoning:n.reasoning,contextWindow:n.contextWindow,maxTokens:n.maxTokens,cost:n.cost,input:["text"],api:j$})}if(D.length===0){Z.skipped.push({provider:M,reason:"no whitelisted models present on proxy"});continue}let A=U.apiOverride??D[0].api;if(!u$.has(A)){Z.skipped.push({provider:M,reason:`api "${A}" not in allowlist`});continue}let B=D.map((N)=>{let G=j.overrides[N.id]??{},n={id:N.id,name:typeof G.name==="string"?G.name:N.name,api:A,reasoning:typeof G.reasoning==="boolean"?G.reasoning:N.reasoning,input:N.input,cost:G.cost??N.cost,contextWindow:typeof G.contextWindow==="number"?G.contextWindow:N.contextWindow,maxTokens:typeof G.maxTokens==="number"?G.maxTokens:N.maxTokens};if(N.thinkingLevelMap)n.thinkingLevelMap=N.thinkingLevelMap;return n}),C={name:M,baseUrl:v$(A,j.proxy.endpoint),apiKey:Q,authHeader:!0,api:A,models:B};$.registerProvider(M,C),X.add(M),Z.registered.push({provider:M,modelCount:B.length,api:A})}let V=new Map(J.customPool.map((M)=>[M.id,M]));for(let[M,U]of Object.entries(j.customProviders)){if(!L$($,Y))return Z;if(!u$.has(U.api)){Z.skipped.push({provider:M,reason:`api "${U.api}" not in allowlist`});continue}let F=U.models.filter((D)=>z.has(D.id));if(F.length===0){Z.skipped.push({provider:M,reason:"no configured models present on proxy"});continue}let T=F.map((D)=>{let A=V.get(D.id),B=Z$(D.id),C=j.overrides[D.id]??{};return{id:D.id,name:D.name??A?.name??B.name??D.id,api:U.api,reasoning:sj(D.reasoning,A?.reasoning,B.reasoning,C.reasoning)??!1,input:["text"],cost:D.cost??A?.cost??B.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:L0(D.contextWindow,A?.contextWindow,B.contextWindow,C.contextWindow)??128000,maxTokens:L0(D.maxTokens,A?.maxTokens,B.maxTokens,C.maxTokens)??16000}}),O={name:M,baseUrl:v$(U.api,j.proxy.endpoint),apiKey:Q,authHeader:!0,api:U.api,models:T};$.registerProvider(M,O),X.add(M),Z.registered.push({provider:M,modelCount:T.length,api:U.api})}if(L$($,Y))B0($,X,Z);return K.debug(`applyAll: registered ${Z.registered.length} providers, skipped ${Z.skipped.length}, unregistered ${Z.unregistered.length}`),Z}function L0(...$){for(let j of $)if(typeof j==="number")return j;return}function sj(...$){for(let j of $)if(typeof j==="boolean")return j;return}var oj=15000,D$=null;function K$(){D$=null}async function K0($,j,J={}){if(!J.force&&D$&&Date.now()-D$.fetchedAt<$.usageCacheTtlMs)return D$.doc;if(!j)throw Error("usage key not configured; set proxy.usageKey in config and rerun");let Y=new URL("/api/usage",new URL($.proxy.endpoint).origin).toString(),Z=new AbortController,X=setTimeout(()=>Z.abort(Error("timeout")),oj),z;try{z=await fetch(Y,{headers:{"X-Plugin-Key":j,Accept:"application/json","User-Agent":Q$},signal:Z.signal})}finally{clearTimeout(X)}if(!z.ok)throw Error(`/api/usage returned ${z.status}`);let Q=await z.json();if(!Q||Q.schemaVersion!==1||!Array.isArray(Q.accounts))throw Error("/api/usage returned unexpected payload shape");return D$={fetchedAt:Date.now(),doc:Q},K.debug("usage fetched, accounts:",Q.accounts.length),Q}import{visibleWidth as o$}from"@duckmind/dm-tui";import{visibleWidth as F0}from"@duckmind/dm-tui";function S($,j){let J=F0($);if(J===j)return $;if(J<j)return $+" ".repeat(j-J);return s$($,j)}function s$($,j){if(j<=0)return"\x1B[0m";let J="",Y=0,Z=0;while(Z<$.length){let X=$[Z];if(X==="\x1B"&&$[Z+1]==="["){let Q=$.indexOf("m",Z+2);if(Q>=0){J+=$.slice(Z,Q+1),Z=Q+1;continue}}let z=F0(X);if(Y+z>j)break;J+=X,Y+=z,Z++}return`${J}\x1B[0m`}var ij="│",rj="╭",dj="╮",tj="╰",aj="╯",R$="─";function r($,j){let J=Math.max(4,j.width);return[ej($,j.title,j.titleColor??"accent",J),...j.lines.map((Y)=>$1($,Y,J)),j1($,j.footer,J)]}function ej($,j,J,Y){let Z=j??"",X=o$(Z),z=Math.max(0,Y-3-X),Q=Z?$.bold($.fg(J,Z)):"",q=`${rj}${R$}${Q}${R$.repeat(z)}${dj}`;return $.fg("borderAccent",q)}function $1($,j,J){let Y=Math.max(0,J-2),Z=$.fg("borderAccent",ij);return`${Z}${S(j,Y)}${Z}`}function j1($,j,J){let Y=`${tj}${R$}`,Z=`${aj}`,X=3;if(!j||!j.hint&&!j.badge){let O=Math.max(0,J-3);return $.fg("borderAccent",`${Y}${R$.repeat(O)}${Z}`)}let z=j.hint??"",Q=j.badge??"",q=o$(z),V=o$(Q),M=Math.max(0,J-3-q-V),U=$.fg("dim",z),F=Q?$.fg("muted",Q):"",T=R$.repeat(M);return`${$.fg("borderAccent",Y)}${U}${$.fg("borderAccent",T)}${F}${$.fg("borderAccent",Z)}`}function F$($){return Math.max(0,$-2)}import{existsSync as S0}from"node:fs";import{homedir as J1}from"node:os";import{getKeybindings as Y1,Input as i$,matchesKey as Z1}from"@duckmind/dm-tui";var X1=[{label:"endpoint",hint:"OpenAI-style base URL of your CliProxyAPI deployment, must include /v1",required:!0,validate:($)=>{try{if(!new URL($).pathname.endsWith("/v1"))return"must end with /v1";return null}catch{return"not a valid URL"}}},{label:"apiKey",hint:"CliProxyAPI bearer key. Input is masked; blank keeps an existing key",required:!0,secret:!0},{label:"providerPrefix",hint:"Prefix for your custom provider names. Suggested groups become <prefix>-glm, <prefix>-gemini, etc. Use any short slug (letters/digits/dashes).",required:!0,validate:($)=>/^[a-z0-9][a-z0-9-]*$/i.test($)?null:"letters/digits/dashes only"},{label:"usageKey",hint:"Optional X-Plugin-Key for /api/usage. Input is masked; blank keeps it, '-' clears it",required:!1,secret:!0}];async function I0($,j=!1){let J=T$(),Y={endpoint:J.proxy.endpoint??"",apiKey:J.proxy.apiKey??"",providerPrefix:J.proxy.providerPrefix??"",usageKey:J.proxy.usageKey??""},Z=!1;for(let z of X1){let Q=Y[z.label]??"",q=z.secret?"":j?Y[z.label]??z.initialValue??"":Y[z.label]??"";if(!j&&q)continue;let V=z.secret&&Q?{...z,required:!1}:z,M=await q1($,V,q||z.initialValue||"");if(M===void 0){Z=!0;break}let U=z1(z,Q,M);if(U===null){if(z.required){$.ui.notify(`${z.label} is required — aborted`,"warning"),Z=!0;break}}Y[z.label]=U??""}if(Z)return!1;let X={...J};if(X.proxy={...J.proxy,endpoint:Y.endpoint??"",apiKey:Y.apiKey??"",providerPrefix:Y.providerPrefix??""},Y.usageKey)X.proxy.usageKey=Y.usageKey;else delete X.proxy.usageKey;return B$(X),$.ui.notify(`saved to ${l}`,"info"),!0}function Q1($){if($.startsWith("!")||$.startsWith("$"))return $;if($.startsWith("~/")){let j=$.replace(/^~/,J1());return S0(j)?`!cat ${$}`:$}if($.startsWith("/"))return S0($)?`!cat ${$}`:$;return $}function z1($,j,J){let Y=J.trim();if(!Y){if($.secret&&j)return j;return $.required?null:""}if($.secret&&!$.required&&Y==="-")return"";return $.label==="providerPrefix"||$.label==="endpoint"?Y:Q1(Y)}async function q1($,j,J){return $.ui.custom((Y,Z,X,z)=>M1(Y,Z,j,J,z),{overlay:!0,overlayOptions:{width:100,maxHeight:"60%"}})}class b0{rawInput=new i$;displayInput=new i$;pasteActive=!1;get focused(){return this.rawInput.focused}set focused($){this.rawInput.focused=$,this.displayInput.focused=$}set onSubmit($){this.rawInput.onSubmit=$}set onEscape($){this.rawInput.onEscape=$}getValue(){return this.rawInput.getValue()}setValue($){this.rawInput.setValue($),this.displayInput.setValue("•".repeat($.length))}handleInput($){this.rawInput.handleInput($),this.displayInput.handleInput(this.maskInput($)),this.displayInput.setValue("•".repeat(this.rawInput.getValue().length))}render($){return this.displayInput.render($)}invalidate(){this.rawInput.invalidate(),this.displayInput.invalidate()}maskInput($){let j="";for(let J=0;J<$.length;){if($.startsWith("\x1B[200~",J)){this.pasteActive=!0,j+="\x1B[200~",J+=6;continue}if($.startsWith("\x1B[201~",J)){this.pasteActive=!1,j+="\x1B[201~",J+=6;continue}if(!this.pasteActive&&$.charCodeAt(J)===27)return $;let Y=$.codePointAt(J),Z=String.fromCodePoint(Y);j+=Y<32||Y===127?Z:"•",J+=Z.length}return j}}function M1($,j,J,Y,Z){let X=J.secret?new b0:new i$;X.setValue(Y),X.focused=!0;let z=null,Q=(q)=>{if(!q&&!J.required){Z("");return}if(!q&&J.required){z="required field",$.requestRender();return}if(J.validate){let V=q.trim();if(V&&!V.startsWith("!")&&!V.startsWith("$")){let M=J.validate(V);if(M){z=M,$.requestRender();return}}}Z(q)};return X.onSubmit=Q,X.onEscape=()=>Z(void 0),{render(q){let V=F$(q),M=X.render(V-4),U=["",` ${j.fg("dim",J.hint)}`,"",...M.map((F)=>` ${j.fg("accent",`❯ ${F}`)}`),"",z?` ${j.fg("error",`! ${z}`)}`:` ${j.fg("dim","enter = save · esc = cancel")}`];return r(j,{width:q,title:` setup: ${J.label} `,lines:U,footer:{hint:" enter = save · esc = cancel "}})},invalidate(){X.invalidate()},handleInput(q){if(Y1().matches(q,"tui.select.cancel")||Z1(q,"escape")){Z(void 0);return}z=null,X.handleInput(q),$.requestRender()}}}import{visibleWidth as S$}from"@duckmind/dm-tui";function x0($,j,J,Y){let Z=$.fg("borderAccent","│"),X=j.map((z,Q)=>{let q=`${Q+1} ${z.label}`;return Q===J?$.bold($.fg("accent",` ${q} `)):$.fg("dim",` ${q} `)});return S(` ${X.join(Z)}`,Y)}function n0($,j,J){let Y=j.join($.fg("dim"," "));return S(` ${Y}`,J)}function P0($,j){return $.fg("borderAccent","─".repeat(Math.max(0,j)))}function d($,j,J){let Y=$.slice(j,j+J);while(Y.length<J)Y.push("");return Y}function I$($,j,J,Y,Z,X){if(Z<=Y)return 0;let z=Math.max(0,Z-Y);if(j<X)return 0;let Q=Math.max(0,Math.min(J,z)),q=Q+X;if($<q)Q=Math.max(0,$-X);let V=Q+Y-1;if(j>V)Q=Math.min(z,j-(Y-1));return Q}function b$($,j,J){let Y=Math.max(0,J-j);return Math.max(0,Math.min($,Y))}import{matchesKey as t}from"@duckmind/dm-tui";import{existsSync as V1,readFileSync as U1}from"node:fs";import{homedir as C0}from"node:os";import{join as w0}from"node:path";var E1=w0(C0(),".dm","models.json"),D1=w0(C0(),".dm","auth.json");function y0($){if(!V1($))return null;try{return JSON.parse(U1($,"utf8"))}catch{return null}}function u0($){let j=[],J=new Set([...Object.keys($.builtinProviders),...Object.keys($.customProviders)]),Y=y0(E1);if(Y&&typeof Y==="object"){for(let X of Object.keys(Y))if(J.has(X))j.push({kind:"models.json",provider:X,detail:`provider "${X}" has user overrides in ~/.dm/models.json which DM merges on top of our registration`})}let Z=y0(D1);if(Z&&typeof Z==="object"){for(let X of Object.keys(Z))if(J.has(X))j.push({kind:"auth.json",provider:X,detail:`provider "${X}" has user-set credentials in ~/.dm/auth.json which take priority over our apiKey`})}return j}function v0($){let{theme:j,cfg:J}=$,Y=0,Z=()=>{let Q=$.getDiscovery(),q=(O)=>j.fg("success",O),V=(O)=>j.fg("error",O),M=(O)=>j.fg("dim",O),U=[];U.push(`${M("endpoint")} ${J.proxy.endpoint||V("(unset)")}`),U.push(`${M("apiKey")} ${h(J.proxy.apiKey)?q("resolves"):V("empty after resolution")}`),U.push(`${M("usageKey")} ${J.proxy.usageKey?h(J.proxy.usageKey)?q("resolves"):V("set but empty"):M("not configured")}`),U.push(`${M("user-agent")} ${Q$}`),U.push(""),U.push(`${M("discovery")} source=${Q.source}`),U.push(`${M("upstream")} v=${Q.upstreamVersion??"(unknown)"} · ${Q.upstreamTotal} ids`);let F=Q.builtinProviders.map((O)=>`${O.name}=${O.models.length}`).join(", ")||M("(none)");U.push(`${M("built-in")} ${F}`),U.push(`${M("custom pool")} ${Q.customPool.length} models`);let T=u0(J);if(U.push(""),T.length===0)U.push(`${M("conflicts")} ${q("none")}`);else{U.push(`${M("conflicts")}`);for(let O of T)U.push(` ${j.fg("warning",`[${O.kind}]`)} ${O.detail}`)}return U};return{id:"diagnostics",label:"Diagnostics",render:(Q,q)=>{let V=Z();return Y=b$(Y,q,V.length),d(V,Y,q).map((M)=>S(` ${M}`,Q))},handleInput:(Q)=>{let q=Z().length;if(t(Q,"up")||t(Q,"k"))return Y=Math.max(0,Y-1),!0;if(t(Q,"down")||t(Q,"j"))return Y=Math.min(Math.max(0,q-1),Y+1),!0;if(t(Q,"pageUp")||t(Q,"b"))return Y=Math.max(0,Y-10),!0;if(t(Q,"pageDown")||t(Q,"f"))return Y=Math.min(Math.max(0,q-1),Y+10),!0;return!1},footerHint:()=>" ↑↓ scroll · r refresh discovery "}}import{matchesKey as k}from"@duckmind/dm-tui";function r$($){let j=new Map,J=new Map;for(let Z of $.builtinProviders){let X=[];for(let z of Z.models)j.set(z.id,{id:z.id,name:z.name,suggestedApi:Z.api,subtitle:z.name&&z.name!==z.id?z.name:void 0,ownedBy:Z.name,reasoning:z.reasoning,contextWindow:z.contextWindow,maxTokens:z.maxTokens,cost:z.cost}),X.push(z.id);J.set(Z.name,X)}let Y=[];for(let Z of $.customPool){if(!j.has(Z.id))j.set(Z.id,{id:Z.id,name:Z.name,suggestedApi:Z.api,subtitle:Z.name&&Z.name!==Z.id?Z.name:void 0,origin:Z.suggestedProvider,ownedBy:Z.ownedBy,reasoning:Z.reasoning,contextWindow:Z.contextWindow,maxTokens:Z.maxTokens,cost:Z.cost});Y.push(Z.id)}return{byId:j,builtinModelIds:J,customPoolIds:Y}}function R1($){let j=new Map;for(let[J,Y]of Object.entries($.customProviders))for(let Z of Y.models)j.set(Z.id,J);return j}function $$($,j){if(j.kind==="builtin")return[...$.builtinProviders[j.name]?.models??[]];return $.customProviders[j.name]?.models.map((J)=>J.id)??[]}function g0($,j,J){if(j.kind==="builtin"){let X=J.builtinModelIds.get(j.name)??[],z=new Set($$($,j));return X.filter((Q)=>!z.has(Q))}let Y=R1($),Z=new Set($$($,j));return J.customPoolIds.filter((X)=>{if(Z.has(X))return!1;let z=Y.get(X);return z===void 0||z===j.name})}function _1($){return{id:$.id,name:$.name,contextWindow:$.contextWindow,maxTokens:$.maxTokens,reasoning:$.reasoning,cost:$.cost}}function f0($,j,J){if(j.kind==="builtin"){let Z=$.builtinProviders[j.name]??{enabled:!0,models:[]},X=new Set(Z.models);X.add(J.id),$.builtinProviders[j.name]={enabled:!0,apiOverride:Z.apiOverride??null,models:Array.from(X).sort()};return}for(let[Z,X]of Object.entries($.customProviders)){if(Z===j.name)continue;let z=X.models.findIndex((Q)=>Q.id===J.id);if(z>=0)X.models.splice(z,1)}let Y=$.customProviders[j.name]??{api:j.api,models:[]};if(!Y.models.some((Z)=>Z.id===J.id))Y.models.push(_1(J));$.customProviders[j.name]=Y}function h0($,j,J){if(j.kind==="builtin"){let Z=$.builtinProviders[j.name];if(!Z)return;Z.models=Z.models.filter((X)=>X!==J),Z.enabled=Z.models.length>0;return}let Y=$.customProviders[j.name];if(!Y)return;Y.models=Y.models.filter((Z)=>Z.id!==J)}function d$($,j){if($===j)return!0;let J=["openai-completions","openai-responses"];if(J.includes($)&&J.includes(j))return!0;return!1}function p0($,j){let J=new Map;for(let Y of $){let Z=j.byId.get(Y),X=Z?.ownedBy||Z?.origin||"misc",z=J.get(X)??[];z.push(Y),J.set(X,z)}return Array.from(J.entries()).sort((Y,Z)=>Y[0].localeCompare(Z[0])).map(([Y,Z])=>({label:Y,ids:Z}))}function c0($,j,J){let Y=J.trim().toLowerCase();if(!Y)return $;return $.filter((Z)=>{if(Z.toLowerCase().includes(Y))return!0;let X=j.byId.get(Z)?.name;return X?X.toLowerCase().includes(Y):!1})}import{getModels as A1,getProviders as O1}from"@duckmind/dm-ai/compat";function t$($,j){let J=[],Y=new Set(j.builtinModelIds.keys()),Z=new Set(O1()),X=new Set([...Y,...Z]);for(let z of Array.from(X).sort()){let Q=j.builtinModelIds.get(z);if(!Q||Q.length===0)continue;let q;try{let M=A1(z).find((U)=>Q.includes(U.id));if(M)q=M.api}catch{}if(!q){let V=Q[0];q=j.byId.get(V)?.suggestedApi??"openai-responses"}if(J.push({kind:"builtin",name:z,api:q,subtitle:`built-in · ${q}`}),!$.builtinProviders[z])$.builtinProviders[z]={enabled:!1,models:[]}}for(let z of Object.keys($.customProviders).sort()){let Q=$.customProviders[z];J.push({kind:"custom",name:z,api:Q.api,subtitle:`custom · ${Q.api}`})}return J}import{getKeybindings as H1,matchesKey as z$}from"@duckmind/dm-tui";async function l0($,j){return $.ui.custom((J,Y,Z,X)=>G1(Y,`Remove custom group “${j}”?`,"All model assignments in this group will be discarded.",X),{overlay:!0,overlayOptions:{width:80,maxHeight:"40%"}})}function G1($,j,J,Y){return{render(Z){return r($,{width:Z,title:" confirm ",titleColor:"error",lines:["",` ${$.bold(j)}`,` ${$.fg("dim",J)}`,""],footer:{hint:" y / enter = remove · n / esc = cancel "}})},invalidate(){},handleInput(Z){if(H1().matches(Z,"tui.select.cancel")||z$(Z,"escape")||z$(Z,"n")||z$(Z,"q")){Y(!1);return}if(z$(Z,"y")||z$(Z,"enter")||z$(Z,"return")){Y(!0);return}}}}import{getKeybindings as N1,Input as W1,matchesKey as k1}from"@duckmind/dm-tui";async function m0($,j){let J=H$(j,"group");return $.ui.custom((Y,Z,X,z)=>T1(Y,Z,J,z),{overlay:!0,overlayOptions:{width:80,maxHeight:"40%"}})}function T1($,j,J,Y){let Z=new W1;if(J)Z.setValue(J);Z.focused=!0;let X=null;return Z.onSubmit=(z)=>{let Q=z.trim();if(!/^[a-z0-9][a-z0-9._-]*$/i.test(Q)){X="letters / digits / dot / dash / underscore only",$.requestRender();return}Y(Q)},Z.onEscape=()=>Y(null),{render(z){let Q=F$(z),q=J?`name shown in /model picker, e.g. ${J}`:"letters / digits / dot / dash / underscore",V=Z.render(Q-4),M=["",` ${j.fg("dim",q)}`,"",...V.map((U)=>` ${j.fg("accent",`❯ ${U}`)}`),"",X?` ${j.fg("error",`! ${X}`)}`:""];return r(j,{width:z,title:" new custom group ",lines:M,footer:{hint:" enter = create · esc = cancel "}})},invalidate(){Z.invalidate()},handleInput(z){if(N1().matches(z,"tui.select.cancel")||k1(z,"escape")){Y(null);return}X=null,Z.handleInput(z),$.requestRender()}}}import{visibleWidth as B1}from"@duckmind/dm-tui";function a$($,j,J){if(!j)return" ";if(J)return $.fg("accent","▶ ");return $.fg("muted","▸ ")}function s0($,j){let J=B1($);if(J===j)return $;if(J>j)return s$($,j);return S($,j)}function o0($,j,J){let{theme:Y,width:Z,isCursor:X,isFocused:z}=J,Q=a$(Y,X,z),q=$.kind==="builtin"?j.builtinProviders[$.name]?.models.length??0:j.customProviders[$.name]?.models.length??0,V=$.kind==="builtin"?Y.fg("accent","B"):Y.fg("warning","C"),M=`${Y.fg("dim","[")}${V}${Y.fg("dim","]")}`,U=q>0?Y.fg("success","●"):Y.fg("dim","○"),F=q>0?Y.fg("success",String(q).padStart(2)):Y.fg("dim"," 0"),T=X&&z?Y.bold(Y.fg("accent",$.name)):X?Y.bold($.name):$.name,O=Y.fg("dim",$.api),D=`${Q}${M} ${T} ${U}${F} ${O}`;return s0(D,Z)}function i0($,j,J,Y){let Z=a$($,j,J),X=j&&J?$.bold($.fg("accent","+ new custom group…")):$.fg("accent","+ new custom group…");return S(`${Z}${X}`,Y)}function e$($,j,J,Y,Z){let{theme:X,width:z,isCursor:Q,isFocused:q}=Z,V=a$(X,Q,q),M=J==="assigned"?X.fg("success","☑"):X.fg("dim","☐"),U=Y?` ${X.fg("warning","⚠")}`:"",F=Q&&q?X.fg("accent",$):$,T=j?.reasoning?` ${X.fg("accent","✨")}`:"",O=j?X.fg("muted",` · ${j.suggestedApi}`):X.fg("error"," · not on proxy"),D=`${V}${M} ${F}${U}${T}${O}`;return s0(D,z)}function r0($,j,J){let Y=`${$.fg("borderAccent","──")} ${$.fg("warning",j)} ${$.fg("borderAccent","──")}`;return S(` ${Y}`,J)}function $0($,j,J){return S(` ${$.fg("dim",j)}`,J)}function d0($,j,J,Y){let Z=Y?$.bold($.fg("accent","❚ ")):$.fg("dim"," ");return S(`${Z}${j}`,J)}function t0($){let{tui:j,theme:J,ctx:Y,cfg:Z}=$,X=$.onChange??(()=>{}),z=r$($.getDiscovery()),Q=t$(Z,z),q="providers",V=0,M=0,U=0,F=0,T=0,O=0,D="",A=!1,B=()=>{if(Q.length===0)return null;let E=Math.max(0,Math.min(V,Q.length-1));return Q[E]??null},C=(E)=>{let H=c0(g0(Z,E,z),z,D);return p0(H,z)},N=(E)=>C(E).flatMap((H)=>H.ids),G=()=>{Q=t$(Z,z);let E=Math.max(0,Q.length);if(V>E)V=E;let H=B();if(H){let x=$$(Z,H).length,W=N(H).length;if(M>=x)M=Math.max(0,x-1);if(U>=W)U=Math.max(0,W-1)}},n=()=>{z=r$($.getDiscovery()),G()},j$=(E)=>{let H=["providers","assigned","pool"],x=H.indexOf(q);q=E?H[(x-1+H.length)%H.length]:H[(x+1)%H.length]},_=(E)=>{if(q==="providers"){let W=Q.length+1;V=Math.max(0,Math.min(V+E,W-1));return}let H=B();if(!H)return;if(q==="assigned"){let W=$$(Z,H).length;if(W===0)return;M=Math.max(0,Math.min(M+E,W-1));return}let x=N(H).length;if(x===0)return;U=Math.max(0,Math.min(U+E,x-1))},b=async()=>{if(q==="providers"){if(V===Q.length){let W=await m0(Y,Z.proxy.providerPrefix);if(W&&!Z.customProviders[W]){if(Z.customProviders[W]={api:"openai-completions",models:[]},X(),G(),V=Q.findIndex((I)=>I.kind==="custom"&&I.name===W),V<0)V=Q.length-1;q="pool"}j.requestRender();return}q="pool";return}let E=B();if(!E)return;if(q==="assigned"){let W=$$(Z,E)[M];if(!W)return;h0(Z,E,W),X(),G();return}let H=N(E)[U];if(!H)return;let x=z.byId.get(H);if(!x)return;f0(Z,E,x),X(),G()},p=async()=>{if(q!=="providers")return;let E=B();if(!E||E.kind!=="custom")return;if(!await l0(Y,E.name)){j.requestRender();return}if(delete Z.customProviders[E.name],X(),G(),V>=Q.length)V=Math.max(0,Q.length-1);j.requestRender()},v=(E,H)=>c(E,H);function c(E,H){let x=Math.max(70,E),W=Math.min(56,Math.max(34,Math.floor(x*0.4))),I=x-W-1,s=Math.max(8,H),o=Math.max(5,Math.floor((s-1)/2)),j0=s-o,m=B(),q$=0,x$=0,n$=0,J0=0,P$=0,Y0=0,a=[];a.push(J$(" providers ",W,q==="providers"));for(let L=0;L<Q.length;L++){let g=Q[L],w=q==="providers"&&L===V;if(w)q$=a.length,x$=q$;a.push(o0(g,Z,{theme:J,width:W,isCursor:w,isFocused:q==="providers"}))}{let L=q==="providers"&&V===Q.length;if(L)q$=a.length,x$=q$;a.push(i0(J,L,q==="providers",W))}let e=[],qj=m?`assigned to ${J.bold(m.name)} ${J.fg("dim",`· ${m.api}`)}`:J.fg("dim","no provider selected");if(e.push(J$(" assigned ",I,q==="assigned")),e.push(d0(J,qj,I,q==="assigned")),m){let L=$$(Z,m);if(L.length===0)e.push($0(J,"(nothing assigned yet)",I));for(let g=0;g<L.length;g++){let w=L[g],f=z.byId.get(w),O$=q==="assigned"&&g===M;if(O$)n$=e.length,J0=n$;let Y$=f?!d$(m.api,f.suggestedApi):!1;e.push(e$(w,f,"assigned",Y$,{theme:J,width:I,isCursor:O$,isFocused:q==="assigned"}))}}let i=[];if(i.push(P(I,q==="pool")),m){let L=C(m);if(L.reduce((w,f)=>w+f.ids.length,0)===0)i.push($0(J,D?`(no matches for "${D}")`:"(no models available)",I));else{let w=0;for(let f of L){let O$=i.length;i.push(r0(J,f.label,I));for(let Y$=0;Y$<f.ids.length;Y$++){let M0=f.ids[Y$],C$=z.byId.get(M0),V0=q==="pool"&&w===U;if(V0)P$=i.length,Y0=Y$===0?O$:P$;let Ej=C$?!d$(m.api,C$.suggestedApi):!1;i.push(e$(M0,C$,"pool",Ej,{theme:J,width:I,isCursor:V0,isFocused:q==="pool"})),w++}}}}let Mj=Math.max(1,j0-1),Z0=I$(x$,q$,F,s,a.length,1),X0=I$(q==="assigned"?J0:0,q==="assigned"?n$:0,T,o,e.length,2),Q0=I$(q==="pool"?Y0:0,q==="pool"?P$:0,O,Mj,i.length,1);F=Z0,T=X0,O=Q0;let z0=d(a,Z0,s),Vj=d(e,X0,o),Uj=d(i,Q0,j0),q0=J.fg("borderAccent","│"),y$=[];for(let L=0;L<s;L++){let g=S(z0[L]??"",W),w=L<o?Vj[L]:Uj[L-o],f=S(w,I);y$.push(`${g}${q0}${f}`)}let A$=o;if(A$>0&&A$<s){let L=S(z0[A$]??"",W),g=J.fg("borderAccent","─".repeat(I));y$[A$]=`${L}${q0}${g}`}return y$}function J$(E,H,x){let W=x?J.bold(J.fg("accent",E)):J.fg("muted",E),I=J.fg("borderAccent","─".repeat(Math.max(0,H-S$(E)-1)));return S(`${W}${I}`,H)}function P(E,H){let W=H?J.bold(J.fg("accent"," available pool ")):J.fg("muted"," available pool "),I="";if(A)I=`${J.fg("dim"," /")}${J.fg("accent",D)}${J.fg("accent","█")} `;else if(D)I=`${J.fg("dim"," /")}${J.fg("warning",D)} `;let s=S$(" available pool ")+S$(I),o=J.fg("borderAccent","─".repeat(Math.max(0,E-s-1)));return S(`${W}${I}${o}`,E)}function _$(E){if(A){if(k(E,"escape"))return D="",A=!1,U=0,!0;if(k(E,"enter")||k(E,"return"))return A=!1,!0;if(k(E,"backspace")||k(E,"delete"))return D=D.slice(0,-1),U=0,!0;if(E.length===1&&E>=" "&&E!=="")return D+=E,U=0,!0;return!0}if(k(E,"tab"))return j$(!1),!0;if(k(E,"shift+tab"))return j$(!0),!0;if(k(E,"/"))return q="pool",A=!0,!0;if(k(E,"enter")||k(E,"return")||k(E,"space"))return b().then(()=>!0);if(q==="providers"&&(k(E,"d")||k(E,"delete")||k(E,"backspace")))return p().then(()=>!0);if(k(E,"up")||k(E,"k"))return _(-1),!0;if(k(E,"down")||k(E,"j"))return _(1),!0;if(k(E,"pageUp")||k(E,"b"))return _(-8),!0;if(k(E,"pageDown")||k(E,"f"))return _(8),!0;if(k(E,"left")||k(E,"h"))return q="providers",!0;if(k(E,"right")||k(E,"l")){if(q==="providers")q="assigned";else if(q==="assigned")q="pool";return!0}return!1}function zj(){if(A)return" type to filter · ↵ apply · esc clear ";return" tab ↔ panel · ↑↓ nav · ↵ move · / filter · d remove group "}return{id:"models",label:"Models",render:v,handleInput:_$,footerHint:zj,rebuild:n}}import{matchesKey as y}from"@duckmind/dm-tui";var R={reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",accent:"\x1B[38;5;111m",muted:"\x1B[38;5;245m",dim2:"\x1B[38;5;240m",green:"\x1B[38;5;114m",yellow:"\x1B[38;5;179m",red:"\x1B[38;5;167m",greenBg:"\x1B[48;5;22m",yellowBg:"\x1B[48;5;94m",redBg:"\x1B[48;5;52m"};function a0($,j={}){let J=[],Y=new Map,Z=0;for(let X of $.accounts){if(!j.showDisabled&&X.disabled){Z++;continue}let z=Y.get(X.provider)??[];z.push(X),Y.set(X.provider,z)}if(J.push(`${R.dim}generated ${$.generatedAt}${R.reset}`),$.unsupportedProviders.length>0)J.push(`${R.dim}providers without quota lookup:${R.reset} ${$.unsupportedProviders.join(", ")}`);if(Z>0)J.push(`${R.dim}hidden disabled accounts:${R.reset} ${Z} ${R.dim2}(press d to show)${R.reset}`);for(let[X,z]of Array.from(Y.entries()).sort()){J.push(""),J.push(`${R.bold}${R.accent}▌ ${X}${R.reset}`);for(let Q of z){if(J.push(K1(Q)),!Q.supported){J.push(` ${R.dim2}(provider not quota-aware)${R.reset}`);continue}if(Q.error){if(j.verbose)for(let V of L1(Q.error,88))J.push(` ${R.red}${V}${R.reset}`);continue}let q=Q.groups??[];if(q.length===0){J.push(` ${R.dim2}(no active quota windows)${R.reset}`);continue}for(let V of q)J.push(S1(V))}}return J.push(""),J.push(`${R.dim}legend:${R.reset} ${e0} ok ${$j} error/unavailable ${jj} disabled ${R.green}✓N${R.reset}/${R.red}✗N${R.reset} request counters ${R.dim}v = verbose, d = show disabled${R.reset}`),J}function L1($,j){if($.length<=j)return[$];let J=[],Y=$;while(Y.length>j){let Z=Y.lastIndexOf(" ",j);if(Z<j*0.6)Z=j;J.push(Y.slice(0,Z)),Y=Y.slice(Z).replace(/^\s+/,"")}if(Y)J.push(Y);return J}function K1($){let j=F1($),J=`${R.green}✓${$.success}${R.reset} ${R.red}✗${$.failed}${R.reset}`,Y=Zj($.label,36);return` ${j} ${R.bold}${Y}${R.reset} ${J}`}var e0=`${R.green}●${R.reset}`,$j=`${R.yellow}⚠${R.reset}`,jj=`${R.dim2}⊘${R.reset}`;function F1($){if($.disabled)return jj;if($.unavailable||$.error||$.status&&$.status!=="active")return $j;return e0}function S1($){let j=Yj($.remainingFraction),J=Math.round(j*100),Y=I1(j),Z=$.resetTime?` ${R.dim}reset ${x1($.resetTime)}${R.reset}`:"",X=Zj($.label,28).padEnd(28," ");return` ${Y} ${b1(J,j)} ${X}${Z}`}function I1($){let j=Yj($),J=Math.round(j*18),Y=Jj(j),Z=R.dim2,X="█".repeat(J),z="░".repeat(18-J);return`${Z}[${Y}${X}${Z}${z}]${R.reset}`}function b1($,j){return`${Jj(j)}${String($).padStart(3)}%${R.reset}`}function Jj($){if($>=0.4)return R.green;if($>=0.15)return R.yellow;return R.red}function x1($){let j=Date.parse($);if(!Number.isFinite(j))return $;let J=j-Date.now();if(J<=0)return"now";let Y=Math.round(J/60000);if(Y<60)return`${Y}m`;let Z=Math.round(Y/60);if(Z<48)return`${Z}h`;return`${Math.round(Z/24)}d`}function Yj($){return Math.max(0,Math.min(1,$))}function Zj($,j){if($.length<=j)return $;return`${$.slice(0,j-1)}…`}function Xj($){let{tui:j,theme:J,cfg:Y}=$,Z="idle",X=null,z="",Q=0,q=!1,V=!1,M=(D)=>{Z="loading",j.requestRender();let A=h(Y.proxy.usageKey);K0(Y,A,{force:D}).then((B)=>{X=B,Z="ready",Q=0,j.requestRender()}).catch((B)=>{z=B.message,Z="error",j.requestRender()})},U=()=>{if(Z==="idle"||Z==="loading")return[J.fg("dim"," loading usage…")];if(Z==="error")return[J.fg("error",` usage failed: ${z}`)];if(!X)return[J.fg("dim"," (no data)")];return a0(X,{showDisabled:q,verbose:V})};return{id:"usage",label:"Usage",render:(D,A)=>{let B=U();return Q=b$(Q,A,B.length),d(B,Q,A).map((C)=>S(` ${C}`,D))},handleInput:(D)=>{if(y(D,"d"))return q=!q,Q=0,!0;if(y(D,"v"))return V=!V,Q=0,!0;let A=U().length;if(y(D,"up")||y(D,"k"))return Q=Math.max(0,Q-1),!0;if(y(D,"down")||y(D,"j"))return Q=Math.min(Math.max(0,A-1),Q+1),!0;if(y(D,"pageUp")||y(D,"b"))return Q=Math.max(0,Q-10),!0;if(y(D,"pageDown")||y(D,"f"))return Q=Math.min(Math.max(0,A-1),Q+10),!0;if(y(D,"home")||y(D,"g"))return Q=0,!0;if(y(D,"end")||y(D,"shift+g"))return Q=Math.max(0,A-1),!0;return!1},footerHint:()=>{let D=q?J.fg("success","[d disabled]"):"d disabled",A=V?J.fg("success","[v verbose]"):"v verbose";return` ↑↓ scroll · ${D} · ${A} `},onActivate:()=>{if(Z==="idle")M(!1)},reload:()=>{if(K$(),Z!=="idle")M(!0)}}}function Qj($){let{dm:j,ctx:J,tui:Y,theme:Z,cfg:X,done:z}=$,Q=$.discovery,q=!1,V="",M=t0({tui:Y,theme:Z,ctx:J,cfg:X,getDiscovery:()=>Q,onChange:()=>{q=!0,V=""}}),U=Xj({tui:Y,theme:Z,cfg:X}),F=v0({theme:Z,cfg:X,getDiscovery:()=>Q}),T=[M,U,F],O=0,D=()=>T[O],A=(_)=>{if(_<0||_>=T.length||_===O)return;O=_,D().onActivate?.(),Y.requestRender()},B=()=>{V=Z.fg("dim","saving…"),B$(X),Y.requestRender();let _=l$(j);m$(j,X,Q,_).then((b)=>{q=!1,V=Z.fg("success",`✓ settings saved · ${b.registered.length} registered, ${b.skipped.length} skipped`),Y.requestRender()}).catch((b)=>{V=Z.fg("error",`save failed: ${b.message}`),Y.requestRender()})},C=async()=>{V=Z.fg("dim","refreshing…"),Y.requestRender();try{let _=l$(j),b=h(X.proxy.apiKey);Q=await W0(X,b),K$(),M.rebuild(),U.reload();let p=await m$(j,X,Q,_);V=Z.fg("success",`✓ refreshed · ${p.registered.length} providers (${Q.source})`)}catch(_){V=Z.fg("error",`refresh failed: ${_.message}`)}Y.requestRender()},N=async()=>{if(!await I0(J,!0)){Y.requestRender();return}let b=T$();X.proxy={...X.proxy,...b.proxy},await C()},G=()=>{let _=X.proxy.endpoint||"(unset)",b=_.length>40?`…${_.slice(-39)}`:_,p=Boolean(h(X.proxy.apiKey)),v=0,c=0;for(let P of Object.values(X.builtinProviders)){if(P.enabled&&P.models.length>0)v++;c+=P.models.length}for(let P of Object.values(X.customProviders)){if(P.models.length>0)v++;c+=P.models.length}let J$=V?V:q?Z.fg("warning","● unsaved — press s to save"):Z.fg("dim","✓ saved");return[`${Z.fg("dim","endpoint")} ${b}`,`${Z.fg("dim","key")} ${p?Z.fg("success","✓"):Z.fg("error","✗")}`,`${Z.fg("dim","providers")} ${v}`,`${Z.fg("dim","models")} ${c}`,J$]},n=(_)=>{let b=Y.rows??40,v=Math.max(16,Math.min(b-6,38))-2,c=Math.max(72,_-2),J$=Math.max(6,v-3),P=[];return P.push(n0(Z,G(),c)),P.push(x0(Z,T.map((_$)=>({id:_$.id,label:_$.label})),O,c)),P.push(P0(Z,c)),P.push(...D().render(c,J$)),r(Z,{width:_,title:" /cliproxy ",lines:P,footer:{hint:D().footerHint(),badge:" [ ] tab · r refresh · e setup · s save · q close "}})},j$=(_)=>{if(n1().matches(_,"tui.select.cancel")||u(_,"q")||u(_,"escape"))return z(),!0;if(u(_,"]")||u(_,"tab"))return A((O+1)%T.length),!0;if(u(_,"[")||u(_,"shift+tab"))return A((O-1+T.length)%T.length),!0;if(u(_,"1"))return A(0),!0;if(u(_,"2"))return A(1),!0;if(u(_,"3"))return A(2),!0;if(u(_,"r"))return C().then(()=>!0);if(u(_,"e"))return N().then(()=>!0);if(u(_,"s"))return B(),!0;return!1};return{render:n,invalidate(){},handleInput(_){let b=D().handleInput(_);if(b instanceof Promise){V="",b.then((v)=>{if(v)Y.requestRender()});return}if(b){V="",Y.requestRender();return}let p=j$(_);if(p instanceof Promise)p.then((v)=>{if(v)Y.requestRender()});else if(p)Y.requestRender()}}}async function FJ($,j,J,Y){if(!j.hasUI){j.ui.notify("interactive UI required for /cliproxy","warning");return}let Z=structuredClone(J);w$(!0);try{await j.ui.custom((X,z,Q,q)=>Qj({dm:$,ctx:j,tui:X,theme:z,cfg:Z,discovery:Y,done:q}),{overlay:!0,overlayOptions:{width:170,maxHeight:"94%"}})}finally{w$(!1)}}export{FJ as runHub};
|
|
1
|
+
import { setLogQuiet } from "../log.js";
|
|
2
|
+
import { buildHub } from "./hub.js";
|
|
3
|
+
export async function runHub(dm, ctx, cfg, discovery) {
|
|
4
|
+
if (!ctx.hasUI) {
|
|
5
|
+
ctx.ui.notify("interactive UI required for /cliproxy", "warning");
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const draft = structuredClone(cfg);
|
|
9
|
+
setLogQuiet(true);
|
|
10
|
+
try {
|
|
11
|
+
await ctx.ui.custom((tui, theme, _kb, done) => buildHub({
|
|
12
|
+
dm,
|
|
13
|
+
ctx,
|
|
14
|
+
tui,
|
|
15
|
+
theme,
|
|
16
|
+
cfg: draft,
|
|
17
|
+
discovery,
|
|
18
|
+
done
|
|
19
|
+
}), {
|
|
20
|
+
overlay: true,
|
|
21
|
+
overlayOptions: { width: 170, maxHeight: "94%" }
|
|
22
|
+
});
|
|
23
|
+
} finally {
|
|
24
|
+
setLogQuiet(false);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1 +1,46 @@
|
|
|
1
|
-
import{visibleWidth
|
|
1
|
+
import { visibleWidth } from "@duckmind/dm-tui";
|
|
2
|
+
import { pad } from "../ui-picker/render-text.js";
|
|
3
|
+
export function tabBar(theme, tabs, activeIdx, width) {
|
|
4
|
+
const sep = theme.fg("borderAccent", "│");
|
|
5
|
+
const cells = tabs.map((t, i) => {
|
|
6
|
+
const label = `${i + 1} ${t.label}`;
|
|
7
|
+
return i === activeIdx ? theme.bold(theme.fg("accent", ` ${label} `)) : theme.fg("dim", ` ${label} `);
|
|
8
|
+
});
|
|
9
|
+
return pad(` ${cells.join(sep)}`, width);
|
|
10
|
+
}
|
|
11
|
+
export function statusHeader(theme, parts, width) {
|
|
12
|
+
const joined = parts.join(theme.fg("dim", " "));
|
|
13
|
+
return pad(` ${joined}`, width);
|
|
14
|
+
}
|
|
15
|
+
export function ruleLine(theme, width) {
|
|
16
|
+
return theme.fg("borderAccent", "─".repeat(Math.max(0, width)));
|
|
17
|
+
}
|
|
18
|
+
export function takeSlice(lines, scroll, count) {
|
|
19
|
+
const s = lines.slice(scroll, scroll + count);
|
|
20
|
+
while (s.length < count)
|
|
21
|
+
s.push("");
|
|
22
|
+
return s;
|
|
23
|
+
}
|
|
24
|
+
export function clampScroll(cursorTop, cursorBottom, prev, visible, total, stickyCount) {
|
|
25
|
+
if (total <= visible)
|
|
26
|
+
return 0;
|
|
27
|
+
const maxScroll = Math.max(0, total - visible);
|
|
28
|
+
if (cursorBottom < stickyCount)
|
|
29
|
+
return 0;
|
|
30
|
+
let scroll = Math.max(0, Math.min(prev, maxScroll));
|
|
31
|
+
const topRow = scroll + stickyCount;
|
|
32
|
+
if (cursorTop < topRow) {
|
|
33
|
+
scroll = Math.max(0, cursorTop - stickyCount);
|
|
34
|
+
}
|
|
35
|
+
const bottomRow = scroll + visible - 1;
|
|
36
|
+
if (cursorBottom > bottomRow) {
|
|
37
|
+
scroll = Math.min(maxScroll, cursorBottom - (visible - 1));
|
|
38
|
+
}
|
|
39
|
+
return scroll;
|
|
40
|
+
}
|
|
41
|
+
export function clampOffset(offset, visible, total) {
|
|
42
|
+
const max = Math.max(0, total - visible);
|
|
43
|
+
return Math.max(0, Math.min(offset, max));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { visibleWidth };
|
|
@@ -1 +1,69 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { matchesKey } from "@duckmind/dm-tui";
|
|
2
|
+
import { resolveConfigValue } from "../config.js";
|
|
3
|
+
import { detectConflicts } from "../conflicts.js";
|
|
4
|
+
import { PLUGIN_USER_AGENT } from "../fetch-models.js";
|
|
5
|
+
import { pad } from "../ui-picker/render-text.js";
|
|
6
|
+
import { clampOffset, takeSlice } from "./shell.js";
|
|
7
|
+
export function buildDiagnosticsView(deps) {
|
|
8
|
+
const { theme, cfg } = deps;
|
|
9
|
+
let offset = 0;
|
|
10
|
+
const buildLines = () => {
|
|
11
|
+
const d = deps.getDiscovery();
|
|
12
|
+
const ok = (s) => theme.fg("success", s);
|
|
13
|
+
const bad = (s) => theme.fg("error", s);
|
|
14
|
+
const dim = (s) => theme.fg("dim", s);
|
|
15
|
+
const lines = [];
|
|
16
|
+
lines.push(`${dim("endpoint")} ${cfg.proxy.endpoint || bad("(unset)")}`);
|
|
17
|
+
lines.push(`${dim("apiKey")} ${resolveConfigValue(cfg.proxy.apiKey) ? ok("resolves") : bad("empty after resolution")}`);
|
|
18
|
+
lines.push(`${dim("usageKey")} ${cfg.proxy.usageKey ? resolveConfigValue(cfg.proxy.usageKey) ? ok("resolves") : bad("set but empty") : dim("not configured")}`);
|
|
19
|
+
lines.push(`${dim("user-agent")} ${PLUGIN_USER_AGENT}`);
|
|
20
|
+
lines.push("");
|
|
21
|
+
lines.push(`${dim("discovery")} source=${d.source}`);
|
|
22
|
+
lines.push(`${dim("upstream")} v=${d.upstreamVersion ?? "(unknown)"} · ${d.upstreamTotal} ids`);
|
|
23
|
+
const builtins = d.builtinProviders.map((p) => `${p.name}=${p.models.length}`).join(", ") || dim("(none)");
|
|
24
|
+
lines.push(`${dim("built-in")} ${builtins}`);
|
|
25
|
+
lines.push(`${dim("custom pool")} ${d.customPool.length} models`);
|
|
26
|
+
const conflicts = detectConflicts(cfg);
|
|
27
|
+
lines.push("");
|
|
28
|
+
if (conflicts.length === 0) {
|
|
29
|
+
lines.push(`${dim("conflicts")} ${ok("none")}`);
|
|
30
|
+
} else {
|
|
31
|
+
lines.push(`${dim("conflicts")}`);
|
|
32
|
+
for (const c of conflicts)
|
|
33
|
+
lines.push(` ${theme.fg("warning", `[${c.kind}]`)} ${c.detail}`);
|
|
34
|
+
}
|
|
35
|
+
return lines;
|
|
36
|
+
};
|
|
37
|
+
const render = (width, height) => {
|
|
38
|
+
const body = buildLines();
|
|
39
|
+
offset = clampOffset(offset, height, body.length);
|
|
40
|
+
return takeSlice(body, offset, height).map((ln) => pad(` ${ln}`, width));
|
|
41
|
+
};
|
|
42
|
+
const handleInput = (data) => {
|
|
43
|
+
const total = buildLines().length;
|
|
44
|
+
if (matchesKey(data, "up") || matchesKey(data, "k")) {
|
|
45
|
+
offset = Math.max(0, offset - 1);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (matchesKey(data, "down") || matchesKey(data, "j")) {
|
|
49
|
+
offset = Math.min(Math.max(0, total - 1), offset + 1);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (matchesKey(data, "pageUp") || matchesKey(data, "b")) {
|
|
53
|
+
offset = Math.max(0, offset - 10);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (matchesKey(data, "pageDown") || matchesKey(data, "f")) {
|
|
57
|
+
offset = Math.min(Math.max(0, total - 1), offset + 10);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
id: "diagnostics",
|
|
64
|
+
label: "Diagnostics",
|
|
65
|
+
render,
|
|
66
|
+
handleInput,
|
|
67
|
+
footerHint: () => " ↑↓ scroll · r refresh discovery "
|
|
68
|
+
};
|
|
69
|
+
}
|