@duckmind/dm-windows-x64 0.60.4 → 0.60.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +70 -123
  3. package/extensions/dm-9router-ext/src/index.js +410 -5
  4. package/extensions/dm-caveman/extensions/caveman.js +283 -12
  5. package/extensions/dm-cliproxy/index.js +182 -2
  6. package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
  7. package/extensions/dm-cliproxy/src/apply.js +228 -1
  8. package/extensions/dm-cliproxy/src/cache.js +42 -1
  9. package/extensions/dm-cliproxy/src/commands.js +50 -2
  10. package/extensions/dm-cliproxy/src/compat.js +81 -1
  11. package/extensions/dm-cliproxy/src/config.js +192 -2
  12. package/extensions/dm-cliproxy/src/conflicts.js +46 -1
  13. package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
  14. package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
  15. package/extensions/dm-cliproxy/src/log.js +23 -1
  16. package/extensions/dm-cliproxy/src/status-quota.js +77 -1
  17. package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
  18. package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
  19. package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
  20. package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
  21. package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
  22. package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
  23. package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
  24. package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
  25. package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
  26. package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
  27. package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
  28. package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
  29. package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
  30. package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
  31. package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
  32. package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
  33. package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
  34. package/extensions/dm-context/src/context.js +144 -1
  35. package/extensions/dm-context/src/index.js +339 -7
  36. package/extensions/dm-context/src/utils.js +9 -1
  37. package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
  38. package/extensions/dm-cua/index.js +75 -6
  39. package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
  40. package/extensions/dm-cua/src/browser-install.mjs +331 -2
  41. package/extensions/dm-fff/src/index.js +688 -12
  42. package/extensions/dm-fff/src/query.js +60 -1
  43. package/extensions/dm-goal/src/goal.js +894 -23
  44. package/extensions/dm-image2/index.js +103 -9
  45. package/extensions/dm-image2/src/image-lib.mjs +1275 -8
  46. package/extensions/dm-subagents/install.mjs +62 -8
  47. package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
  48. package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
  49. package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
  50. package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
  51. package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
  52. package/extensions/dm-subagents/src/agents/agents.js +1139 -11
  53. package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
  54. package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
  55. package/extensions/dm-subagents/src/agents/identity.js +29 -1
  56. package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
  57. package/extensions/dm-subagents/src/agents/skills.js +614 -6
  58. package/extensions/dm-subagents/src/extension/config.js +35 -2
  59. package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
  60. package/extensions/dm-subagents/src/extension/doctor.js +172 -15
  61. package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
  62. package/extensions/dm-subagents/src/extension/index.js +521 -359
  63. package/extensions/dm-subagents/src/extension/rpc.js +266 -7
  64. package/extensions/dm-subagents/src/extension/schemas.js +275 -1
  65. package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
  66. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
  67. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
  68. package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
  69. package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
  70. package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
  71. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
  72. package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
  73. package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
  74. package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
  75. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
  76. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
  77. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
  78. package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
  79. package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
  80. package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
  81. package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
  82. package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
  83. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
  84. package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
  85. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
  86. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
  87. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
  88. package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
  89. package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
  90. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
  91. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
  92. package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
  93. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
  94. package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
  95. package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
  96. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
  97. package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
  98. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
  99. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
  100. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
  101. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
  102. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
  103. package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
  104. package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
  105. package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
  106. package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
  107. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
  108. package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
  109. package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
  110. package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
  111. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
  112. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
  113. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
  114. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
  115. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
  116. package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
  117. package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
  118. package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
  119. package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
  120. package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
  121. package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
  122. package/extensions/dm-subagents/src/shared/formatters.js +98 -7
  123. package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
  124. package/extensions/dm-subagents/src/shared/model-info.js +62 -1
  125. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
  126. package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
  127. package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
  128. package/extensions/dm-subagents/src/shared/settings.js +198 -11
  129. package/extensions/dm-subagents/src/shared/status-format.js +53 -1
  130. package/extensions/dm-subagents/src/shared/types.js +184 -6
  131. package/extensions/dm-subagents/src/shared/utils.js +462 -2
  132. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
  133. package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
  134. package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
  135. package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
  136. package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
  137. package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
  138. package/extensions/dm-subagents/src/tui/render.js +1542 -4
  139. package/extensions/dm-usage/index.js +1294 -9
  140. package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
  141. package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
  142. package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
  143. package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
  144. package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
  145. package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
  146. package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
  147. package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
  148. package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
  149. package/extensions/greedysearch-dm/bin/search.mjs +620 -540
  150. package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
  151. package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
  152. package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
  153. package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
  154. package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
  155. package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
  156. package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
  157. package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
  158. package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
  159. package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
  160. package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
  161. package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
  162. package/extensions/greedysearch-dm/index.js +123 -23
  163. package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
  164. package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
  165. package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
  166. package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
  167. package/extensions/greedysearch-dm/src/github.mjs +222 -7
  168. package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
  169. package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
  170. package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
  171. package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
  172. package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
  173. package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
  174. package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
  175. package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
  176. package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
  177. package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
  178. package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
  179. package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
  180. package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
  181. package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
  182. package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
  183. package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
  184. package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
  185. package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
  186. package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
  187. package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
  188. package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
  189. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
  190. package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
  191. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
  192. package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
  193. package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
  194. package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
  195. package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
  196. package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
  197. package/package.json +1 -1
@@ -1,5 +1,73 @@
1
- import{readFile as g,writeFile as r,mkdir as l}from"node:fs/promises";import{homedir as v}from"node:os";import{join as S}from"node:path";import{getSettingsListTheme as o}from"@duckmind/dm-coding-agent";import{Container as s,SettingsList as c,Text as G}from"@duckmind/dm-tui";var Y=["off","lite","full","ultra","wenyan-lite","wenyan","wenyan-ultra","micro"],m=new Set(["off","stop","quit"]),d=[{value:"lite",label:"lite",description:"Professional, no fluff"},{value:"full",label:"full",description:"Classic caveman"},{value:"ultra",label:"ultra",description:"Maximum compression"},{value:"wenyan-lite",label:"wenyan-lite",description:"Semi-classical Chinese"},{value:"wenyan",label:"wenyan",description:"Full 文言文"},{value:"wenyan-ultra",label:"wenyan-ultra",description:"Extreme 文言文"},{value:"micro",label:"micro",description:"Experimental prompt-minimized mode"},{value:"off",label:"off",description:"Disable caveman mode"},{value:"stop",label:"stop",description:"Disable caveman mode"},{value:"quit",label:"quit",description:"Disable caveman mode"},{value:"config",label:"config",description:"Open settings dialog"}],E=S(v(),".dm","agent","caveman.json"),O={defaultLevel:"full",showStatus:!0},_=Promise.resolve();async function n(){try{let $=await g(E,"utf8"),q=JSON.parse($);return{defaultLevel:Y.includes(q.defaultLevel)?q.defaultLevel:O.defaultLevel,showStatus:typeof q.showStatus==="boolean"?q.showStatus:O.showStatus}}catch{return{...O}}}async function a($){let q=JSON.stringify($,null,2)+`
2
- `;return _=_.then(async()=>{await l(S(v(),".dm","agent"),{recursive:!0}),await r(E,q,"utf8")}),_}var M="\x1B[38;5;196m",N="\x1B[38;5;208m",p="\x1B[38;5;220m",L="\x1B[38;5;230m",F="\x1B[38;5;52m",j="\x1B[0m",k=[`${M}⠠${N}⠄${j}`,`${N}⠔${p}⠂${j}`,`${p}⠊${L}⠑${j}`,`${L}⠑${p}⠊${j}`,`${p}⠂${N}⠔${j}`,`${N}⠄${M}⠠${j}`,`${M}⠠${F}⠄${j}`,`${F}⠔${M}⠂${j}`],f={lite:{frames:k,label:"LITE",interval:300},full:{frames:k,label:"FULL",interval:200},ultra:{frames:k,label:"ULTRA",interval:100},"wenyan-lite":{frames:k,label:"文言",interval:300},wenyan:{frames:k,label:"文言文",interval:200},"wenyan-ultra":{frames:k,label:"文言文極",interval:100},micro:{frames:k,label:"MICRO",interval:120}},i=`IMPORTANT: You are in CAVEMAN MODE. Respond terse like smart caveman. All technical substance stay. Only fluff die.
1
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { getSettingsListTheme } from "@duckmind/dm-coding-agent";
5
+ import { Container, SettingsList, Text } from "@duckmind/dm-tui";
6
+ const LEVELS = ["off", "lite", "full", "ultra", "wenyan-lite", "wenyan", "wenyan-ultra", "micro"];
7
+ const STOP_ALIASES = new Set(["off", "stop", "quit"]);
8
+ const CAVEMAN_COMMAND_OPTIONS = [
9
+ { value: "lite", label: "lite", description: "Professional, no fluff" },
10
+ { value: "full", label: "full", description: "Classic caveman" },
11
+ { value: "ultra", label: "ultra", description: "Maximum compression" },
12
+ { value: "wenyan-lite", label: "wenyan-lite", description: "Semi-classical Chinese" },
13
+ { value: "wenyan", label: "wenyan", description: "Full 文言文" },
14
+ { value: "wenyan-ultra", label: "wenyan-ultra", description: "Extreme 文言文" },
15
+ { value: "micro", label: "micro", description: "Experimental prompt-minimized mode" },
16
+ { value: "off", label: "off", description: "Disable caveman mode" },
17
+ { value: "stop", label: "stop", description: "Disable caveman mode" },
18
+ { value: "quit", label: "quit", description: "Disable caveman mode" },
19
+ { value: "config", label: "config", description: "Open settings dialog" }
20
+ ];
21
+ const CONFIG_PATH = join(homedir(), ".dm", "agent", "caveman.json");
22
+ const DEFAULT_CONFIG = { defaultLevel: "full", showStatus: true };
23
+ let saveConfigQueue = Promise.resolve();
24
+ async function loadConfig() {
25
+ try {
26
+ const raw = await readFile(CONFIG_PATH, "utf8");
27
+ const parsed = JSON.parse(raw);
28
+ return {
29
+ defaultLevel: LEVELS.includes(parsed.defaultLevel) ? parsed.defaultLevel : DEFAULT_CONFIG.defaultLevel,
30
+ showStatus: typeof parsed.showStatus === "boolean" ? parsed.showStatus : DEFAULT_CONFIG.showStatus
31
+ };
32
+ } catch {
33
+ return { ...DEFAULT_CONFIG };
34
+ }
35
+ }
36
+ async function saveConfig(config) {
37
+ const snapshot = JSON.stringify(config, null, 2) + `
38
+ `;
39
+ saveConfigQueue = saveConfigQueue.then(async () => {
40
+ await mkdir(join(homedir(), ".dm", "agent"), { recursive: true });
41
+ await writeFile(CONFIG_PATH, snapshot, "utf8");
42
+ });
43
+ return saveConfigQueue;
44
+ }
45
+ const R = "\x1B[38;5;196m";
46
+ const O = "\x1B[38;5;208m";
47
+ const Y = "\x1B[38;5;220m";
48
+ const W = "\x1B[38;5;230m";
49
+ const E = "\x1B[38;5;52m";
50
+ const X = "\x1B[0m";
51
+ const FIRE_FRAMES = [
52
+ `${R}⠠${O}⠄${X}`,
53
+ `${O}⠔${Y}⠂${X}`,
54
+ `${Y}⠊${W}⠑${X}`,
55
+ `${W}⠑${Y}⠊${X}`,
56
+ `${Y}⠂${O}⠔${X}`,
57
+ `${O}⠄${R}⠠${X}`,
58
+ `${R}⠠${E}⠄${X}`,
59
+ `${E}⠔${R}⠂${X}`
60
+ ];
61
+ const ANIMATIONS = {
62
+ lite: { frames: FIRE_FRAMES, label: "LITE", interval: 300 },
63
+ full: { frames: FIRE_FRAMES, label: "FULL", interval: 200 },
64
+ ultra: { frames: FIRE_FRAMES, label: "ULTRA", interval: 100 },
65
+ "wenyan-lite": { frames: FIRE_FRAMES, label: "文言", interval: 300 },
66
+ wenyan: { frames: FIRE_FRAMES, label: "文言文", interval: 200 },
67
+ "wenyan-ultra": { frames: FIRE_FRAMES, label: "文言文極", interval: 100 },
68
+ micro: { frames: FIRE_FRAMES, label: "MICRO", interval: 120 }
69
+ };
70
+ const BASE = `IMPORTANT: You are in CAVEMAN MODE. Respond terse like smart caveman. All technical substance stay. Only fluff die.
3
71
 
4
72
  Rules:
5
73
  - Drop articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries, hedging
@@ -8,22 +76,225 @@ Rules:
8
76
  - Pattern: [thing] [action] [reason]. [next step].
9
77
 
10
78
  Bad: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
11
- Good: "Bug in auth middleware. Token expiry check use \`<\` not \`<=\`. Fix:"`,t=`# Token efficiency
79
+ Good: "Bug in auth middleware. Token expiry check use \`<\` not \`<=\`. Fix:"`;
80
+ const MICRO_PROMPT = `# Token efficiency
12
81
  Respond like smart caveman. Cut all filler, keep technical substance.
13
82
  - Drop articles (a, an, the), filler (just, really, basically, actually).
14
83
  - Drop pleasantries (sure, certainly, happy to).
15
84
  - No hedging. Fragments fine. Short synonyms.
16
85
  - Technical terms stay exact. Code blocks unchanged.
17
- - Pattern: [thing] [action] [reason]. [next step].`,e={lite:'No filler/hedging. Keep articles + full sentences. Professional but tight.\nExample: "Your component re-renders because you create a new object reference each render. Wrap it in `useMemo`."',full:'Drop articles, fragments OK, short synonyms.\nExample: "New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`."',ultra:'Abbreviate (DB/auth/config/req/res/fn/impl), strip conjunctions, arrows for causality (X → Y).\nExample: "Inline obj prop → new ref → re-render. `useMemo`."',"wenyan-lite":`Semi-classical Chinese. Grammar intact, filler gone. Technical terms in English.
18
- Example: "組件頻重繪,以每繪新生對象參照故。以 useMemo 包之。"`,wenyan:`Maximum classical terseness. 80-90% character reduction. Technical terms in English.
19
- Example: "物出新參照,致重繪。useMemo Wrap之。"`,"wenyan-ultra":`Extreme classical compression. Technical terms in English.
20
- Example: "新參照→重繪。useMemo Wrap。"`},bb=`Auto-clarity: drop caveman for security warnings, irreversible action confirmations, or when user is confused. Resume after.
21
- Boundaries: write normal code. Only compress explanations. "stop caveman" or "normal mode" reverts.`;function qb($){let q="off",z={...O},X=null,V=0,P=!1,h=null,R=async()=>{if(!h)h=(async()=>{if(z=await n(),q==="off"&&z.defaultLevel!=="off")q=z.defaultLevel})();await h};function I(){if(X)clearInterval(X),X=null;V=0}function Q(Z){I();let J=Z.ui.theme;if(q==="off"||!z.showStatus){Z.ui.setStatus("caveman","");return}let b=f[q],B=(U)=>{Z.ui.setStatus("caveman",U+" "+J.fg("muted","caveman level: ")+J.fg("text",b.label))};if(!P){B(b.frames[0]);return}let w=()=>{B(b.frames[V%b.frames.length]),V++};w(),X=setInterval(w,b.interval)}$.on("session_start",async(Z,J)=>{await R();let b=null;for(let B of J.sessionManager.getEntries())if(B.type==="custom"&&B.customType==="caveman-level")b=B.data?.level??null;if(b!==null)q=b;else if(z.defaultLevel!=="off")q=z.defaultLevel,$.appendEntry("caveman-level",{level:q});Q(J)}),$.on("agent_start",async(Z,J)=>{P=!0,Q(J)}),$.on("agent_end",async(Z,J)=>{P=!1,Q(J)}),$.on("session_shutdown",async()=>{I(),P=!1}),$.registerCommand("caveman",{description:"Toggle caveman mode, set level, use stop/off/quit to disable, or 'config' to open settings",getArgumentCompletions:(Z)=>{let J=Z.trim().toLowerCase(),b=d.filter((B)=>B.value.startsWith(J));return b.length>0?b:null},handler:async(Z,J)=>{let b=Z?.trim().toLowerCase();if(b==="config"){await u(J);return}if(!b)q=q==="off"?"full":"off";else if(m.has(b))q="off";else if(Y.includes(b))q=b;else{J.ui.notify(`Unknown: "${b}". Use: ${Y.join(", ")}, stop, quit, or config`,"error");return}$.appendEntry("caveman-level",{level:q}),Q(J),J.ui.notify(q==="off"?"Caveman mode off.":`Caveman: ${f[q].label}`,"info")}});async function u(Z){await R(),await Z.ui.custom((J,b,B,w)=>{let U=[{id:"defaultLevel",label:"Default level for new sessions",currentValue:z.defaultLevel,values:[...Y]},{id:"showStatus",label:"Show animated status bar",currentValue:z.showStatus?"on":"off",values:["on","off"]}],W=new s;W.addChild(new G(b.fg("accent",b.bold(" Caveman Config")),0,0)),W.addChild(new G(b.fg("dim"," Saved to ~/.dm/agent/caveman.json"),0,0)),W.addChild(new G(b.fg("dim"," Default level applies to future sessions."),0,0)),W.addChild(new G("",0,0));let A=(K,D)=>{if(K==="defaultLevel"&&Y.includes(D))z.defaultLevel=D;else if(K==="showStatus")z.showStatus=D==="on";a(z),Q(Z)},y=new c(U,Math.min(U.length+2,10),o(),A,()=>w(void 0));W.addChild(y),W.addChild(new G(b.fg("dim"," ←→/hl/tab change • ↑↓/jk move • esc close"),0,0));let T=(K)=>{let D=y.selectedIndex,H=U[D];if(!H?.values?.length)return;let x=(H.values.indexOf(H.currentValue)+K+H.values.length)%H.values.length,C=H.values[x];H.currentValue=C,y.updateValue(H.id,C),A(H.id,C)};return{render:(K)=>W.render(K),invalidate:()=>W.invalidate(),handleInput:(K)=>{if(K==="j")K="\x1B[B";else if(K==="k")K="\x1B[A";else if(K==="h"){T(-1),J.requestRender();return}else if(K==="l"||K==="\x1B[C"||K==="\t"){T(1),J.requestRender();return}else if(K==="\x1B[D"){T(-1),J.requestRender();return}y.handleInput?.(K),J.requestRender()}}})}$.on("before_agent_start",async(Z)=>{if(await R(),q==="off")return;if(q==="micro")return{systemPrompt:`${Z.systemPrompt}
86
+ - Pattern: [thing] [action] [reason]. [next step].`;
87
+ const INTENSITY = {
88
+ lite: `No filler/hedging. Keep articles + full sentences. Professional but tight.
89
+ Example: "Your component re-renders because you create a new object reference each render. Wrap it in \`useMemo\`."`,
90
+ full: `Drop articles, fragments OK, short synonyms.
91
+ Example: "New object ref each render. Inline object prop = new ref = re-render. Wrap in \`useMemo\`."`,
92
+ ultra: `Abbreviate (DB/auth/config/req/res/fn/impl), strip conjunctions, arrows for causality (X → Y).
93
+ Example: "Inline obj prop → new ref → re-render. \`useMemo\`."`,
94
+ "wenyan-lite": `Semi-classical Chinese. Grammar intact, filler gone. Technical terms in English.
95
+ Example: "組件頻重繪,以每繪新生對象參照故。以 useMemo 包之。"`,
96
+ wenyan: `Maximum classical terseness. 80-90% character reduction. Technical terms in English.
97
+ Example: "物出新參照,致重繪。useMemo Wrap之。"`,
98
+ "wenyan-ultra": `Extreme classical compression. Technical terms in English.
99
+ Example: "新參照→重繪。useMemo Wrap。"`
100
+ };
101
+ const SAFETY = `Auto-clarity: drop caveman for security warnings, irreversible action confirmations, or when user is confused. Resume after.
102
+ Boundaries: write normal code. Only compress explanations. "stop caveman" or "normal mode" reverts.`;
103
+ export default function caveman(dm) {
104
+ let level = "off";
105
+ let config = { ...DEFAULT_CONFIG };
106
+ let timer = null;
107
+ let frameIndex = 0;
108
+ let isActive = false;
109
+ let configLoadPromise = null;
110
+ const ensureConfigLoaded = async () => {
111
+ if (!configLoadPromise) {
112
+ configLoadPromise = (async () => {
113
+ config = await loadConfig();
114
+ if (level === "off" && config.defaultLevel !== "off") {
115
+ level = config.defaultLevel;
116
+ }
117
+ })();
118
+ }
119
+ await configLoadPromise;
120
+ };
121
+ function stopAnimation() {
122
+ if (timer) {
123
+ clearInterval(timer);
124
+ timer = null;
125
+ }
126
+ frameIndex = 0;
127
+ }
128
+ function syncStatus(ctx) {
129
+ stopAnimation();
130
+ const theme = ctx.ui.theme;
131
+ if (level === "off" || !config.showStatus) {
132
+ ctx.ui.setStatus("caveman", "");
133
+ return;
134
+ }
135
+ const anim = ANIMATIONS[level];
136
+ const setFrame = (frame) => {
137
+ ctx.ui.setStatus("caveman", frame + " " + theme.fg("muted", "caveman level: ") + theme.fg("text", anim.label));
138
+ };
139
+ if (!isActive) {
140
+ setFrame(anim.frames[0]);
141
+ return;
142
+ }
143
+ const renderFrame = () => {
144
+ setFrame(anim.frames[frameIndex % anim.frames.length]);
145
+ frameIndex++;
146
+ };
147
+ renderFrame();
148
+ timer = setInterval(renderFrame, anim.interval);
149
+ }
150
+ dm.on("session_start", async (_event, ctx) => {
151
+ await ensureConfigLoaded();
152
+ let sessionLevel = null;
153
+ for (const entry of ctx.sessionManager.getEntries()) {
154
+ if (entry.type === "custom" && entry.customType === "caveman-level") {
155
+ sessionLevel = entry.data?.level ?? null;
156
+ }
157
+ }
158
+ if (sessionLevel !== null) {
159
+ level = sessionLevel;
160
+ } else if (config.defaultLevel !== "off") {
161
+ level = config.defaultLevel;
162
+ dm.appendEntry("caveman-level", { level });
163
+ }
164
+ syncStatus(ctx);
165
+ });
166
+ dm.on("agent_start", async (_event, ctx) => {
167
+ isActive = true;
168
+ syncStatus(ctx);
169
+ });
170
+ dm.on("agent_end", async (_event, ctx) => {
171
+ isActive = false;
172
+ syncStatus(ctx);
173
+ });
174
+ dm.on("session_shutdown", async () => {
175
+ stopAnimation();
176
+ isActive = false;
177
+ });
178
+ dm.registerCommand("caveman", {
179
+ description: "Toggle caveman mode, set level, use stop/off/quit to disable, or 'config' to open settings",
180
+ getArgumentCompletions: (prefix) => {
181
+ const normalized = prefix.trim().toLowerCase();
182
+ const items = CAVEMAN_COMMAND_OPTIONS.filter((item) => item.value.startsWith(normalized));
183
+ return items.length > 0 ? items : null;
184
+ },
185
+ handler: async (args, ctx) => {
186
+ const arg = args?.trim().toLowerCase();
187
+ if (arg === "config") {
188
+ await openConfig(ctx);
189
+ return;
190
+ }
191
+ if (!arg) {
192
+ level = level === "off" ? "full" : "off";
193
+ } else if (STOP_ALIASES.has(arg)) {
194
+ level = "off";
195
+ } else if (LEVELS.includes(arg)) {
196
+ level = arg;
197
+ } else {
198
+ ctx.ui.notify(`Unknown: "${arg}". Use: ${LEVELS.join(", ")}, stop, quit, or config`, "error");
199
+ return;
200
+ }
201
+ dm.appendEntry("caveman-level", { level });
202
+ syncStatus(ctx);
203
+ ctx.ui.notify(level === "off" ? "Caveman mode off." : `Caveman: ${ANIMATIONS[level].label}`, "info");
204
+ }
205
+ });
206
+ async function openConfig(ctx) {
207
+ await ensureConfigLoaded();
208
+ await ctx.ui.custom((_tui, theme, _kb, done) => {
209
+ const items = [
210
+ {
211
+ id: "defaultLevel",
212
+ label: "Default level for new sessions",
213
+ currentValue: config.defaultLevel,
214
+ values: [...LEVELS]
215
+ },
216
+ {
217
+ id: "showStatus",
218
+ label: "Show animated status bar",
219
+ currentValue: config.showStatus ? "on" : "off",
220
+ values: ["on", "off"]
221
+ }
222
+ ];
223
+ const container = new Container;
224
+ container.addChild(new Text(theme.fg("accent", theme.bold(" Caveman Config")), 0, 0));
225
+ container.addChild(new Text(theme.fg("dim", " Saved to ~/.dm/agent/caveman.json"), 0, 0));
226
+ container.addChild(new Text(theme.fg("dim", " Default level applies to future sessions."), 0, 0));
227
+ container.addChild(new Text("", 0, 0));
228
+ const applySettingChange = (id, newValue) => {
229
+ if (id === "defaultLevel" && LEVELS.includes(newValue)) {
230
+ config.defaultLevel = newValue;
231
+ } else if (id === "showStatus") {
232
+ config.showStatus = newValue === "on";
233
+ }
234
+ saveConfig(config);
235
+ syncStatus(ctx);
236
+ };
237
+ const settingsList = new SettingsList(items, Math.min(items.length + 2, 10), getSettingsListTheme(), applySettingChange, () => done(undefined));
238
+ container.addChild(settingsList);
239
+ container.addChild(new Text(theme.fg("dim", " ←→/hl/tab change • ↑↓/jk move • esc close"), 0, 0));
240
+ const cycleSelectedValue = (direction) => {
241
+ const selectedIndex = settingsList.selectedIndex;
242
+ const item = items[selectedIndex];
243
+ if (!item?.values?.length)
244
+ return;
245
+ const currentIndex = item.values.indexOf(item.currentValue);
246
+ const nextIndex = (currentIndex + direction + item.values.length) % item.values.length;
247
+ const newValue = item.values[nextIndex];
248
+ item.currentValue = newValue;
249
+ settingsList.updateValue(item.id, newValue);
250
+ applySettingChange(item.id, newValue);
251
+ };
252
+ return {
253
+ render: (w) => container.render(w),
254
+ invalidate: () => container.invalidate(),
255
+ handleInput: (data) => {
256
+ if (data === "j")
257
+ data = "\x1B[B";
258
+ else if (data === "k")
259
+ data = "\x1B[A";
260
+ else if (data === "h") {
261
+ cycleSelectedValue(-1);
262
+ _tui.requestRender();
263
+ return;
264
+ } else if (data === "l" || data === "\x1B[C" || data === "\t") {
265
+ cycleSelectedValue(1);
266
+ _tui.requestRender();
267
+ return;
268
+ } else if (data === "\x1B[D") {
269
+ cycleSelectedValue(-1);
270
+ _tui.requestRender();
271
+ return;
272
+ }
273
+ settingsList.handleInput?.(data);
274
+ _tui.requestRender();
275
+ }
276
+ };
277
+ });
278
+ }
279
+ dm.on("before_agent_start", async (event) => {
280
+ await ensureConfigLoaded();
281
+ if (level === "off")
282
+ return;
283
+ if (level === "micro") {
284
+ return {
285
+ systemPrompt: `${event.systemPrompt}
22
286
 
23
- ${t}`};return{systemPrompt:`${Z.systemPrompt}
287
+ ${MICRO_PROMPT}`
288
+ };
289
+ }
290
+ return {
291
+ systemPrompt: `${event.systemPrompt}
24
292
 
25
- ${i}
293
+ ${BASE}
26
294
 
27
- ${e[q]}
295
+ ${INTENSITY[level]}
28
296
 
29
- ${bb}`}})}export{qb as default};
297
+ ${SAFETY}`
298
+ };
299
+ });
300
+ }
@@ -1,2 +1,182 @@
1
- import{getModels as Uj}from"@duckmind/dm-ai/compat";var o$=new Set(["openai-completions","openai-responses","anthropic-messages"]);function d$($,j){let J=j.replace(/\/+$/,"");if($==="anthropic-messages")return J.replace(/\/v1$/,"");return J}function T0($,j){if(!$)return!0;for(let J of j)if(new RegExp("^"+J.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")+"$").test($))return!0;return!1}var g1=128000,p1=16000,h1={input:0,output:0,cacheRead:0,cacheWrite:0},m1=[/.*-thinking$/,/^gpt-5(\.\d+)?(-.*)?$/,/^gemini-3(\.\d+)?(-.*)?$/];function I$($){return m1.some((j)=>j.test($))}var l1=[[new Set(["zai"]),"glm","openai-completions"],[new Set(["Mistral"]),"mistral","openai-completions"],[new Set(["google","antigravity"]),"gemini","openai-completions"],[new Set(["Ollama","Ollama pay"]),"ollama","openai-completions"],[new Set(["Xiaomi"]),"mimo","openai-completions"],[new Set(["OpenRouter"]),"openrouter","openai-completions"],[new Set(["cerebras"]),"cerebras","openai-completions"]];function k$($,j){let J=($??"").trim();return J?`${J}-${j}`:j}function L0($,j){let J=(j??"").trim(),Z=$.match(/^([a-z0-9]+-)(.*)$/);if(Z)return J?`${J}-${Z[1]}`:Z[1];return $}function k0($,j){for(let[J,Z,X]of l1)if(J.has($))return{slug:k$(j,Z),api:X};return{slug:k$(j,"misc"),api:"openai-completions"}}function c1($){return $.replace(/[/:]/g," ").split(/[-_]/).filter(Boolean).map((j)=>j[0].toUpperCase()+j.slice(1)).join(" ")}function E$($){return{id:$,name:c1($),contextWindow:g1,maxTokens:p1,reasoning:I$($),cost:{...h1}}}import{execSync as s1}from"node:child_process";import{randomUUID as K0}from"node:crypto";import{chmodSync as a$,closeSync as I0,constants as i1,copyFileSync as o1,existsSync as O$,fsyncSync as d1,linkSync as r1,mkdirSync as t1,openSync as a1,readFileSync as F0,renameSync as e1,rmSync as t$,writeFileSync as $j}from"node:fs";import{homedir as K$}from"node:os";import{dirname as S$,join as U$}from"node:path";import{fileURLToPath as jj}from"node:url";var H$=!1;function r$($){H$=$}var H={info(...$){if(!H$)console.log("[dm-cliproxy]",...$)},warn(...$){if(!H$)console.warn("[dm-cliproxy]",...$)},error(...$){if(!H$)console.error("[dm-cliproxy]",...$)},debug(...$){if(!H$&&(process.env.DM_CLIPROXY_DEBUG||process.env.PI_CLIPROXYAPI_DEBUG))console.log("[dm-cliproxy]","[debug]",...$)}};var Y$=U$(K$(),".dm","agent","dm-cliproxy"),o=U$(Y$,"config.json"),Jj=[U$(K$(),".pi","agent","pi-cliproxyapi","config.json"),U$(K$(),".config","pi-cliproxyapi","config.json")];function l($){t1($,{recursive:!0,mode:448}),a$($,448)}function u($){if(O$($))a$($,384)}function Zj($){let j;try{j=JSON.parse(F0($,"utf8"))}catch{return}let J=j&&typeof j==="object"?j.proxy:void 0;for(let Z of[J?.apiKey,J?.usageKey]){if(typeof Z!=="string")continue;let X=/^!cat\s+((?:~\/|\/)[^\s;&|`]+)$/.exec(Z.trim());if(!X)continue;let z=X[1].replace(/^~/,K$());u(z)}}function Xj(){return O$(U$(S$(jj(import.meta.url)),"..",".git"))}function zj($=Jj,j=o,J=Xj()){if(O$(j))return;let X=(typeof $==="string"?[$]:[...$]).find((q)=>O$(q));if(!X)return;let z;try{u(X),Zj(X),l(S$(j)),z=`${j}.${process.pid}.${K0()}.tmp`,o1(X,z,i1.COPYFILE_EXCL),a$(z,384),r1(z,j),t$(z),z=void 0,H.info("legacy config copied to",j)}catch(q){if(z)try{t$(z,{force:!0})}catch(Y){H.warn("failed to clean temporary config:",Y)}H.warn("failed to migrate legacy config:",q)}}var F$={proxy:{endpoint:"",apiKey:""},builtinProviders:{},customProviders:{},discoveryExcludes:["*:*"],overrides:{},refreshIntervalMinutes:0,usageCacheTtlMs:30000};function f(){if(zj(),!O$(o))return H.debug("config not found, using defaults at",o),structuredClone(F$);try{l(S$(o)),u(o);let $=JSON.parse(F0(o,"utf8"));return qj($)}catch($){return H.error("failed to read config:",$,"— using defaults"),structuredClone(F$)}}function Yj($,j=o){l(S$(j));let J=`${j}.${process.pid}.${K0()}.tmp`,Z;try{Z=a1(J,"wx",384),$j(Z,JSON.stringify($,null,2)+`
2
- `,{encoding:"utf8"}),d1(Z),I0(Z),Z=void 0,e1(J,j),u(j)}finally{if(Z!==void 0)I0(Z);t$(J,{force:!0})}}function b$($){Yj($),H.info("config saved to",o)}function qj($){if(!$||typeof $!=="object")return structuredClone(F$);let j=$,J=structuredClone(F$),Z=j.proxy??{};if(J.proxy.endpoint=typeof Z.endpoint==="string"?Z.endpoint:J.proxy.endpoint,J.proxy.apiKey=typeof Z.apiKey==="string"?Z.apiKey:"",typeof Z.usageKey==="string")J.proxy.usageKey=Z.usageKey;if(typeof Z.providerPrefix==="string"&&Z.providerPrefix.trim())J.proxy.providerPrefix=Z.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((X)=>typeof X==="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 x($){if(!$)return"";let j=$.trim();if(j.startsWith("!"))try{return s1(j.slice(1),{encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()}catch(J){let Z=J?.status,X=typeof Z==="number"?` (exit ${Z})`:"";return H.warn(`failed to resolve "!" config value${X}`),""}if(j.startsWith("$"))return process.env[j.slice(1)]??"";return j}import{createHash as Qj}from"node:crypto";import{existsSync as Mj,readFileSync as Vj,writeFileSync as Ej}from"node:fs";import{dirname as S0,join as Dj}from"node:path";var b0=Dj(Y$,"discovery-cache.json");function x$($,j){return Qj("sha256").update($.trim().replace(/\/+$/,"")).update("\x00").update(j).digest("hex")}function x0($,j=b0){if(!Mj(j))return null;try{l(S0(j)),u(j);let J=JSON.parse(Vj(j,"utf8"));if(!J||typeof J.savedAt!=="number"||J.scope!==$||!J.discovery||!Array.isArray(J.discovery.builtinProviders)||!Array.isArray(J.discovery.customPool))return null;return{discovery:J.discovery,ageMs:Date.now()-J.savedAt}}catch(J){return H.warn("failed to read discovery cache:",J.message),null}}function e$($,j,J=b0){try{l(S0(J));let Z={savedAt:Date.now(),scope:j,discovery:$};Ej(J,JSON.stringify(Z),{encoding:"utf8",mode:384}),u(J)}catch(Z){H.warn("failed to write discovery cache:",Z.message)}}var D$="dm-cliproxy/0.3.5",Rj=5000;async function C0($,j){let J=new AbortController,Z=setTimeout(()=>J.abort(Error("timeout")),Rj);try{return await fetch($,{...j,signal:J.signal})}finally{clearTimeout(Z)}}function Nj($){return new URL("/.well-known/pi",new URL($).origin).toString()}async function Wj($){let j=Nj($.proxy.endpoint),J;try{J=await C0(j,{headers:{"User-Agent":D$,Accept:"application/json"}})}catch(Y){return H.debug("well-known fetch failed:",Y.message,"— falling back to /v1/models"),null}if(!J.ok)return H.debug(`well-known returned ${J.status} — falling back to /v1/models`),null;let Z;try{Z=await J.json()}catch{return H.debug("well-known returned non-JSON — falling back to /v1/models"),null}if(!Z||Z.schemaVersion!==1)return H.debug("well-known schemaVersion != 1 — falling back to /v1/models"),null;let X=[],z=Z.builtinProviders??{};for(let[Y,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??I$(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}}));X.push({name:Y,api:Q.api??"openai-responses",models:V})}let q=(Array.isArray(Z.customModelPool)?Z.customModelPool:[]).map((Y)=>({id:String(Y.id),name:typeof Y.name==="string"?Y.name:String(Y.id),reasoning:Boolean(Y.reasoning??I$(String(Y.id))),contextWindow:typeof Y.contextWindow==="number"?Y.contextWindow:128000,maxTokens:typeof Y.maxTokens==="number"?Y.maxTokens:16000,cost:Y.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},api:Y.api??"openai-completions",suggestedProvider:L0(typeof Y.suggestedProviderName==="string"?Y.suggestedProviderName:"misc",$.proxy.providerPrefix),ownedBy:typeof Y.owned_by==="string"?Y.owned_by:""}));return{source:"well-known",upstreamVersion:typeof Z?.upstream?.upstreamVersion==="string"?Z.upstream.upstreamVersion:null,builtinProviders:X,customPool:q,serverDiscoveryExcludes:Array.isArray(Z.discoveryExcludes)?Z.discoveryExcludes.filter((Y)=>typeof Y==="string"):[],upstreamTotal:typeof Z?.counts?.upstreamTotal==="number"?Z.counts.upstreamTotal:0}}async function Hj($,j){let J=new URL("/v1/models",new URL($.proxy.endpoint).origin).toString(),Z=await C0(J,{headers:{Authorization:`Bearer ${j}`,Accept:"application/json","User-Agent":D$}});if(!Z.ok)throw Error(`/v1/models returned ${Z.status}`);let X=await Z.json();if(!X?.data||!Array.isArray(X.data))return[];return X.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 Oj($,j){let J=j.discoveryExcludes,Z=new Map,X=[],z=0;for(let q of $){if(z++,T0(q.id,J))continue;if(q.owned_by==="openai"){let M=E$(q.id);w0(Z,"openai","openai-responses",P0(M));continue}if(q.owned_by==="anthropic"){let M=E$(q.id);w0(Z,"anthropic","anthropic-messages",P0(M));continue}let{slug:Y,api:Q}=k0(q.owned_by,j.proxy.providerPrefix),V=E$(q.id);X.push({id:q.id,name:V.name??q.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:Y,ownedBy:q.owned_by})}return{source:"v1-models",upstreamVersion:null,builtinProviders:Array.from(Z.values()),customPool:X,serverDiscoveryExcludes:[],upstreamTotal:z}}function w0($,j,J,Z){let X=$.get(j);if(!X)X={name:j,api:J,models:[]},$.set(j,X);X.models.push(Z)}function P0($){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 t($,j){let J=x$($.proxy.endpoint,j),Z=await Wj($);if(Z)return H.debug(`discovery via /.well-known/pi: ${Z.builtinProviders.length} builtin, ${Z.customPool.length} custom`),e$(Z,J),Z;if(!j)throw Error("well-known unavailable AND proxy apiKey is empty — cannot fall back to /v1/models. Set proxy.apiKey in config.");let X=await Hj($,j),z=Oj(X,$);return H.debug(`discovery via /v1/models: ${z.builtinProviders.length} builtin, ${z.customPool.length} custom`),e$(z,J),z}function y0($){let j=new Set;for(let J of $.builtinProviders)for(let Z of J.models)j.add(Z.id);for(let J of $.customPool)j.add(J.id);return j}var n0=new WeakMap,$0=new WeakMap;function c($){let j=($0.get($)??0)+1;return $0.set($,j),j}function w$($,j){return j===void 0||j===($0.get($)??0)}function j0($,j,J){let Z=n0.get($)??new Set;for(let X of Z){if(j.has(X))continue;$.unregisterProvider(X),J.unregistered.push(X)}n0.set($,new Set(j))}function Gj($){let j={registered:[],skipped:[],unregistered:[]};return j0($,new Set,j),j.unregistered}function J0($){return c($),Gj($)}async function d($,j,J,Z){let X={registered:[],skipped:[],unregistered:[]};if(!w$($,Z))return X.skipped.push({provider:"*",reason:"stale refresh generation"}),X;let z=new Set,q=y0(J),Y=x(j.proxy.apiKey);if(!Y)return H.warn("proxy apiKey is empty — dm.registerProvider calls will be skipped"),j0($,z,X),X;let Q=new Map;for(let M of J.builtinProviders)Q.set(M.name,M.models.map((E)=>({id:E.id,name:E.name,reasoning:E.reasoning,contextWindow:E.contextWindow,maxTokens:E.maxTokens,cost:E.cost})));for(let[M,E]of Object.entries(j.builtinProviders)){if(!w$($,Z))return X;if(!E?.enabled){X.skipped.push({provider:M,reason:"disabled"});continue}if(!Array.isArray(E.models)||E.models.length===0){X.skipped.push({provider:M,reason:"empty whitelist"});continue}let k=[];try{k=Uj(M)}catch{}let _=new Map(k.map((A)=>[A.id,A])),U=new Map((Q.get(M)??[]).map((A)=>[A.id,A])),R=[];for(let A of E.models){if(!q.has(A))continue;let B=_.get(A),P=U.get(A);if(B){R.push({id:B.id,name:B.name,reasoning:B.reasoning,contextWindow:B.contextWindow,maxTokens:B.maxTokens,cost:B.cost,input:B.input,api:B.api,thinkingLevelMap:B.thinkingLevelMap});continue}if(!P)continue;let Q$=M==="anthropic"?"anthropic-messages":E.apiOverride??"openai-responses";R.push({id:P.id,name:P.name,reasoning:P.reasoning,contextWindow:P.contextWindow,maxTokens:P.maxTokens,cost:P.cost,input:["text"],api:Q$})}if(R.length===0){X.skipped.push({provider:M,reason:"no whitelisted models present on proxy"});continue}let O=E.apiOverride??R[0].api;if(!o$.has(O)){X.skipped.push({provider:M,reason:`api "${O}" not in allowlist`});continue}let I=R.map((A)=>{let B=j.overrides[A.id]??{},P={id:A.id,name:typeof B.name==="string"?B.name:A.name,api:O,reasoning:typeof B.reasoning==="boolean"?B.reasoning:A.reasoning,input:A.input,cost:B.cost??A.cost,contextWindow:typeof B.contextWindow==="number"?B.contextWindow:A.contextWindow,maxTokens:typeof B.maxTokens==="number"?B.maxTokens:A.maxTokens};if(A.thinkingLevelMap)P.thinkingLevelMap=A.thinkingLevelMap;return P}),n={name:M,baseUrl:d$(O,j.proxy.endpoint),apiKey:Y,authHeader:!0,api:O,models:I};$.registerProvider(M,n),z.add(M),X.registered.push({provider:M,modelCount:I.length,api:O})}let V=new Map(J.customPool.map((M)=>[M.id,M]));for(let[M,E]of Object.entries(j.customProviders)){if(!w$($,Z))return X;if(!o$.has(E.api)){X.skipped.push({provider:M,reason:`api "${E.api}" not in allowlist`});continue}let k=E.models.filter((R)=>q.has(R.id));if(k.length===0){X.skipped.push({provider:M,reason:"no configured models present on proxy"});continue}let _=k.map((R)=>{let O=V.get(R.id),I=E$(R.id),n=j.overrides[R.id]??{};return{id:R.id,name:R.name??O?.name??I.name??R.id,api:E.api,reasoning:Bj(R.reasoning,O?.reasoning,I.reasoning,n.reasoning)??!1,input:["text"],cost:R.cost??O?.cost??I.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:u0(R.contextWindow,O?.contextWindow,I.contextWindow,n.contextWindow)??128000,maxTokens:u0(R.maxTokens,O?.maxTokens,I.maxTokens,n.maxTokens)??16000}}),U={name:M,baseUrl:d$(E.api,j.proxy.endpoint),apiKey:Y,authHeader:!0,api:E.api,models:_};$.registerProvider(M,U),z.add(M),X.registered.push({provider:M,modelCount:_.length,api:E.api})}if(w$($,Z))j0($,z,X);return H.debug(`applyAll: registered ${X.registered.length} providers, skipped ${X.skipped.length}, unregistered ${X.unregistered.length}`),X}function u0(...$){for(let j of $)if(typeof j==="number")return j;return}function Bj(...$){for(let j of $)if(typeof j==="boolean")return j;return}var _j=15000,G$=null;function R$(){G$=null}async function P$($,j,J={}){if(!J.force&&G$&&Date.now()-G$.fetchedAt<$.usageCacheTtlMs)return G$.doc;if(!j)throw Error("usage key not configured; set proxy.usageKey in config and rerun");let Z=new URL("/api/usage",new URL($.proxy.endpoint).origin).toString(),X=new AbortController,z=setTimeout(()=>X.abort(Error("timeout")),_j),q;try{q=await fetch(Z,{headers:{"X-Plugin-Key":j,Accept:"application/json","User-Agent":D$},signal:X.signal})}finally{clearTimeout(z)}if(!q.ok)throw Error(`/api/usage returned ${q.status}`);let Y=await q.json();if(!Y||Y.schemaVersion!==1||!Array.isArray(Y.accounts))throw Error("/api/usage returned unexpected payload shape");return G$={fetchedAt:Date.now(),doc:Y},H.debug("usage fetched, accounts:",Y.accounts.length),Y}import{getKeybindings as Y2,matchesKey as g}from"@duckmind/dm-tui";import{visibleWidth as X0}from"@duckmind/dm-tui";import{visibleWidth as v0}from"@duckmind/dm-tui";function F($,j){let J=v0($);if(J===j)return $;if(J<j)return $+" ".repeat(j-J);return Z0($,j)}function Z0($,j){if(j<=0)return"\x1B[0m";let J="",Z=0,X=0;while(X<$.length){let z=$[X];if(z==="\x1B"&&$[X+1]==="["){let Y=$.indexOf("m",X+2);if(Y>=0){J+=$.slice(X,Y+1),X=Y+1;continue}}let q=v0(z);if(Z+q>j)break;J+=z,Z+=q,X++}return`${J}\x1B[0m`}var Aj="│",Tj="╭",Lj="╮",kj="╰",Ij="╯",B$="─";function j$($,j){let J=Math.max(4,j.width);return[Kj($,j.title,j.titleColor??"accent",J),...j.lines.map((Z)=>Fj($,Z,J)),Sj($,j.footer,J)]}function Kj($,j,J,Z){let X=j??"",z=X0(X),q=Math.max(0,Z-3-z),Y=X?$.bold($.fg(J,X)):"",Q=`${Tj}${B$}${Y}${B$.repeat(q)}${Lj}`;return $.fg("borderAccent",Q)}function Fj($,j,J){let Z=Math.max(0,J-2),X=$.fg("borderAccent",Aj);return`${X}${F(j,Z)}${X}`}function Sj($,j,J){let Z=`${kj}${B$}`,X=`${Ij}`,z=3;if(!j||!j.hint&&!j.badge){let U=Math.max(0,J-3);return $.fg("borderAccent",`${Z}${B$.repeat(U)}${X}`)}let q=j.hint??"",Y=j.badge??"",Q=X0(q),V=X0(Y),M=Math.max(0,J-3-Q-V),E=$.fg("dim",q),k=Y?$.fg("muted",Y):"",_=B$.repeat(M);return`${$.fg("borderAccent",Z)}${E}${$.fg("borderAccent",_)}${k}${$.fg("borderAccent",X)}`}function C$($){return Math.max(0,$-2)}import{existsSync as f0}from"node:fs";import{homedir as bj}from"node:os";import{getKeybindings as xj,Input as z0,matchesKey as wj}from"@duckmind/dm-tui";var Pj=[{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 _$($,j=!1){let J=f(),Z={endpoint:J.proxy.endpoint??"",apiKey:J.proxy.apiKey??"",providerPrefix:J.proxy.providerPrefix??"",usageKey:J.proxy.usageKey??""},X=!1;for(let q of Pj){let Y=Z[q.label]??"",Q=q.secret?"":j?Z[q.label]??q.initialValue??"":Z[q.label]??"";if(!j&&Q)continue;let V=q.secret&&Y?{...q,required:!1}:q,M=await nj($,V,Q||q.initialValue||"");if(M===void 0){X=!0;break}let E=yj(q,Y,M);if(E===null){if(q.required){$.ui.notify(`${q.label} is required — aborted`,"warning"),X=!0;break}}Z[q.label]=E??""}if(X)return!1;let z={...J};if(z.proxy={...J.proxy,endpoint:Z.endpoint??"",apiKey:Z.apiKey??"",providerPrefix:Z.providerPrefix??""},Z.usageKey)z.proxy.usageKey=Z.usageKey;else delete z.proxy.usageKey;return b$(z),$.ui.notify(`saved to ${o}`,"info"),!0}function Cj($){if($.startsWith("!")||$.startsWith("$"))return $;if($.startsWith("~/")){let j=$.replace(/^~/,bj());return f0(j)?`!cat ${$}`:$}if($.startsWith("/"))return f0($)?`!cat ${$}`:$;return $}function yj($,j,J){let Z=J.trim();if(!Z){if($.secret&&j)return j;return $.required?null:""}if($.secret&&!$.required&&Z==="-")return"";return $.label==="providerPrefix"||$.label==="endpoint"?Z:Cj(Z)}async function nj($,j,J){return $.ui.custom((Z,X,z,q)=>uj(Z,X,j,J,q),{overlay:!0,overlayOptions:{width:100,maxHeight:"60%"}})}class g0{rawInput=new z0;displayInput=new z0;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 Z=$.codePointAt(J),X=String.fromCodePoint(Z);j+=Z<32||Z===127?X:"•",J+=X.length}return j}}function uj($,j,J,Z,X){let z=J.secret?new g0:new z0;z.setValue(Z),z.focused=!0;let q=null,Y=(Q)=>{if(!Q&&!J.required){X("");return}if(!Q&&J.required){q="required field",$.requestRender();return}if(J.validate){let V=Q.trim();if(V&&!V.startsWith("!")&&!V.startsWith("$")){let M=J.validate(V);if(M){q=M,$.requestRender();return}}}X(Q)};return z.onSubmit=Y,z.onEscape=()=>X(void 0),{render(Q){let V=C$(Q),M=z.render(V-4),E=["",` ${j.fg("dim",J.hint)}`,"",...M.map((k)=>` ${j.fg("accent",`❯ ${k}`)}`),"",q?` ${j.fg("error",`! ${q}`)}`:` ${j.fg("dim","enter = save · esc = cancel")}`];return j$(j,{width:Q,title:` setup: ${J.label} `,lines:E,footer:{hint:" enter = save · esc = cancel "}})},invalidate(){z.invalidate()},handleInput(Q){if(xj().matches(Q,"tui.select.cancel")||wj(Q,"escape")){X(void 0);return}q=null,z.handleInput(Q),$.requestRender()}}}import{visibleWidth as y$}from"@duckmind/dm-tui";function p0($,j,J,Z){let X=$.fg("borderAccent","│"),z=j.map((q,Y)=>{let Q=`${Y+1} ${q.label}`;return Y===J?$.bold($.fg("accent",` ${Q} `)):$.fg("dim",` ${Q} `)});return F(` ${z.join(X)}`,Z)}function h0($,j,J){let Z=j.join($.fg("dim"," "));return F(` ${Z}`,J)}function m0($,j){return $.fg("borderAccent","─".repeat(Math.max(0,j)))}function J$($,j,J){let Z=$.slice(j,j+J);while(Z.length<J)Z.push("");return Z}function n$($,j,J,Z,X,z){if(X<=Z)return 0;let q=Math.max(0,X-Z);if(j<z)return 0;let Y=Math.max(0,Math.min(J,q)),Q=Y+z;if($<Q)Y=Math.max(0,$-z);let V=Y+Z-1;if(j>V)Y=Math.min(q,j-(Z-1));return Y}function u$($,j,J){let Z=Math.max(0,J-j);return Math.max(0,Math.min($,Z))}import{matchesKey as Z$}from"@duckmind/dm-tui";import{existsSync as vj,readFileSync as fj}from"node:fs";import{homedir as c0}from"node:os";import{join as s0}from"node:path";var gj=s0(c0(),".dm","models.json"),pj=s0(c0(),".dm","auth.json");function l0($){if(!vj($))return null;try{return JSON.parse(fj($,"utf8"))}catch{return null}}function v$($){let j=[],J=new Set([...Object.keys($.builtinProviders),...Object.keys($.customProviders)]),Z=l0(gj);if(Z&&typeof Z==="object"){for(let z of Object.keys(Z))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 X=l0(pj);if(X&&typeof X==="object"){for(let z of Object.keys(X))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 i0($){let{theme:j,cfg:J}=$,Z=0,X=()=>{let Y=$.getDiscovery(),Q=(U)=>j.fg("success",U),V=(U)=>j.fg("error",U),M=(U)=>j.fg("dim",U),E=[];E.push(`${M("endpoint")} ${J.proxy.endpoint||V("(unset)")}`),E.push(`${M("apiKey")} ${x(J.proxy.apiKey)?Q("resolves"):V("empty after resolution")}`),E.push(`${M("usageKey")} ${J.proxy.usageKey?x(J.proxy.usageKey)?Q("resolves"):V("set but empty"):M("not configured")}`),E.push(`${M("user-agent")} ${D$}`),E.push(""),E.push(`${M("discovery")} source=${Y.source}`),E.push(`${M("upstream")} v=${Y.upstreamVersion??"(unknown)"} · ${Y.upstreamTotal} ids`);let k=Y.builtinProviders.map((U)=>`${U.name}=${U.models.length}`).join(", ")||M("(none)");E.push(`${M("built-in")} ${k}`),E.push(`${M("custom pool")} ${Y.customPool.length} models`);let _=v$(J);if(E.push(""),_.length===0)E.push(`${M("conflicts")} ${Q("none")}`);else{E.push(`${M("conflicts")}`);for(let U of _)E.push(` ${j.fg("warning",`[${U.kind}]`)} ${U.detail}`)}return E};return{id:"diagnostics",label:"Diagnostics",render:(Y,Q)=>{let V=X();return Z=u$(Z,Q,V.length),J$(V,Z,Q).map((M)=>F(` ${M}`,Y))},handleInput:(Y)=>{let Q=X().length;if(Z$(Y,"up")||Z$(Y,"k"))return Z=Math.max(0,Z-1),!0;if(Z$(Y,"down")||Z$(Y,"j"))return Z=Math.min(Math.max(0,Q-1),Z+1),!0;if(Z$(Y,"pageUp")||Z$(Y,"b"))return Z=Math.max(0,Z-10),!0;if(Z$(Y,"pageDown")||Z$(Y,"f"))return Z=Math.min(Math.max(0,Q-1),Z+10),!0;return!1},footerHint:()=>" ↑↓ scroll · r refresh discovery "}}import{matchesKey as L}from"@duckmind/dm-tui";function Y0($){let j=new Map,J=new Map;for(let X of $.builtinProviders){let z=[];for(let q of X.models)j.set(q.id,{id:q.id,name:q.name,suggestedApi:X.api,subtitle:q.name&&q.name!==q.id?q.name:void 0,ownedBy:X.name,reasoning:q.reasoning,contextWindow:q.contextWindow,maxTokens:q.maxTokens,cost:q.cost}),z.push(q.id);J.set(X.name,z)}let Z=[];for(let X of $.customPool){if(!j.has(X.id))j.set(X.id,{id:X.id,name:X.name,suggestedApi:X.api,subtitle:X.name&&X.name!==X.id?X.name:void 0,origin:X.suggestedProvider,ownedBy:X.ownedBy,reasoning:X.reasoning,contextWindow:X.contextWindow,maxTokens:X.maxTokens,cost:X.cost});Z.push(X.id)}return{byId:j,builtinModelIds:J,customPoolIds:Z}}function hj($){let j=new Map;for(let[J,Z]of Object.entries($.customProviders))for(let X of Z.models)j.set(X.id,J);return j}function q$($,j){if(j.kind==="builtin")return[...$.builtinProviders[j.name]?.models??[]];return $.customProviders[j.name]?.models.map((J)=>J.id)??[]}function o0($,j,J){if(j.kind==="builtin"){let z=J.builtinModelIds.get(j.name)??[],q=new Set(q$($,j));return z.filter((Y)=>!q.has(Y))}let Z=hj($),X=new Set(q$($,j));return J.customPoolIds.filter((z)=>{if(X.has(z))return!1;let q=Z.get(z);return q===void 0||q===j.name})}function mj($){return{id:$.id,name:$.name,contextWindow:$.contextWindow,maxTokens:$.maxTokens,reasoning:$.reasoning,cost:$.cost}}function d0($,j,J){if(j.kind==="builtin"){let X=$.builtinProviders[j.name]??{enabled:!0,models:[]},z=new Set(X.models);z.add(J.id),$.builtinProviders[j.name]={enabled:!0,apiOverride:X.apiOverride??null,models:Array.from(z).sort()};return}for(let[X,z]of Object.entries($.customProviders)){if(X===j.name)continue;let q=z.models.findIndex((Y)=>Y.id===J.id);if(q>=0)z.models.splice(q,1)}let Z=$.customProviders[j.name]??{api:j.api,models:[]};if(!Z.models.some((X)=>X.id===J.id))Z.models.push(mj(J));$.customProviders[j.name]=Z}function r0($,j,J){if(j.kind==="builtin"){let X=$.builtinProviders[j.name];if(!X)return;X.models=X.models.filter((z)=>z!==J),X.enabled=X.models.length>0;return}let Z=$.customProviders[j.name];if(!Z)return;Z.models=Z.models.filter((X)=>X.id!==J)}function q0($,j){if($===j)return!0;let J=["openai-completions","openai-responses"];if(J.includes($)&&J.includes(j))return!0;return!1}function t0($,j){let J=new Map;for(let Z of $){let X=j.byId.get(Z),z=X?.ownedBy||X?.origin||"misc",q=J.get(z)??[];q.push(Z),J.set(z,q)}return Array.from(J.entries()).sort((Z,X)=>Z[0].localeCompare(X[0])).map(([Z,X])=>({label:Z,ids:X}))}function a0($,j,J){let Z=J.trim().toLowerCase();if(!Z)return $;return $.filter((X)=>{if(X.toLowerCase().includes(Z))return!0;let z=j.byId.get(X)?.name;return z?z.toLowerCase().includes(Z):!1})}import{getModels as lj,getProviders as cj}from"@duckmind/dm-ai/compat";function Q0($,j){let J=[],Z=new Set(j.builtinModelIds.keys()),X=new Set(cj()),z=new Set([...Z,...X]);for(let q of Array.from(z).sort()){let Y=j.builtinModelIds.get(q);if(!Y||Y.length===0)continue;let Q;try{let M=lj(q).find((E)=>Y.includes(E.id));if(M)Q=M.api}catch{}if(!Q){let V=Y[0];Q=j.byId.get(V)?.suggestedApi??"openai-responses"}if(J.push({kind:"builtin",name:q,api:Q,subtitle:`built-in · ${Q}`}),!$.builtinProviders[q])$.builtinProviders[q]={enabled:!1,models:[]}}for(let q of Object.keys($.customProviders).sort()){let Y=$.customProviders[q];J.push({kind:"custom",name:q,api:Y.api,subtitle:`custom · ${Y.api}`})}return J}import{getKeybindings as sj,matchesKey as N$}from"@duckmind/dm-tui";async function e0($,j){return $.ui.custom((J,Z,X,z)=>ij(Z,`Remove custom group “${j}”?`,"All model assignments in this group will be discarded.",z),{overlay:!0,overlayOptions:{width:80,maxHeight:"40%"}})}function ij($,j,J,Z){return{render(X){return j$($,{width:X,title:" confirm ",titleColor:"error",lines:["",` ${$.bold(j)}`,` ${$.fg("dim",J)}`,""],footer:{hint:" y / enter = remove · n / esc = cancel "}})},invalidate(){},handleInput(X){if(sj().matches(X,"tui.select.cancel")||N$(X,"escape")||N$(X,"n")||N$(X,"q")){Z(!1);return}if(N$(X,"y")||N$(X,"enter")||N$(X,"return")){Z(!0);return}}}}import{getKeybindings as oj,Input as dj,matchesKey as rj}from"@duckmind/dm-tui";async function $1($,j){let J=k$(j,"group");return $.ui.custom((Z,X,z,q)=>tj(Z,X,J,q),{overlay:!0,overlayOptions:{width:80,maxHeight:"40%"}})}function tj($,j,J,Z){let X=new dj;if(J)X.setValue(J);X.focused=!0;let z=null;return X.onSubmit=(q)=>{let Y=q.trim();if(!/^[a-z0-9][a-z0-9._-]*$/i.test(Y)){z="letters / digits / dot / dash / underscore only",$.requestRender();return}Z(Y)},X.onEscape=()=>Z(null),{render(q){let Y=C$(q),Q=J?`name shown in /model picker, e.g. ${J}`:"letters / digits / dot / dash / underscore",V=X.render(Y-4),M=["",` ${j.fg("dim",Q)}`,"",...V.map((E)=>` ${j.fg("accent",`❯ ${E}`)}`),"",z?` ${j.fg("error",`! ${z}`)}`:""];return j$(j,{width:q,title:" new custom group ",lines:M,footer:{hint:" enter = create · esc = cancel "}})},invalidate(){X.invalidate()},handleInput(q){if(oj().matches(q,"tui.select.cancel")||rj(q,"escape")){Z(null);return}z=null,X.handleInput(q),$.requestRender()}}}import{visibleWidth as aj}from"@duckmind/dm-tui";function M0($,j,J){if(!j)return" ";if(J)return $.fg("accent","▶ ");return $.fg("muted","▸ ")}function j1($,j){let J=aj($);if(J===j)return $;if(J>j)return Z0($,j);return F($,j)}function J1($,j,J){let{theme:Z,width:X,isCursor:z,isFocused:q}=J,Y=M0(Z,z,q),Q=$.kind==="builtin"?j.builtinProviders[$.name]?.models.length??0:j.customProviders[$.name]?.models.length??0,V=$.kind==="builtin"?Z.fg("accent","B"):Z.fg("warning","C"),M=`${Z.fg("dim","[")}${V}${Z.fg("dim","]")}`,E=Q>0?Z.fg("success","●"):Z.fg("dim","○"),k=Q>0?Z.fg("success",String(Q).padStart(2)):Z.fg("dim"," 0"),_=z&&q?Z.bold(Z.fg("accent",$.name)):z?Z.bold($.name):$.name,U=Z.fg("dim",$.api),R=`${Y}${M} ${_} ${E}${k} ${U}`;return j1(R,X)}function Z1($,j,J,Z){let X=M0($,j,J),z=j&&J?$.bold($.fg("accent","+ new custom group…")):$.fg("accent","+ new custom group…");return F(`${X}${z}`,Z)}function V0($,j,J,Z,X){let{theme:z,width:q,isCursor:Y,isFocused:Q}=X,V=M0(z,Y,Q),M=J==="assigned"?z.fg("success","☑"):z.fg("dim","☐"),E=Z?` ${z.fg("warning","⚠")}`:"",k=Y&&Q?z.fg("accent",$):$,_=j?.reasoning?` ${z.fg("accent","✨")}`:"",U=j?z.fg("muted",` · ${j.suggestedApi}`):z.fg("error"," · not on proxy"),R=`${V}${M} ${k}${E}${_}${U}`;return j1(R,q)}function X1($,j,J){let Z=`${$.fg("borderAccent","──")} ${$.fg("warning",j)} ${$.fg("borderAccent","──")}`;return F(` ${Z}`,J)}function E0($,j,J){return F(` ${$.fg("dim",j)}`,J)}function z1($,j,J,Z){let X=Z?$.bold($.fg("accent","❚ ")):$.fg("dim"," ");return F(`${X}${j}`,J)}function Y1($){let{tui:j,theme:J,ctx:Z,cfg:X}=$,z=$.onChange??(()=>{}),q=Y0($.getDiscovery()),Y=Q0(X,q),Q="providers",V=0,M=0,E=0,k=0,_=0,U=0,R="",O=!1,I=()=>{if(Y.length===0)return null;let D=Math.max(0,Math.min(V,Y.length-1));return Y[D]??null},n=(D)=>{let G=a0(o0(X,D,q),q,R);return t0(G,q)},A=(D)=>n(D).flatMap((G)=>G.ids),B=()=>{Y=Q0(X,q);let D=Math.max(0,Y.length);if(V>D)V=D;let G=I();if(G){let w=q$(X,G).length,T=A(G).length;if(M>=w)M=Math.max(0,w-1);if(E>=T)E=Math.max(0,T-1)}},P=()=>{q=Y0($.getDiscovery()),B()},Q$=(D)=>{let G=["providers","assigned","pool"],w=G.indexOf(Q);Q=D?G[(w-1+G.length)%G.length]:G[(w+1)%G.length]},W=(D)=>{if(Q==="providers"){let T=Y.length+1;V=Math.max(0,Math.min(V+D,T-1));return}let G=I();if(!G)return;if(Q==="assigned"){let T=q$(X,G).length;if(T===0)return;M=Math.max(0,Math.min(M+D,T-1));return}let w=A(G).length;if(w===0)return;E=Math.max(0,Math.min(E+D,w-1))},b=async()=>{if(Q==="providers"){if(V===Y.length){let T=await $1(Z,X.proxy.providerPrefix);if(T&&!X.customProviders[T]){if(X.customProviders[T]={api:"openai-completions",models:[]},z(),B(),V=Y.findIndex((S)=>S.kind==="custom"&&S.name===T),V<0)V=Y.length-1;Q="pool"}j.requestRender();return}Q="pool";return}let D=I();if(!D)return;if(Q==="assigned"){let T=q$(X,D)[M];if(!T)return;r0(X,D,T),z(),B();return}let G=A(D)[E];if(!G)return;let w=q.byId.get(G);if(!w)return;d0(X,D,w),z(),B()},s=async()=>{if(Q!=="providers")return;let D=I();if(!D||D.kind!=="custom")return;if(!await e0(Z,D.name)){j.requestRender();return}if(delete X.customProviders[D.name],z(),B(),V>=Y.length)V=Math.max(0,Y.length-1);j.requestRender()},p=(D,G)=>i(D,G);function i(D,G){let w=Math.max(70,D),T=Math.min(56,Math.max(34,Math.floor(w*0.4))),S=w-T-1,a=Math.max(8,G),e=Math.max(5,Math.floor((a-1)/2)),R0=a-e,r=I(),W$=0,m$=0,l$=0,N0=0,c$=0,W0=0,X$=[];X$.push(M$(" providers ",T,Q==="providers"));for(let K=0;K<Y.length;K++){let h=Y[K],v=Q==="providers"&&K===V;if(v)W$=X$.length,m$=W$;X$.push(J1(h,X,{theme:J,width:T,isCursor:v,isFocused:Q==="providers"}))}{let K=Q==="providers"&&V===Y.length;if(K)W$=X$.length,m$=W$;X$.push(Z1(J,K,Q==="providers",T))}let z$=[],y1=r?`assigned to ${J.bold(r.name)} ${J.fg("dim",`· ${r.api}`)}`:J.fg("dim","no provider selected");if(z$.push(M$(" assigned ",S,Q==="assigned")),z$.push(z1(J,y1,S,Q==="assigned")),r){let K=q$(X,r);if(K.length===0)z$.push(E0(J,"(nothing assigned yet)",S));for(let h=0;h<K.length;h++){let v=K[h],m=q.byId.get(v),L$=Q==="assigned"&&h===M;if(L$)l$=z$.length,N0=l$;let V$=m?!q0(r.api,m.suggestedApi):!1;z$.push(V0(v,m,"assigned",V$,{theme:J,width:S,isCursor:L$,isFocused:Q==="assigned"}))}}let $$=[];if($$.push(C(S,Q==="pool")),r){let K=n(r);if(K.reduce((v,m)=>v+m.ids.length,0)===0)$$.push(E0(J,R?`(no matches for "${R}")`:"(no models available)",S));else{let v=0;for(let m of K){let L$=$$.length;$$.push(X1(J,m.label,S));for(let V$=0;V$<m.ids.length;V$++){let _0=m.ids[V$],i$=q.byId.get(_0),A0=Q==="pool"&&v===E;if(A0)c$=$$.length,W0=V$===0?L$:c$;let f1=i$?!q0(r.api,i$.suggestedApi):!1;$$.push(V0(_0,i$,"pool",f1,{theme:J,width:S,isCursor:A0,isFocused:Q==="pool"})),v++}}}}let n1=Math.max(1,R0-1),H0=n$(m$,W$,k,a,X$.length,1),O0=n$(Q==="assigned"?N0:0,Q==="assigned"?l$:0,_,e,z$.length,2),U0=n$(Q==="pool"?W0:0,Q==="pool"?c$:0,U,n1,$$.length,1);k=H0,_=O0,U=U0;let G0=J$(X$,H0,a),u1=J$(z$,O0,e),v1=J$($$,U0,R0),B0=J.fg("borderAccent","│"),s$=[];for(let K=0;K<a;K++){let h=F(G0[K]??"",T),v=K<e?u1[K]:v1[K-e],m=F(v,S);s$.push(`${h}${B0}${m}`)}let T$=e;if(T$>0&&T$<a){let K=F(G0[T$]??"",T),h=J.fg("borderAccent","─".repeat(S));s$[T$]=`${K}${B0}${h}`}return s$}function M$(D,G,w){let T=w?J.bold(J.fg("accent",D)):J.fg("muted",D),S=J.fg("borderAccent","─".repeat(Math.max(0,G-y$(D)-1)));return F(`${T}${S}`,G)}function C(D,G){let T=G?J.bold(J.fg("accent"," available pool ")):J.fg("muted"," available pool "),S="";if(O)S=`${J.fg("dim"," /")}${J.fg("accent",R)}${J.fg("accent","█")} `;else if(R)S=`${J.fg("dim"," /")}${J.fg("warning",R)} `;let a=y$(" available pool ")+y$(S),e=J.fg("borderAccent","─".repeat(Math.max(0,D-a-1)));return F(`${T}${S}${e}`,D)}function A$(D){if(O){if(L(D,"escape"))return R="",O=!1,E=0,!0;if(L(D,"enter")||L(D,"return"))return O=!1,!0;if(L(D,"backspace")||L(D,"delete"))return R=R.slice(0,-1),E=0,!0;if(D.length===1&&D>=" "&&D!=="")return R+=D,E=0,!0;return!0}if(L(D,"tab"))return Q$(!1),!0;if(L(D,"shift+tab"))return Q$(!0),!0;if(L(D,"/"))return Q="pool",O=!0,!0;if(L(D,"enter")||L(D,"return")||L(D,"space"))return b().then(()=>!0);if(Q==="providers"&&(L(D,"d")||L(D,"delete")||L(D,"backspace")))return s().then(()=>!0);if(L(D,"up")||L(D,"k"))return W(-1),!0;if(L(D,"down")||L(D,"j"))return W(1),!0;if(L(D,"pageUp")||L(D,"b"))return W(-8),!0;if(L(D,"pageDown")||L(D,"f"))return W(8),!0;if(L(D,"left")||L(D,"h"))return Q="providers",!0;if(L(D,"right")||L(D,"l")){if(Q==="providers")Q="assigned";else if(Q==="assigned")Q="pool";return!0}return!1}function C1(){if(O)return" type to filter · ↵ apply · esc clear ";return" tab ↔ panel · ↑↓ nav · ↵ move · / filter · d remove group "}return{id:"models",label:"Models",render:p,handleInput:A$,footerHint:C1,rebuild:P}}import{matchesKey as y}from"@duckmind/dm-tui";var N={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 q1($,j={}){let J=[],Z=new Map,X=0;for(let z of $.accounts){if(!j.showDisabled&&z.disabled){X++;continue}let q=Z.get(z.provider)??[];q.push(z),Z.set(z.provider,q)}if(J.push(`${N.dim}generated ${$.generatedAt}${N.reset}`),$.unsupportedProviders.length>0)J.push(`${N.dim}providers without quota lookup:${N.reset} ${$.unsupportedProviders.join(", ")}`);if(X>0)J.push(`${N.dim}hidden disabled accounts:${N.reset} ${X} ${N.dim2}(press d to show)${N.reset}`);for(let[z,q]of Array.from(Z.entries()).sort()){J.push(""),J.push(`${N.bold}${N.accent}▌ ${z}${N.reset}`);for(let Y of q){if(J.push($2(Y)),!Y.supported){J.push(` ${N.dim2}(provider not quota-aware)${N.reset}`);continue}if(Y.error){if(j.verbose)for(let V of ej(Y.error,88))J.push(` ${N.red}${V}${N.reset}`);continue}let Q=Y.groups??[];if(Q.length===0){J.push(` ${N.dim2}(no active quota windows)${N.reset}`);continue}for(let V of Q)J.push(J2(V))}}return J.push(""),J.push(`${N.dim}legend:${N.reset} ${Q1} ok ${M1} error/unavailable ${V1} disabled ${N.green}✓N${N.reset}/${N.red}✗N${N.reset} request counters ${N.dim}v = verbose, d = show disabled${N.reset}`),J}function ej($,j){if($.length<=j)return[$];let J=[],Z=$;while(Z.length>j){let X=Z.lastIndexOf(" ",j);if(X<j*0.6)X=j;J.push(Z.slice(0,X)),Z=Z.slice(X).replace(/^\s+/,"")}if(Z)J.push(Z);return J}function $2($){let j=j2($),J=`${N.green}✓${$.success}${N.reset} ${N.red}✗${$.failed}${N.reset}`,Z=R1($.label,36);return` ${j} ${N.bold}${Z}${N.reset} ${J}`}var Q1=`${N.green}●${N.reset}`,M1=`${N.yellow}⚠${N.reset}`,V1=`${N.dim2}⊘${N.reset}`;function j2($){if($.disabled)return V1;if($.unavailable||$.error||$.status&&$.status!=="active")return M1;return Q1}function J2($){let j=D1($.remainingFraction),J=Math.round(j*100),Z=Z2(j),X=$.resetTime?` ${N.dim}reset ${z2($.resetTime)}${N.reset}`:"",z=R1($.label,28).padEnd(28," ");return` ${Z} ${X2(J,j)} ${z}${X}`}function Z2($){let j=D1($),J=Math.round(j*18),Z=E1(j),X=N.dim2,z="█".repeat(J),q="░".repeat(18-J);return`${X}[${Z}${z}${X}${q}]${N.reset}`}function X2($,j){return`${E1(j)}${String($).padStart(3)}%${N.reset}`}function E1($){if($>=0.4)return N.green;if($>=0.15)return N.yellow;return N.red}function z2($){let j=Date.parse($);if(!Number.isFinite(j))return $;let J=j-Date.now();if(J<=0)return"now";let Z=Math.round(J/60000);if(Z<60)return`${Z}m`;let X=Math.round(Z/60);if(X<48)return`${X}h`;return`${Math.round(X/24)}d`}function D1($){return Math.max(0,Math.min(1,$))}function R1($,j){if($.length<=j)return $;return`${$.slice(0,j-1)}…`}function N1($){let{tui:j,theme:J,cfg:Z}=$,X="idle",z=null,q="",Y=0,Q=!1,V=!1,M=(R)=>{X="loading",j.requestRender();let O=x(Z.proxy.usageKey);P$(Z,O,{force:R}).then((I)=>{z=I,X="ready",Y=0,j.requestRender()}).catch((I)=>{q=I.message,X="error",j.requestRender()})},E=()=>{if(X==="idle"||X==="loading")return[J.fg("dim"," loading usage…")];if(X==="error")return[J.fg("error",` usage failed: ${q}`)];if(!z)return[J.fg("dim"," (no data)")];return q1(z,{showDisabled:Q,verbose:V})};return{id:"usage",label:"Usage",render:(R,O)=>{let I=E();return Y=u$(Y,O,I.length),J$(I,Y,O).map((n)=>F(` ${n}`,R))},handleInput:(R)=>{if(y(R,"d"))return Q=!Q,Y=0,!0;if(y(R,"v"))return V=!V,Y=0,!0;let O=E().length;if(y(R,"up")||y(R,"k"))return Y=Math.max(0,Y-1),!0;if(y(R,"down")||y(R,"j"))return Y=Math.min(Math.max(0,O-1),Y+1),!0;if(y(R,"pageUp")||y(R,"b"))return Y=Math.max(0,Y-10),!0;if(y(R,"pageDown")||y(R,"f"))return Y=Math.min(Math.max(0,O-1),Y+10),!0;if(y(R,"home")||y(R,"g"))return Y=0,!0;if(y(R,"end")||y(R,"shift+g"))return Y=Math.max(0,O-1),!0;return!1},footerHint:()=>{let R=Q?J.fg("success","[d disabled]"):"d disabled",O=V?J.fg("success","[v verbose]"):"v verbose";return` ↑↓ scroll · ${R} · ${O} `},onActivate:()=>{if(X==="idle")M(!1)},reload:()=>{if(R$(),X!=="idle")M(!0)}}}function W1($){let{dm:j,ctx:J,tui:Z,theme:X,cfg:z,done:q}=$,Y=$.discovery,Q=!1,V="",M=Y1({tui:Z,theme:X,ctx:J,cfg:z,getDiscovery:()=>Y,onChange:()=>{Q=!0,V=""}}),E=N1({tui:Z,theme:X,cfg:z}),k=i0({theme:X,cfg:z,getDiscovery:()=>Y}),_=[M,E,k],U=0,R=()=>_[U],O=(W)=>{if(W<0||W>=_.length||W===U)return;U=W,R().onActivate?.(),Z.requestRender()},I=()=>{V=X.fg("dim","saving…"),b$(z),Z.requestRender();let W=c(j);d(j,z,Y,W).then((b)=>{Q=!1,V=X.fg("success",`✓ settings saved · ${b.registered.length} registered, ${b.skipped.length} skipped`),Z.requestRender()}).catch((b)=>{V=X.fg("error",`save failed: ${b.message}`),Z.requestRender()})},n=async()=>{V=X.fg("dim","refreshing…"),Z.requestRender();try{let W=c(j),b=x(z.proxy.apiKey);Y=await t(z,b),R$(),M.rebuild(),E.reload();let s=await d(j,z,Y,W);V=X.fg("success",`✓ refreshed · ${s.registered.length} providers (${Y.source})`)}catch(W){V=X.fg("error",`refresh failed: ${W.message}`)}Z.requestRender()},A=async()=>{if(!await _$(J,!0)){Z.requestRender();return}let b=f();z.proxy={...z.proxy,...b.proxy},await n()},B=()=>{let W=z.proxy.endpoint||"(unset)",b=W.length>40?`…${W.slice(-39)}`:W,s=Boolean(x(z.proxy.apiKey)),p=0,i=0;for(let C of Object.values(z.builtinProviders)){if(C.enabled&&C.models.length>0)p++;i+=C.models.length}for(let C of Object.values(z.customProviders)){if(C.models.length>0)p++;i+=C.models.length}let M$=V?V:Q?X.fg("warning","● unsaved — press s to save"):X.fg("dim","✓ saved");return[`${X.fg("dim","endpoint")} ${b}`,`${X.fg("dim","key")} ${s?X.fg("success","✓"):X.fg("error","✗")}`,`${X.fg("dim","providers")} ${p}`,`${X.fg("dim","models")} ${i}`,M$]},P=(W)=>{let b=Z.rows??40,p=Math.max(16,Math.min(b-6,38))-2,i=Math.max(72,W-2),M$=Math.max(6,p-3),C=[];return C.push(h0(X,B(),i)),C.push(p0(X,_.map((A$)=>({id:A$.id,label:A$.label})),U,i)),C.push(m0(X,i)),C.push(...R().render(i,M$)),j$(X,{width:W,title:" /cliproxy ",lines:C,footer:{hint:R().footerHint(),badge:" [ ] tab · r refresh · e setup · s save · q close "}})},Q$=(W)=>{if(Y2().matches(W,"tui.select.cancel")||g(W,"q")||g(W,"escape"))return q(),!0;if(g(W,"]")||g(W,"tab"))return O((U+1)%_.length),!0;if(g(W,"[")||g(W,"shift+tab"))return O((U-1+_.length)%_.length),!0;if(g(W,"1"))return O(0),!0;if(g(W,"2"))return O(1),!0;if(g(W,"3"))return O(2),!0;if(g(W,"r"))return n().then(()=>!0);if(g(W,"e"))return A().then(()=>!0);if(g(W,"s"))return I(),!0;return!1};return{render:P,invalidate(){},handleInput(W){let b=R().handleInput(W);if(b instanceof Promise){V="",b.then((p)=>{if(p)Z.requestRender()});return}if(b){V="",Z.requestRender();return}let s=Q$(W);if(s instanceof Promise)s.then((p)=>{if(p)Z.requestRender()});else if(s)Z.requestRender()}}}async function H1($,j,J,Z){if(!j.hasUI){j.ui.notify("interactive UI required for /cliproxy","warning");return}let X=structuredClone(J);r$(!0);try{await j.ui.custom((z,q,Y,Q)=>W1({dm:$,ctx:j,tui:z,theme:q,cfg:X,discovery:Z,done:Q}),{overlay:!0,overlayOptions:{width:170,maxHeight:"94%"}})}finally{r$(!1)}}function O1($){$.registerCommand("cliproxy",{description:"Manage proxy models, usage, and diagnostics in one hub overlay",handler:q2.bind(null,$)}),$.registerCommand("cliproxy-setup",{description:"Set endpoint, API key, and (optional) usage key for the proxy",handler:Q2.bind(null,$)})}async function q2($,j,J){let Z=f();if(!Z.proxy.endpoint||!x(Z.proxy.apiKey)){if(J.ui.notify("endpoint or API key not set — launching setup first","info"),!await _$(J,!0))return}let X=f(),z=x(X.proxy.apiKey),q;try{q=await t(X,z)}catch(Y){J.ui.notify(`discovery failed: ${Y.message}`,"error");return}await H1($,J,X,q)}async function Q2($,j,J){if(!await _$(J,!0))return;let X=f();try{let z=c($),q=await t(X,x(X.proxy.apiKey)),Y=await d($,X,q,z);R$(),J.ui.notify(`setup ok · ${Y.registered.length} providers registered (source=${q.source})`,"info")}catch(z){J.ui.notify(`setup saved, but apply failed: ${z.message}`,"warning")}}import{createHash as M2}from"node:crypto";import{existsSync as U1,readFileSync as G1,statSync as V2,unlinkSync as B1,writeFileSync as _1}from"node:fs";import{dirname as f$,join as g$}from"node:path";var A1=g$(Y$,"usage-cache.json"),T1=g$(Y$,"usage-cache.lock"),E2=120000,D2=30000;function L1($,j,J=Y$){let Z=M2("sha256").update($.trim().replace(/\/+$/,"")).update("\x00").update(j).digest("hex").slice(0,24);return{cachePath:g$(J,`usage-cache-${Z}.json`),lockPath:g$(J,`usage-cache-${Z}.lock`)}}function k1($=A1){if(!U1($))return null;try{l(f$($)),u($);let j=JSON.parse(G1($,"utf8"));if(!j||typeof j.fetchedAt!=="number"||!j.doc||!Array.isArray(j.doc.accounts))return null;return{doc:j.doc,ageMs:Date.now()-j.fetchedAt}}catch(j){return H.warn("failed to read usage cache:",j.message),null}}function I1($){return $<E2}function K1($,j=A1){try{l(f$(j));let J={fetchedAt:Date.now(),doc:$};_1(j,JSON.stringify(J),{encoding:"utf8",mode:384}),u(j)}catch(J){H.warn("failed to write usage cache:",J.message)}}function F1($=T1){let j=`${process.pid}@${Date.now()}`;if(U1($))try{l(f$($)),u($);let J=V2($);if(Date.now()-J.mtimeMs>D2)H.debug("removing stale usage lock"),B1($);else return null}catch{return null}try{return l(f$($)),_1($,j,{flag:"wx",mode:384}),u($),j}catch{return null}}function S1($,j=T1){try{if(u(j),G1(j,"utf8").trim()===$)return B1(j),!0;return!1}catch{return!1}}var D0=["⡀","⠂","⣀","⣄","⣤","⣶","⣷","⣾"];function R2($){if($==="anthropic")return"claude";if($==="openai")return"codex";return null}function N2($){return!$.disabled&&!$.unavailable&&$.supported&&Array.isArray($.groups)&&$.groups.length>0}function W2($){let j=`${$.id} ${$.label??""}`.toLowerCase();return j.includes("5h")||j.includes("five-hour")||j.includes("five hour")}function H2($){let j=`${$.id} ${$.label??""}`.toLowerCase();return j.includes("7d")||j.includes("seven-day")||j.includes("seven day")}function b1($,j){let J=-1;for(let Z of $){if(!N2(Z))continue;let X=1/0;for(let z of Z.groups??[])if(j(z)){if(z.remainingFraction<X)X=z.remainingFraction}if(X!==1/0&&X>J)J=X}if(J<0)return null;return{fraction:O2(J)}}function O2($){return Math.max(0,Math.min(1,$))}function x1($){let j=Math.min(D0.length-1,Math.floor($*D0.length));return D0[j]}function p$($){if($>=0.7)return"success";if($>=0.3)return"warning";return"error"}function w1($){return Math.round($*100)}function P1($,j,J){let Z=R2(j);if(!Z)return null;let X=$.accounts.filter((Q)=>Q.provider===Z);if(X.length===0)return null;let z=b1(X,W2),q=b1(X,H2);if(!z&&!q)return null;let Y=[J.fg("dim","\uDB81\uDE27")];if(z)Y.push(`${J.fg("dim","5h")} ${J.fg(p$(z.fraction),x1(z.fraction))} ${J.fg(p$(z.fraction),String(w1(z.fraction)))}`);if(q)Y.push(`${J.fg("dim","7d")} ${J.fg(p$(q.fraction),x1(q.fraction))} ${J.fg(p$(q.fraction),String(w1(q.fraction)))}`);return Y.join(" ")}var h$="0quota",U2=5000;async function G2($,j,J={}){let{cachePath:Z,lockPath:X}=L1($.proxy.endpoint,j),z=k1(Z);if(z&&I1(z.ageMs))return z.doc;if(J.readOnly)return z?.doc??null;let q=F1(X);if(!q)return z?.doc??null;try{let Y=await P$($,j,{force:!0});return K1(Y,Z),Y}catch(Y){return H.debug("usage fetch failed in shared cache:",Y.message),z?.doc??null}finally{S1(q,X)}}async function B2($,j,J,Z,X={}){if(!Z){J.setStatus(h$,void 0);return}if(!j){J.setStatus(h$,void 0);return}let z=await G2($,j,X);if(!z){J.setStatus(h$,void 0);return}let q=P1(z,Z.provider,J.theme);J.setStatus(h$,q??void 0)}function _2($,j={},J={active:!0,quotaGeneration:0}){let Z={loadConfig:f,resolveConfigValue:x,refreshQuotaStatus:B2,now:Date.now,...j},X=0,z=async(q,Y=!1)=>{if(!q.hasUI||!J.active)return;let Q=++J.quotaGeneration,V=Z.loadConfig(),M=Z.resolveConfigValue(V.proxy.usageKey),E={theme:q.ui.theme,setStatus(k,_){if(J.active&&Q===J.quotaGeneration)q.ui.setStatus(k,_)}};await Z.refreshQuotaStatus(V,M,E,q.model,Y?{readOnly:!0}:{})};$.on("session_start",async(q,Y)=>{await z(Y)}),$.on("model_select",async(q,Y)=>{await z(Y,!0)}),$.on("turn_end",async(q,Y)=>{let Q=Z.now(),V=Q-X<U2;if(!V)X=Q;await z(Y,V)})}async function A2($){O1($);let j={active:!0,quotaGeneration:0};_2($,{},j);let J;$.on("session_shutdown",()=>{if(j.active=!1,j.quotaGeneration++,J!==void 0)clearInterval(J);J0($)});let Z=f(),X=x(Z.proxy.apiKey);if(!X){H.debug("apiKey is empty after resolution — skipping initial apply. Run /cliproxy-setup to configure."),J0($);return}let z=v$(Z);for(let q of z)H.warn(`conflict (${q.kind}): ${q.detail}`);try{let q=c($),Y=x0(x$(Z.proxy.endpoint,X));if(Y)H.debug(`discovery from cache (age ${Math.round(Y.ageMs/1000)}s): ${Y.discovery.builtinProviders.length} builtin, ${Y.discovery.customPool.length} custom`),await d($,Z,Y.discovery,q),(async()=>{let Q=c($);try{let V=await t(Z,X);if(!j.active)return;await d($,Z,V,Q),H.debug("discovery revalidated from network")}catch(V){H.warn("background discovery revalidate failed:",V.message)}})();else{let Q=c($),V=await t(Z,X);if(!j.active)return;await d($,Z,V,Q)}}catch(q){H.error("initial apply failed:",q.message)}if(Z.refreshIntervalMinutes>0){let q=Z.refreshIntervalMinutes*60000;J=setInterval(()=>{(async()=>{let Y=c($);try{if(!j.active)return;let Q=f(),V=x(Q.proxy.apiKey);if(!V)return;let M=await t(Q,V);if(!j.active)return;await d($,Q,M,Y),H.debug("background refresh ok")}catch(Q){H.warn("background refresh failed:",Q.message)}})()},q),H.debug(`background refresh every ${Z.refreshIntervalMinutes}m`)}}export{_2 as registerQuotaHooks,A2 as default};
1
+ import {
2
+ applyAll,
3
+ beginProviderRefresh,
4
+ invalidateProviderLifecycle
5
+ } from "./src/apply.js";
6
+ import {
7
+ discoveryCacheScope,
8
+ readDiscoveryCache
9
+ } from "./src/cache.js";
10
+ import { registerCommands } from "./src/commands.js";
11
+ import { loadConfig, resolveConfigValue } from "./src/config.js";
12
+ import { detectConflicts } from "./src/conflicts.js";
13
+ import { fetchDiscovery } from "./src/fetch-models.js";
14
+ import { fetchUsage } from "./src/fetch-usage.js";
15
+ import { log } from "./src/log.js";
16
+ import {
17
+ isUsageFresh,
18
+ readUsageCache,
19
+ releaseUsageLock,
20
+ tryAcquireUsageLock,
21
+ usageCachePaths,
22
+ writeUsageCache
23
+ } from "./src/usage-shared-cache.js";
24
+ import { renderQuotaSegment } from "./src/status-quota.js";
25
+ const QUOTA_STATUS_KEY = "0quota";
26
+ const TURN_FETCH_DEBOUNCE_MS = 5000;
27
+ async function loadUsageCached(cfg, resolvedUsageKey, opts = {}) {
28
+ const { cachePath, lockPath } = usageCachePaths(cfg.proxy.endpoint, resolvedUsageKey);
29
+ const cached = readUsageCache(cachePath);
30
+ if (cached && isUsageFresh(cached.ageMs))
31
+ return cached.doc;
32
+ if (opts.readOnly)
33
+ return cached?.doc ?? null;
34
+ const token = tryAcquireUsageLock(lockPath);
35
+ if (!token) {
36
+ return cached?.doc ?? null;
37
+ }
38
+ try {
39
+ const doc = await fetchUsage(cfg, resolvedUsageKey, { force: true });
40
+ writeUsageCache(doc, cachePath);
41
+ return doc;
42
+ } catch (e) {
43
+ log.debug("usage fetch failed in shared cache:", e.message);
44
+ return cached?.doc ?? null;
45
+ } finally {
46
+ releaseUsageLock(token, lockPath);
47
+ }
48
+ }
49
+ async function refreshQuotaStatus(cfg, resolvedUsageKey, ui, model, opts = {}) {
50
+ if (!model) {
51
+ ui.setStatus(QUOTA_STATUS_KEY, undefined);
52
+ return;
53
+ }
54
+ if (!resolvedUsageKey) {
55
+ ui.setStatus(QUOTA_STATUS_KEY, undefined);
56
+ return;
57
+ }
58
+ const doc = await loadUsageCached(cfg, resolvedUsageKey, opts);
59
+ if (!doc) {
60
+ ui.setStatus(QUOTA_STATUS_KEY, undefined);
61
+ return;
62
+ }
63
+ const rendered = renderQuotaSegment(doc, model.provider, ui.theme);
64
+ ui.setStatus(QUOTA_STATUS_KEY, rendered ?? undefined);
65
+ }
66
+ export function registerQuotaHooks(dm, overrides = {}, lifecycle = { active: true, quotaGeneration: 0 }) {
67
+ const deps = {
68
+ loadConfig,
69
+ resolveConfigValue,
70
+ refreshQuotaStatus,
71
+ now: Date.now,
72
+ ...overrides
73
+ };
74
+ let lastTurnFetchMs = 0;
75
+ const renderCurrent = async (ctx, readOnly = false) => {
76
+ if (!ctx.hasUI || !lifecycle.active)
77
+ return;
78
+ const renderGeneration = ++lifecycle.quotaGeneration;
79
+ const cfg = deps.loadConfig();
80
+ const resolvedUsageKey = deps.resolveConfigValue(cfg.proxy.usageKey);
81
+ const guardedUi = {
82
+ theme: ctx.ui.theme,
83
+ setStatus(key, text) {
84
+ if (lifecycle.active && renderGeneration === lifecycle.quotaGeneration) {
85
+ ctx.ui.setStatus(key, text);
86
+ }
87
+ }
88
+ };
89
+ await deps.refreshQuotaStatus(cfg, resolvedUsageKey, guardedUi, ctx.model, readOnly ? { readOnly: true } : {});
90
+ };
91
+ dm.on("session_start", async (_event, ctx) => {
92
+ await renderCurrent(ctx);
93
+ });
94
+ dm.on("model_select", async (_event, ctx) => {
95
+ await renderCurrent(ctx, true);
96
+ });
97
+ dm.on("turn_end", async (_event, ctx) => {
98
+ const now = deps.now();
99
+ const readOnly = now - lastTurnFetchMs < TURN_FETCH_DEBOUNCE_MS;
100
+ if (!readOnly)
101
+ lastTurnFetchMs = now;
102
+ await renderCurrent(ctx, readOnly);
103
+ });
104
+ }
105
+ export default async function cliproxyapi(dm) {
106
+ registerCommands(dm);
107
+ const lifecycle = {
108
+ active: true,
109
+ quotaGeneration: 0
110
+ };
111
+ registerQuotaHooks(dm, {}, lifecycle);
112
+ let refreshTimer;
113
+ dm.on("session_shutdown", () => {
114
+ lifecycle.active = false;
115
+ lifecycle.quotaGeneration++;
116
+ if (refreshTimer !== undefined)
117
+ clearInterval(refreshTimer);
118
+ invalidateProviderLifecycle(dm);
119
+ });
120
+ const cfg = loadConfig();
121
+ const resolvedKey = resolveConfigValue(cfg.proxy.apiKey);
122
+ if (!resolvedKey) {
123
+ log.debug("apiKey is empty after resolution — skipping initial apply. Run /cliproxy-setup to configure.");
124
+ invalidateProviderLifecycle(dm);
125
+ return;
126
+ }
127
+ const conflicts = detectConflicts(cfg);
128
+ for (const c of conflicts)
129
+ log.warn(`conflict (${c.kind}): ${c.detail}`);
130
+ try {
131
+ const cachedGeneration = beginProviderRefresh(dm);
132
+ const cached = readDiscoveryCache(discoveryCacheScope(cfg.proxy.endpoint, resolvedKey));
133
+ if (cached) {
134
+ log.debug(`discovery from cache (age ${Math.round(cached.ageMs / 1000)}s): ${cached.discovery.builtinProviders.length} builtin, ${cached.discovery.customPool.length} custom`);
135
+ await applyAll(dm, cfg, cached.discovery, cachedGeneration);
136
+ (async () => {
137
+ const refreshGeneration = beginProviderRefresh(dm);
138
+ try {
139
+ const fresh = await fetchDiscovery(cfg, resolvedKey);
140
+ if (!lifecycle.active)
141
+ return;
142
+ await applyAll(dm, cfg, fresh, refreshGeneration);
143
+ log.debug("discovery revalidated from network");
144
+ } catch (e) {
145
+ log.warn("background discovery revalidate failed:", e.message);
146
+ }
147
+ })();
148
+ } else {
149
+ const refreshGeneration = beginProviderRefresh(dm);
150
+ const discovery = await fetchDiscovery(cfg, resolvedKey);
151
+ if (!lifecycle.active)
152
+ return;
153
+ await applyAll(dm, cfg, discovery, refreshGeneration);
154
+ }
155
+ } catch (err) {
156
+ log.error("initial apply failed:", err.message);
157
+ }
158
+ if (cfg.refreshIntervalMinutes > 0) {
159
+ const ms = cfg.refreshIntervalMinutes * 60000;
160
+ refreshTimer = setInterval(() => {
161
+ (async () => {
162
+ const refreshGeneration = beginProviderRefresh(dm);
163
+ try {
164
+ if (!lifecycle.active)
165
+ return;
166
+ const c = loadConfig();
167
+ const k = resolveConfigValue(c.proxy.apiKey);
168
+ if (!k)
169
+ return;
170
+ const d = await fetchDiscovery(c, k);
171
+ if (!lifecycle.active)
172
+ return;
173
+ await applyAll(dm, c, d, refreshGeneration);
174
+ log.debug("background refresh ok");
175
+ } catch (e) {
176
+ log.warn("background refresh failed:", e.message);
177
+ }
178
+ })();
179
+ }, ms);
180
+ log.debug(`background refresh every ${cfg.refreshIntervalMinutes}m`);
181
+ }
182
+ }