@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.
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,2 +1,192 @@
1
- import{execSync as G}from"node:child_process";import{randomUUID as k}from"node:crypto";import{chmodSync as B,closeSync as U,constants as N,copyFileSync as w,existsSync as Y,fsyncSync as A,linkSync as I,mkdirSync as _,openSync as D,readFileSync as O,renameSync as C,rmSync as L,writeFileSync as x}from"node:fs";import{homedir as V}from"node:os";import{dirname as q,join as $}from"node:path";import{fileURLToPath as v}from"node:url";var M=!1;var b={info(...K){if(!M)console.log("[dm-cliproxy]",...K)},warn(...K){if(!M)console.warn("[dm-cliproxy]",...K)},error(...K){if(!M)console.error("[dm-cliproxy]",...K)},debug(...K){if(!M&&(process.env.DM_CLIPROXY_DEBUG||process.env.PI_CLIPROXYAPI_DEBUG))console.log("[dm-cliproxy]","[debug]",...K)}};var S=$(V(),".dm","agent","dm-cliproxy"),z=$(S,"config.json"),F=[$(V(),".pi","agent","pi-cliproxyapi","config.json"),$(V(),".config","pi-cliproxyapi","config.json")];function H(K){_(K,{recursive:!0,mode:448}),B(K,448)}function R(K){if(Y(K))B(K,384)}function u(K){let J;try{J=JSON.parse(O(K,"utf8"))}catch{return}let W=J&&typeof J==="object"?J.proxy:void 0;for(let Q of[W?.apiKey,W?.usageKey]){if(typeof Q!=="string")continue;let X=/^!cat\s+((?:~\/|\/)[^\s;&|`]+)$/.exec(Q.trim());if(!X)continue;let Z=X[1].replace(/^~/,V());R(Z)}}function f(){return Y($(q(v(import.meta.url)),"..",".git"))}function p(K=F,J=z,W=f()){if(Y(J))return;let X=(typeof K==="string"?[K]:[...K]).find((E)=>Y(E));if(!X)return;let Z;try{R(X),u(X),H(q(J)),Z=`${J}.${process.pid}.${k()}.tmp`,w(X,Z,N.COPYFILE_EXCL),B(Z,384),I(Z,J),L(Z),Z=void 0,b.info("legacy config copied to",J)}catch(E){if(Z)try{L(Z,{force:!0})}catch(T){b.warn("failed to clean temporary config:",T)}b.warn("failed to migrate legacy config:",E)}}var j={proxy:{endpoint:"",apiKey:""},builtinProviders:{},customProviders:{},discoveryExcludes:["*:*"],overrides:{},refreshIntervalMinutes:0,usageCacheTtlMs:30000};function c(){if(p(),!Y(z))return b.debug("config not found, using defaults at",z),structuredClone(j);try{H(q(z)),R(z);let K=JSON.parse(O(z,"utf8"));return m(K)}catch(K){return b.error("failed to read config:",K,"— using defaults"),structuredClone(j)}}function d(K,J=z){H(q(J));let W=`${J}.${process.pid}.${k()}.tmp`,Q;try{Q=D(W,"wx",384),x(Q,JSON.stringify(K,null,2)+`
2
- `,{encoding:"utf8"}),A(Q),U(Q),Q=void 0,C(W,J),R(J)}finally{if(Q!==void 0)U(Q);L(W,{force:!0})}}function o(K){d(K),b.info("config saved to",z)}function m(K){if(!K||typeof K!=="object")return structuredClone(j);let J=K,W=structuredClone(j),Q=J.proxy??{};if(W.proxy.endpoint=typeof Q.endpoint==="string"?Q.endpoint:W.proxy.endpoint,W.proxy.apiKey=typeof Q.apiKey==="string"?Q.apiKey:"",typeof Q.usageKey==="string")W.proxy.usageKey=Q.usageKey;if(typeof Q.providerPrefix==="string"&&Q.providerPrefix.trim())W.proxy.providerPrefix=Q.providerPrefix.trim();if(J.builtinProviders&&typeof J.builtinProviders==="object")W.builtinProviders=J.builtinProviders;if(J.customProviders&&typeof J.customProviders==="object")W.customProviders=J.customProviders;if(Array.isArray(J.discoveryExcludes))W.discoveryExcludes=J.discoveryExcludes.filter((X)=>typeof X==="string");if(J.overrides&&typeof J.overrides==="object")W.overrides=J.overrides;if(typeof J.refreshIntervalMinutes==="number")W.refreshIntervalMinutes=J.refreshIntervalMinutes;if(typeof J.usageCacheTtlMs==="number")W.usageCacheTtlMs=J.usageCacheTtlMs;return W}function t(K){if(!K)return"";let J=K.trim();if(J.startsWith("!"))try{return G(J.slice(1),{encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()}catch(W){let Q=W?.status,X=typeof Q==="number"?` (exit ${Q})`:"";return b.warn(`failed to resolve "!" config value${X}`),""}if(J.startsWith("$"))return process.env[J.slice(1)]??"";return J}export{d as saveConfigAtPath,o as saveConfig,t as resolveConfigValue,p as migrateLegacyConfig,c as loadConfig,f as isLocalCheckout,R as ensurePrivateFile,H as ensurePrivateDirectory,F as LEGACY_CONFIG_PATHS,z as CONFIG_PATH,S as CONFIG_DIR};
1
+ import { execSync } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import {
4
+ chmodSync,
5
+ closeSync,
6
+ constants,
7
+ copyFileSync,
8
+ existsSync,
9
+ fsyncSync,
10
+ linkSync,
11
+ mkdirSync,
12
+ openSync,
13
+ readFileSync,
14
+ renameSync,
15
+ rmSync,
16
+ writeFileSync
17
+ } from "node:fs";
18
+ import { homedir } from "node:os";
19
+ import { dirname, join } from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+ import { log } from "./log.js";
22
+ export const CONFIG_DIR = join(homedir(), ".dm", "agent", "dm-cliproxy");
23
+ export const CONFIG_PATH = join(CONFIG_DIR, "config.json");
24
+ export const LEGACY_CONFIG_PATHS = [
25
+ join(homedir(), ".pi", "agent", "pi-cliproxyapi", "config.json"),
26
+ join(homedir(), ".config", "pi-cliproxyapi", "config.json")
27
+ ];
28
+ export function ensurePrivateDirectory(path) {
29
+ mkdirSync(path, { recursive: true, mode: 448 });
30
+ chmodSync(path, 448);
31
+ }
32
+ export function ensurePrivateFile(path) {
33
+ if (existsSync(path))
34
+ chmodSync(path, 384);
35
+ }
36
+ function hardenReferencedSecretFiles(configPath) {
37
+ let raw;
38
+ try {
39
+ raw = JSON.parse(readFileSync(configPath, "utf8"));
40
+ } catch {
41
+ return;
42
+ }
43
+ const proxy = raw && typeof raw === "object" ? raw.proxy : undefined;
44
+ for (const value of [proxy?.apiKey, proxy?.usageKey]) {
45
+ if (typeof value !== "string")
46
+ continue;
47
+ const match = /^!cat\s+((?:~\/|\/)[^\s;&|`]+)$/.exec(value.trim());
48
+ if (!match)
49
+ continue;
50
+ const secretPath = match[1].replace(/^~/, homedir());
51
+ ensurePrivateFile(secretPath);
52
+ }
53
+ }
54
+ export function isLocalCheckout() {
55
+ return existsSync(join(dirname(fileURLToPath(import.meta.url)), "..", ".git"));
56
+ }
57
+ export function migrateLegacyConfig(legacyPaths = LEGACY_CONFIG_PATHS, configPath = CONFIG_PATH, _localCheckout = isLocalCheckout()) {
58
+ if (existsSync(configPath))
59
+ return;
60
+ const candidates = typeof legacyPaths === "string" ? [legacyPaths] : [...legacyPaths];
61
+ const legacyPath = candidates.find((candidate) => existsSync(candidate));
62
+ if (!legacyPath)
63
+ return;
64
+ let temporaryPath;
65
+ try {
66
+ ensurePrivateFile(legacyPath);
67
+ hardenReferencedSecretFiles(legacyPath);
68
+ ensurePrivateDirectory(dirname(configPath));
69
+ temporaryPath = `${configPath}.${process.pid}.${randomUUID()}.tmp`;
70
+ copyFileSync(legacyPath, temporaryPath, constants.COPYFILE_EXCL);
71
+ chmodSync(temporaryPath, 384);
72
+ linkSync(temporaryPath, configPath);
73
+ rmSync(temporaryPath);
74
+ temporaryPath = undefined;
75
+ log.info("legacy config copied to", configPath);
76
+ } catch (err) {
77
+ if (temporaryPath) {
78
+ try {
79
+ rmSync(temporaryPath, { force: true });
80
+ } catch (cleanupErr) {
81
+ log.warn("failed to clean temporary config:", cleanupErr);
82
+ }
83
+ }
84
+ log.warn("failed to migrate legacy config:", err);
85
+ }
86
+ }
87
+ const DEFAULT_CONFIG = {
88
+ proxy: {
89
+ endpoint: "",
90
+ apiKey: ""
91
+ },
92
+ builtinProviders: {},
93
+ customProviders: {},
94
+ discoveryExcludes: ["*:*"],
95
+ overrides: {},
96
+ refreshIntervalMinutes: 0,
97
+ usageCacheTtlMs: 30000
98
+ };
99
+ export function loadConfig() {
100
+ migrateLegacyConfig();
101
+ if (!existsSync(CONFIG_PATH)) {
102
+ log.debug("config not found, using defaults at", CONFIG_PATH);
103
+ return structuredClone(DEFAULT_CONFIG);
104
+ }
105
+ try {
106
+ ensurePrivateDirectory(dirname(CONFIG_PATH));
107
+ ensurePrivateFile(CONFIG_PATH);
108
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
109
+ return normalizeConfig(raw);
110
+ } catch (err) {
111
+ log.error("failed to read config:", err, "— using defaults");
112
+ return structuredClone(DEFAULT_CONFIG);
113
+ }
114
+ }
115
+ export function saveConfigAtPath(cfg, configPath = CONFIG_PATH) {
116
+ ensurePrivateDirectory(dirname(configPath));
117
+ const temporaryPath = `${configPath}.${process.pid}.${randomUUID()}.tmp`;
118
+ let fileDescriptor;
119
+ try {
120
+ fileDescriptor = openSync(temporaryPath, "wx", 384);
121
+ writeFileSync(fileDescriptor, JSON.stringify(cfg, null, 2) + `
122
+ `, {
123
+ encoding: "utf8"
124
+ });
125
+ fsyncSync(fileDescriptor);
126
+ closeSync(fileDescriptor);
127
+ fileDescriptor = undefined;
128
+ renameSync(temporaryPath, configPath);
129
+ ensurePrivateFile(configPath);
130
+ } finally {
131
+ if (fileDescriptor !== undefined)
132
+ closeSync(fileDescriptor);
133
+ rmSync(temporaryPath, { force: true });
134
+ }
135
+ }
136
+ export function saveConfig(cfg) {
137
+ saveConfigAtPath(cfg);
138
+ log.info("config saved to", CONFIG_PATH);
139
+ }
140
+ function normalizeConfig(raw) {
141
+ if (!raw || typeof raw !== "object")
142
+ return structuredClone(DEFAULT_CONFIG);
143
+ const r = raw;
144
+ const merged = structuredClone(DEFAULT_CONFIG);
145
+ const proxyBlock = r.proxy ?? {};
146
+ merged.proxy.endpoint = typeof proxyBlock.endpoint === "string" ? proxyBlock.endpoint : merged.proxy.endpoint;
147
+ merged.proxy.apiKey = typeof proxyBlock.apiKey === "string" ? proxyBlock.apiKey : "";
148
+ if (typeof proxyBlock.usageKey === "string")
149
+ merged.proxy.usageKey = proxyBlock.usageKey;
150
+ if (typeof proxyBlock.providerPrefix === "string" && proxyBlock.providerPrefix.trim()) {
151
+ merged.proxy.providerPrefix = proxyBlock.providerPrefix.trim();
152
+ }
153
+ if (r.builtinProviders && typeof r.builtinProviders === "object") {
154
+ merged.builtinProviders = r.builtinProviders;
155
+ }
156
+ if (r.customProviders && typeof r.customProviders === "object") {
157
+ merged.customProviders = r.customProviders;
158
+ }
159
+ if (Array.isArray(r.discoveryExcludes)) {
160
+ merged.discoveryExcludes = r.discoveryExcludes.filter((x) => typeof x === "string");
161
+ }
162
+ if (r.overrides && typeof r.overrides === "object") {
163
+ merged.overrides = r.overrides;
164
+ }
165
+ if (typeof r.refreshIntervalMinutes === "number")
166
+ merged.refreshIntervalMinutes = r.refreshIntervalMinutes;
167
+ if (typeof r.usageCacheTtlMs === "number")
168
+ merged.usageCacheTtlMs = r.usageCacheTtlMs;
169
+ return merged;
170
+ }
171
+ export function resolveConfigValue(raw) {
172
+ if (!raw)
173
+ return "";
174
+ const value = raw.trim();
175
+ if (value.startsWith("!")) {
176
+ try {
177
+ return execSync(value.slice(1), {
178
+ encoding: "utf8",
179
+ stdio: ["ignore", "pipe", "pipe"]
180
+ }).trim();
181
+ } catch (err) {
182
+ const status = err?.status;
183
+ const suffix = typeof status === "number" ? ` (exit ${status})` : "";
184
+ log.warn(`failed to resolve "!" config value${suffix}`);
185
+ return "";
186
+ }
187
+ }
188
+ if (value.startsWith("$")) {
189
+ return process.env[value.slice(1)] ?? "";
190
+ }
191
+ return value;
192
+ }
@@ -1 +1,46 @@
1
- import{existsSync as c,readFileSync as f}from"node:fs";import{homedir as d}from"node:os";import{join as a}from"node:path";var m=a(d(),".dm","models.json"),u=a(d(),".dm","auth.json");function s(o){if(!c(o))return null;try{return JSON.parse(f(o,"utf8"))}catch{return null}}function g(o){let n=[],i=new Set([...Object.keys(o.builtinProviders),...Object.keys(o.customProviders)]),r=s(m);if(r&&typeof r==="object"){for(let e of Object.keys(r))if(i.has(e))n.push({kind:"models.json",provider:e,detail:`provider "${e}" has user overrides in ~/.dm/models.json which DM merges on top of our registration`})}let t=s(u);if(t&&typeof t==="object"){for(let e of Object.keys(t))if(i.has(e))n.push({kind:"auth.json",provider:e,detail:`provider "${e}" has user-set credentials in ~/.dm/auth.json which take priority over our apiKey`})}return n}export{g as detectConflicts};
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ const MODELS_JSON = join(homedir(), ".dm", "models.json");
5
+ const AUTH_JSON = join(homedir(), ".dm", "auth.json");
6
+ function safeRead(path) {
7
+ if (!existsSync(path))
8
+ return null;
9
+ try {
10
+ return JSON.parse(readFileSync(path, "utf8"));
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+ export function detectConflicts(cfg) {
16
+ const out = [];
17
+ const providersInCfg = new Set([
18
+ ...Object.keys(cfg.builtinProviders),
19
+ ...Object.keys(cfg.customProviders)
20
+ ]);
21
+ const models = safeRead(MODELS_JSON);
22
+ if (models && typeof models === "object") {
23
+ for (const name of Object.keys(models)) {
24
+ if (providersInCfg.has(name)) {
25
+ out.push({
26
+ kind: "models.json",
27
+ provider: name,
28
+ detail: `provider "${name}" has user overrides in ~/.dm/models.json which DM merges on top of our registration`
29
+ });
30
+ }
31
+ }
32
+ }
33
+ const auth = safeRead(AUTH_JSON);
34
+ if (auth && typeof auth === "object") {
35
+ for (const name of Object.keys(auth)) {
36
+ if (providersInCfg.has(name)) {
37
+ out.push({
38
+ kind: "auth.json",
39
+ provider: name,
40
+ detail: `provider "${name}" has user-set credentials in ~/.dm/auth.json which take priority over our apiKey`
41
+ });
42
+ }
43
+ }
44
+ }
45
+ return out;
46
+ }
@@ -1 +1,190 @@
1
- function C(J,Z){if(!J)return!0;for(let $ of Z)if(new RegExp("^"+$.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*")+"$").test(J))return!0;return!1}var T=128000,x=16000,S={input:0,output:0,cacheRead:0,cacheWrite:0},b=[/.*-thinking$/,/^gpt-5(\.\d+)?(-.*)?$/,/^gemini-3(\.\d+)?(-.*)?$/];function H(J){return b.some((Z)=>Z.test(J))}var u=[[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 L(J,Z){let $=(J??"").trim();return $?`${$}-${Z}`:Z}function E(J,Z){let $=(Z??"").trim(),Y=J.match(/^([a-z0-9]+-)(.*)$/);if(Y)return $?`${$}-${Y[1]}`:Y[1];return J}function B(J,Z){for(let[$,Y,V]of u)if($.has(J))return{slug:L(Z,Y),api:V};return{slug:L(Z,"misc"),api:"openai-completions"}}function h(J){return J.replace(/[/:]/g," ").split(/[-_]/).filter(Boolean).map((Z)=>Z[0].toUpperCase()+Z.slice(1)).join(" ")}function v(J){return{id:J,name:h(J),contextWindow:T,maxTokens:x,reasoning:H(J),cost:{...S}}}import{createHash as n}from"node:crypto";import{existsSync as k0,readFileSync as A0,writeFileSync as p}from"node:fs";import{dirname as P,join as s}from"node:path";import{chmodSync as O,closeSync as a,constants as e,copyFileSync as J0,existsSync as f,fsyncSync as Y0,linkSync as Z0,mkdirSync as y,openSync as $0,readFileSync as Q0,renameSync as V0,rmSync as z0,writeFileSync as j0}from"node:fs";import{homedir as D}from"node:os";import{dirname as W0,join as R}from"node:path";var K=!1;var X={info(...J){if(!K)console.log("[dm-cliproxy]",...J)},warn(...J){if(!K)console.warn("[dm-cliproxy]",...J)},error(...J){if(!K)console.error("[dm-cliproxy]",...J)},debug(...J){if(!K&&(process.env.DM_CLIPROXY_DEBUG||process.env.PI_CLIPROXYAPI_DEBUG))console.log("[dm-cliproxy]","[debug]",...J)}};var k=R(D(),".dm","agent","dm-cliproxy"),H0=R(k,"config.json"),v0=[R(D(),".pi","agent","pi-cliproxyapi","config.json"),R(D(),".config","pi-cliproxyapi","config.json")];function w(J){y(J,{recursive:!0,mode:448}),O(J,448)}function U(J){if(f(J))O(J,384)}var g=s(k,"discovery-cache.json");function G(J,Z){return n("sha256").update(J.trim().replace(/\/+$/,"")).update("\x00").update(Z).digest("hex")}function A(J,Z,$=g){try{w(P($));let Y={savedAt:Date.now(),scope:Z,discovery:J};p($,JSON.stringify(Y),{encoding:"utf8",mode:384}),U($)}catch(Y){X.warn("failed to write discovery cache:",Y.message)}}var _="dm-cliproxy/0.3.5",d=5000;async function F(J,Z){let $=new AbortController,Y=setTimeout(()=>$.abort(Error("timeout")),d);try{return await fetch(J,{...Z,signal:$.signal})}finally{clearTimeout(Y)}}function l(J){return new URL("/.well-known/pi",new URL(J).origin).toString()}async function c(J){let Z=l(J.proxy.endpoint),$;try{$=await F(Z,{headers:{"User-Agent":_,Accept:"application/json"}})}catch(Q){return X.debug("well-known fetch failed:",Q.message,"— falling back to /v1/models"),null}if(!$.ok)return X.debug(`well-known returned ${$.status} — falling back to /v1/models`),null;let Y;try{Y=await $.json()}catch{return X.debug("well-known returned non-JSON — falling back to /v1/models"),null}if(!Y||Y.schemaVersion!==1)return X.debug("well-known schemaVersion != 1 — falling back to /v1/models"),null;let V=[],z=Y.builtinProviders??{};for(let[Q,q]of Object.entries(z)){if(!q||!Array.isArray(q.models))continue;let W=q.models.map((j)=>({id:String(j.id),name:typeof j.name==="string"?j.name:String(j.id),reasoning:Boolean(j.reasoning??H(String(j.id))),contextWindow:typeof j.contextWindow==="number"?j.contextWindow:200000,maxTokens:typeof j.maxTokens==="number"?j.maxTokens:16000,cost:j.cost??{input:0,output:0,cacheRead:0,cacheWrite:0}}));V.push({name:Q,api:q.api??"openai-responses",models:W})}let M=(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??H(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:E(typeof Q.suggestedProviderName==="string"?Q.suggestedProviderName:"misc",J.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:V,customPool:M,serverDiscoveryExcludes:Array.isArray(Y.discoveryExcludes)?Y.discoveryExcludes.filter((Q)=>typeof Q==="string"):[],upstreamTotal:typeof Y?.counts?.upstreamTotal==="number"?Y.counts.upstreamTotal:0}}async function t(J,Z){let $=new URL("/v1/models",new URL(J.proxy.endpoint).origin).toString(),Y=await F($,{headers:{Authorization:`Bearer ${Z}`,Accept:"application/json","User-Agent":_}});if(!Y.ok)throw Error(`/v1/models returned ${Y.status}`);let V=await Y.json();if(!V?.data||!Array.isArray(V.data))return[];return V.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 i(J,Z){let $=Z.discoveryExcludes,Y=new Map,V=[],z=0;for(let M of J){if(z++,C(M.id,$))continue;if(M.owned_by==="openai"){let j=v(M.id);I(Y,"openai","openai-responses",N(j));continue}if(M.owned_by==="anthropic"){let j=v(M.id);I(Y,"anthropic","anthropic-messages",N(j));continue}let{slug:Q,api:q}=B(M.owned_by,Z.proxy.providerPrefix),W=v(M.id);V.push({id:M.id,name:W.name??M.id,reasoning:W.reasoning??!1,contextWindow:W.contextWindow??128000,maxTokens:W.maxTokens??16000,cost:W.cost??{input:0,output:0,cacheRead:0,cacheWrite:0},api:q,suggestedProvider:Q,ownedBy:M.owned_by})}return{source:"v1-models",upstreamVersion:null,builtinProviders:Array.from(Y.values()),customPool:V,serverDiscoveryExcludes:[],upstreamTotal:z}}function I(J,Z,$,Y){let V=J.get(Z);if(!V)V={name:Z,api:$,models:[]},J.set(Z,V);V.models.push(Y)}function N(J){return{id:J.id,name:J.name??J.id,reasoning:J.reasoning??!1,contextWindow:J.contextWindow??128000,maxTokens:J.maxTokens??16000,cost:J.cost??{input:0,output:0,cacheRead:0,cacheWrite:0}}}async function G0(J,Z){let $=G(J.proxy.endpoint,Z),Y=await c(J);if(Y)return X.debug(`discovery via /.well-known/pi: ${Y.builtinProviders.length} builtin, ${Y.customPool.length} custom`),A(Y,$),Y;if(!Z)throw Error("well-known unavailable AND proxy apiKey is empty — cannot fall back to /v1/models. Set proxy.apiKey in config.");let V=await t(J,Z),z=i(V,J);return X.debug(`discovery via /v1/models: ${z.builtinProviders.length} builtin, ${z.customPool.length} custom`),A(z,$),z}function I0(J){let Z=new Set;for(let $ of J.builtinProviders)for(let Y of $.models)Z.add(Y.id);for(let $ of J.customPool)Z.add($.id);return Z}export{G0 as fetchDiscovery,I0 as discoveryToIdSet,_ as PLUGIN_USER_AGENT};
1
+ import {
2
+ classifyCustom,
3
+ isExcluded,
4
+ modelDefaults,
5
+ normalizeSuggestedProvider,
6
+ reasoningFromId
7
+ } from "./compat.js";
8
+ import { discoveryCacheScope, writeDiscoveryCache } from "./cache.js";
9
+ import { log } from "./log.js";
10
+ export const PLUGIN_USER_AGENT = "dm-cliproxy/0.3.5";
11
+ const REQUEST_TIMEOUT_MS = 5000;
12
+ async function fetchWithTimeout(url, init) {
13
+ const ctrl = new AbortController;
14
+ const timer = setTimeout(() => ctrl.abort(new Error("timeout")), REQUEST_TIMEOUT_MS);
15
+ try {
16
+ return await fetch(url, { ...init, signal: ctrl.signal });
17
+ } finally {
18
+ clearTimeout(timer);
19
+ }
20
+ }
21
+ function discoveryUrl(endpoint) {
22
+ return new URL("/.well-known/pi", new URL(endpoint).origin).toString();
23
+ }
24
+ async function tryWellKnown(cfg) {
25
+ const url = discoveryUrl(cfg.proxy.endpoint);
26
+ let resp;
27
+ try {
28
+ resp = await fetchWithTimeout(url, {
29
+ headers: { "User-Agent": PLUGIN_USER_AGENT, Accept: "application/json" }
30
+ });
31
+ } catch (err) {
32
+ log.debug("well-known fetch failed:", err.message, "— falling back to /v1/models");
33
+ return null;
34
+ }
35
+ if (!resp.ok) {
36
+ log.debug(`well-known returned ${resp.status} — falling back to /v1/models`);
37
+ return null;
38
+ }
39
+ let body;
40
+ try {
41
+ body = await resp.json();
42
+ } catch {
43
+ log.debug("well-known returned non-JSON — falling back to /v1/models");
44
+ return null;
45
+ }
46
+ if (!body || body.schemaVersion !== 1) {
47
+ log.debug("well-known schemaVersion != 1 — falling back to /v1/models");
48
+ return null;
49
+ }
50
+ const builtin = [];
51
+ const builtinProviders = body.builtinProviders ?? {};
52
+ for (const [name, p] of Object.entries(builtinProviders)) {
53
+ if (!p || !Array.isArray(p.models))
54
+ continue;
55
+ const models = p.models.map((m) => ({
56
+ id: String(m.id),
57
+ name: typeof m.name === "string" ? m.name : String(m.id),
58
+ reasoning: Boolean(m.reasoning ?? reasoningFromId(String(m.id))),
59
+ contextWindow: typeof m.contextWindow === "number" ? m.contextWindow : 200000,
60
+ maxTokens: typeof m.maxTokens === "number" ? m.maxTokens : 16000,
61
+ cost: m.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
62
+ }));
63
+ builtin.push({ name, api: p.api ?? "openai-responses", models });
64
+ }
65
+ const customPool = (Array.isArray(body.customModelPool) ? body.customModelPool : []).map((m) => ({
66
+ id: String(m.id),
67
+ name: typeof m.name === "string" ? m.name : String(m.id),
68
+ reasoning: Boolean(m.reasoning ?? reasoningFromId(String(m.id))),
69
+ contextWindow: typeof m.contextWindow === "number" ? m.contextWindow : 128000,
70
+ maxTokens: typeof m.maxTokens === "number" ? m.maxTokens : 16000,
71
+ cost: m.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
72
+ api: m.api ?? "openai-completions",
73
+ suggestedProvider: normalizeSuggestedProvider(typeof m.suggestedProviderName === "string" ? m.suggestedProviderName : "misc", cfg.proxy.providerPrefix),
74
+ ownedBy: typeof m.owned_by === "string" ? m.owned_by : ""
75
+ }));
76
+ return {
77
+ source: "well-known",
78
+ upstreamVersion: typeof body?.upstream?.upstreamVersion === "string" ? body.upstream.upstreamVersion : null,
79
+ builtinProviders: builtin,
80
+ customPool,
81
+ serverDiscoveryExcludes: Array.isArray(body.discoveryExcludes) ? body.discoveryExcludes.filter((s) => typeof s === "string") : [],
82
+ upstreamTotal: typeof body?.counts?.upstreamTotal === "number" ? body.counts.upstreamTotal : 0
83
+ };
84
+ }
85
+ async function fetchRawModels(cfg, resolvedKey) {
86
+ const url = new URL("/v1/models", new URL(cfg.proxy.endpoint).origin).toString();
87
+ const resp = await fetchWithTimeout(url, {
88
+ headers: {
89
+ Authorization: `Bearer ${resolvedKey}`,
90
+ Accept: "application/json",
91
+ "User-Agent": PLUGIN_USER_AGENT
92
+ }
93
+ });
94
+ if (!resp.ok) {
95
+ throw new Error(`/v1/models returned ${resp.status}`);
96
+ }
97
+ const body = await resp.json();
98
+ if (!body?.data || !Array.isArray(body.data))
99
+ return [];
100
+ return body.data.map((m) => ({
101
+ id: typeof m.id === "string" ? m.id : "",
102
+ owned_by: typeof m.owned_by === "string" ? m.owned_by : ""
103
+ })).filter((m) => m.id);
104
+ }
105
+ function classifyLocally(raw, cfg) {
106
+ const excludes = cfg.discoveryExcludes;
107
+ const builtinByName = new Map;
108
+ const customPool = [];
109
+ let upstreamTotal = 0;
110
+ for (const m of raw) {
111
+ upstreamTotal++;
112
+ if (isExcluded(m.id, excludes))
113
+ continue;
114
+ if (m.owned_by === "openai") {
115
+ const entry = modelDefaults(m.id);
116
+ pushBuiltin(builtinByName, "openai", "openai-responses", entryToDiscovery(entry));
117
+ continue;
118
+ }
119
+ if (m.owned_by === "anthropic") {
120
+ const entry = modelDefaults(m.id);
121
+ pushBuiltin(builtinByName, "anthropic", "anthropic-messages", entryToDiscovery(entry));
122
+ continue;
123
+ }
124
+ const { slug, api } = classifyCustom(m.owned_by, cfg.proxy.providerPrefix);
125
+ const base = modelDefaults(m.id);
126
+ customPool.push({
127
+ id: m.id,
128
+ name: base.name ?? m.id,
129
+ reasoning: base.reasoning ?? false,
130
+ contextWindow: base.contextWindow ?? 128000,
131
+ maxTokens: base.maxTokens ?? 16000,
132
+ cost: base.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
133
+ api,
134
+ suggestedProvider: slug,
135
+ ownedBy: m.owned_by
136
+ });
137
+ }
138
+ return {
139
+ source: "v1-models",
140
+ upstreamVersion: null,
141
+ builtinProviders: Array.from(builtinByName.values()),
142
+ customPool,
143
+ serverDiscoveryExcludes: [],
144
+ upstreamTotal
145
+ };
146
+ }
147
+ function pushBuiltin(map, name, api, entry) {
148
+ let p = map.get(name);
149
+ if (!p) {
150
+ p = { name, api, models: [] };
151
+ map.set(name, p);
152
+ }
153
+ p.models.push(entry);
154
+ }
155
+ function entryToDiscovery(base) {
156
+ return {
157
+ id: base.id,
158
+ name: base.name ?? base.id,
159
+ reasoning: base.reasoning ?? false,
160
+ contextWindow: base.contextWindow ?? 128000,
161
+ maxTokens: base.maxTokens ?? 16000,
162
+ cost: base.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
163
+ };
164
+ }
165
+ export async function fetchDiscovery(cfg, resolvedKey) {
166
+ const cacheScope = discoveryCacheScope(cfg.proxy.endpoint, resolvedKey);
167
+ const wk = await tryWellKnown(cfg);
168
+ if (wk) {
169
+ log.debug(`discovery via /.well-known/pi: ${wk.builtinProviders.length} builtin, ${wk.customPool.length} custom`);
170
+ writeDiscoveryCache(wk, cacheScope);
171
+ return wk;
172
+ }
173
+ if (!resolvedKey) {
174
+ throw new Error("well-known unavailable AND proxy apiKey is empty — cannot fall back to /v1/models. Set proxy.apiKey in config.");
175
+ }
176
+ const raw = await fetchRawModels(cfg, resolvedKey);
177
+ const d = classifyLocally(raw, cfg);
178
+ log.debug(`discovery via /v1/models: ${d.builtinProviders.length} builtin, ${d.customPool.length} custom`);
179
+ writeDiscoveryCache(d, cacheScope);
180
+ return d;
181
+ }
182
+ export function discoveryToIdSet(d) {
183
+ const out = new Set;
184
+ for (const p of d.builtinProviders)
185
+ for (const m of p.models)
186
+ out.add(m.id);
187
+ for (const m of d.customPool)
188
+ out.add(m.id);
189
+ return out;
190
+ }
@@ -1 +1,41 @@
1
- import{dirname as O,join as z}from"node:path";import{homedir as A}from"node:os";import{dirname as U,join as $}from"node:path";var V=!1;var s={info(...n){if(!V)console.log("[dm-cliproxy]",...n)},warn(...n){if(!V)console.warn("[dm-cliproxy]",...n)},error(...n){if(!V)console.error("[dm-cliproxy]",...n)},debug(...n){if(!V&&(process.env.DM_CLIPROXY_DEBUG||process.env.PI_CLIPROXYAPI_DEBUG))console.log("[dm-cliproxy]","[debug]",...n)}};var M=$(A(),".dm","agent","dm-cliproxy"),H=$(M,"config.json"),C=[$(A(),".pi","agent","pi-cliproxyapi","config.json"),$(A(),".config","pi-cliproxyapi","config.json")];var I=z(M,"discovery-cache.json");var Z="dm-cliproxy/0.3.5";var R=15000,u=null;function l(){u=null}async function y(n,Q,j={}){if(!j.force&&u&&Date.now()-u.fetchedAt<n.usageCacheTtlMs)return u.doc;if(!Q)throw Error("usage key not configured; set proxy.usageKey in config and rerun");let X=new URL("/api/usage",new URL(n.proxy.endpoint).origin).toString(),Y=new AbortController,w=setTimeout(()=>Y.abort(Error("timeout")),R),J;try{J=await fetch(X,{headers:{"X-Plugin-Key":Q,Accept:"application/json","User-Agent":Z},signal:Y.signal})}finally{clearTimeout(w)}if(!J.ok)throw Error(`/api/usage returned ${J.status}`);let t=await J.json();if(!t||t.schemaVersion!==1||!Array.isArray(t.accounts))throw Error("/api/usage returned unexpected payload shape");return u={fetchedAt:Date.now(),doc:t},s.debug("usage fetched, accounts:",t.accounts.length),t}export{y as fetchUsage,l as clearUsageCache};
1
+ import { PLUGIN_USER_AGENT } from "./fetch-models.js";
2
+ import { log } from "./log.js";
3
+ const REQUEST_TIMEOUT_MS = 15000;
4
+ let cache = null;
5
+ export function clearUsageCache() {
6
+ cache = null;
7
+ }
8
+ export async function fetchUsage(cfg, resolvedUsageKey, opts = {}) {
9
+ if (!opts.force && cache && Date.now() - cache.fetchedAt < cfg.usageCacheTtlMs) {
10
+ return cache.doc;
11
+ }
12
+ if (!resolvedUsageKey) {
13
+ throw new Error("usage key not configured; set proxy.usageKey in config and rerun");
14
+ }
15
+ const url = new URL("/api/usage", new URL(cfg.proxy.endpoint).origin).toString();
16
+ const ctrl = new AbortController;
17
+ const timer = setTimeout(() => ctrl.abort(new Error("timeout")), REQUEST_TIMEOUT_MS);
18
+ let resp;
19
+ try {
20
+ resp = await fetch(url, {
21
+ headers: {
22
+ "X-Plugin-Key": resolvedUsageKey,
23
+ Accept: "application/json",
24
+ "User-Agent": PLUGIN_USER_AGENT
25
+ },
26
+ signal: ctrl.signal
27
+ });
28
+ } finally {
29
+ clearTimeout(timer);
30
+ }
31
+ if (!resp.ok) {
32
+ throw new Error(`/api/usage returned ${resp.status}`);
33
+ }
34
+ const body = await resp.json();
35
+ if (!body || body.schemaVersion !== 1 || !Array.isArray(body.accounts)) {
36
+ throw new Error("/api/usage returned unexpected payload shape");
37
+ }
38
+ cache = { fetchedAt: Date.now(), doc: body };
39
+ log.debug("usage fetched, accounts:", body.accounts.length);
40
+ return body;
41
+ }
@@ -1 +1,23 @@
1
- var f=!1;function h(b){f=b}var j={info(...b){if(!f)console.log("[dm-cliproxy]",...b)},warn(...b){if(!f)console.warn("[dm-cliproxy]",...b)},error(...b){if(!f)console.error("[dm-cliproxy]",...b)},debug(...b){if(!f&&(process.env.DM_CLIPROXY_DEBUG||process.env.PI_CLIPROXYAPI_DEBUG))console.log("[dm-cliproxy]","[debug]",...b)}};export{h as setLogQuiet,j as log};
1
+ const TAG = "[dm-cliproxy]";
2
+ let quiet = false;
3
+ export function setLogQuiet(v) {
4
+ quiet = v;
5
+ }
6
+ export const log = {
7
+ info(...args) {
8
+ if (!quiet)
9
+ console.log(TAG, ...args);
10
+ },
11
+ warn(...args) {
12
+ if (!quiet)
13
+ console.warn(TAG, ...args);
14
+ },
15
+ error(...args) {
16
+ if (!quiet)
17
+ console.error(TAG, ...args);
18
+ },
19
+ debug(...args) {
20
+ if (!quiet && (process.env.DM_CLIPROXY_DEBUG || process.env.PI_CLIPROXYAPI_DEBUG))
21
+ console.log(TAG, "[debug]", ...args);
22
+ }
23
+ };
@@ -1 +1,77 @@
1
- var O=["⡀","⠂","⣀","⣄","⣤","⣶","⣷","⣾"];function Y(j){if(j==="anthropic")return"claude";if(j==="openai")return"codex";return null}function Z(j){return!j.disabled&&!j.unavailable&&j.supported&&Array.isArray(j.groups)&&j.groups.length>0}function _(j){let k=`${j.id} ${j.label??""}`.toLowerCase();return k.includes("5h")||k.includes("five-hour")||k.includes("five hour")}function $(j){let k=`${j.id} ${j.label??""}`.toLowerCase();return k.includes("7d")||k.includes("seven-day")||k.includes("seven day")}function Q(j,k){let q=-1;for(let H of j){if(!Z(H))continue;let C=1/0;for(let z of H.groups??[])if(k(z)){if(z.remainingFraction<C)C=z.remainingFraction}if(C!==1/0&&C>q)q=C}if(q<0)return null;return{fraction:N(q)}}function N(j){return Math.max(0,Math.min(1,j))}function S(j){let k=Math.min(O.length-1,Math.floor(j*O.length));return O[k]}function J(j){if(j>=0.7)return"success";if(j>=0.3)return"warning";return"error"}function V(j){return Math.round(j*100)}function T(j,k,q){let H=Y(k);if(!H)return null;let C=j.accounts.filter((X)=>X.provider===H);if(C.length===0)return null;let z=Q(C,_),D=Q(C,$);if(!z&&!D)return null;let M=[q.fg("dim","\uDB81\uDE27")];if(z)M.push(`${q.fg("dim","5h")} ${q.fg(J(z.fraction),S(z.fraction))} ${q.fg(J(z.fraction),String(V(z.fraction)))}`);if(D)M.push(`${q.fg("dim","7d")} ${q.fg(J(D.fraction),S(D.fraction))} ${q.fg(J(D.fraction),String(V(D.fraction)))}`);return M.join(" ")}export{T as renderQuotaSegment,Y as providerToUsageKey};
1
+ const GAUGE_ICON = "\uDB81\uDE27";
2
+ const BRAILLE = ["⡀", "⠂", "⣀", "⣄", "⣤", "⣶", "⣷", "⣾"];
3
+ export function providerToUsageKey(piProvider) {
4
+ if (piProvider === "anthropic")
5
+ return "claude";
6
+ if (piProvider === "openai")
7
+ return "codex";
8
+ return null;
9
+ }
10
+ function isLiveAccount(a) {
11
+ return !a.disabled && !a.unavailable && a.supported && Array.isArray(a.groups) && a.groups.length > 0;
12
+ }
13
+ function is5hGroup(g) {
14
+ const s = `${g.id} ${g.label ?? ""}`.toLowerCase();
15
+ return s.includes("5h") || s.includes("five-hour") || s.includes("five hour");
16
+ }
17
+ function is7dGroup(g) {
18
+ const s = `${g.id} ${g.label ?? ""}`.toLowerCase();
19
+ return s.includes("7d") || s.includes("seven-day") || s.includes("seven day");
20
+ }
21
+ function aggregateWindow(accounts, predicate) {
22
+ let maxAcross = -1;
23
+ for (const a of accounts) {
24
+ if (!isLiveAccount(a))
25
+ continue;
26
+ let minWithin = 1 / 0;
27
+ for (const g of a.groups ?? []) {
28
+ if (predicate(g)) {
29
+ if (g.remainingFraction < minWithin)
30
+ minWithin = g.remainingFraction;
31
+ }
32
+ }
33
+ if (minWithin !== 1 / 0 && minWithin > maxAcross) {
34
+ maxAcross = minWithin;
35
+ }
36
+ }
37
+ if (maxAcross < 0)
38
+ return null;
39
+ return { fraction: clamp(maxAcross) };
40
+ }
41
+ function clamp(f) {
42
+ return Math.max(0, Math.min(1, f));
43
+ }
44
+ function brailleFor(fraction) {
45
+ const idx = Math.min(BRAILLE.length - 1, Math.floor(fraction * BRAILLE.length));
46
+ return BRAILLE[idx];
47
+ }
48
+ function colorForFraction(fraction) {
49
+ if (fraction >= 0.7)
50
+ return "success";
51
+ if (fraction >= 0.3)
52
+ return "warning";
53
+ return "error";
54
+ }
55
+ function pct(fraction) {
56
+ return Math.round(fraction * 100);
57
+ }
58
+ export function renderQuotaSegment(doc, piProvider, theme) {
59
+ const usageKey = providerToUsageKey(piProvider);
60
+ if (!usageKey)
61
+ return null;
62
+ const accounts = doc.accounts.filter((a) => a.provider === usageKey);
63
+ if (accounts.length === 0)
64
+ return null;
65
+ const w5 = aggregateWindow(accounts, is5hGroup);
66
+ const w7 = aggregateWindow(accounts, is7dGroup);
67
+ if (!w5 && !w7)
68
+ return null;
69
+ const parts = [theme.fg("dim", GAUGE_ICON)];
70
+ if (w5) {
71
+ parts.push(`${theme.fg("dim", "5h")} ${theme.fg(colorForFraction(w5.fraction), brailleFor(w5.fraction))} ${theme.fg(colorForFraction(w5.fraction), String(pct(w5.fraction)))}`);
72
+ }
73
+ if (w7) {
74
+ parts.push(`${theme.fg("dim", "7d")} ${theme.fg(colorForFraction(w7.fraction), brailleFor(w7.fraction))} ${theme.fg(colorForFraction(w7.fraction), String(pct(w7.fraction)))}`);
75
+ }
76
+ return parts.join(" ");
77
+ }
@@ -1 +1,50 @@
1
- import{visibleWidth as X}from"@duckmind/dm-tui";import{visibleWidth as Y}from"@duckmind/dm-tui";function Z(j,q){let z=Y(j);if(z===q)return j;if(z<q)return j+" ".repeat(q-z);return N(j,q)}function N(j,q){if(q<=0)return"\x1B[0m";let z="",J=0,F=0;while(F<j.length){let O=j[F];if(O==="\x1B"&&j[F+1]==="["){let K=j.indexOf("m",F+2);if(K>=0){z+=j.slice(F,K+1),F=K+1;continue}}let M=Y(O);if(J+M>q)break;z+=O,J+=M,F++}return`${z}\x1B[0m`}var U="│",A="╭",I="╮",L="╰",C="╯",P="─";function W(j,q){let z=Math.max(4,q.width);return[S(j,q.title,q.titleColor??"accent",z),...q.lines.map((J)=>T(j,J,z)),V(j,q.footer,z)]}function S(j,q,z,J){let F=q??"",O=X(F),M=Math.max(0,J-3-O),K=F?j.bold(j.fg(z,F)):"",Q=`${A}${P}${K}${P.repeat(M)}${I}`;return j.fg("borderAccent",Q)}function T(j,q,z){let J=Math.max(0,z-2),F=j.fg("borderAccent",U);return`${F}${Z(q,J)}${F}`}function V(j,q,z){let J=`${L}${P}`,F=`${C}`,O=3;if(!q||!q.hint&&!q.badge){let G=Math.max(0,z-3);return j.fg("borderAccent",`${J}${P.repeat(G)}${F}`)}let M=q.hint??"",K=q.badge??"",Q=X(M),_=X(K),$=Math.max(0,z-3-Q-_),x=j.fg("dim",M),D=K?j.fg("muted",K):"",E=P.repeat($);return`${j.fg("borderAccent",J)}${x}${j.fg("borderAccent",E)}${D}${j.fg("borderAccent",F)}`}function R(j){return Math.max(0,j-2)}export{R as frameInner,W as frame};
1
+ import { visibleWidth } from "@duckmind/dm-tui";
2
+ import { pad } from "./ui-picker/render-text.js";
3
+ const SIDE = "│";
4
+ const TL = "╭";
5
+ const TR = "╮";
6
+ const BL = "╰";
7
+ const BR = "╯";
8
+ const HR = "─";
9
+ export function frame(theme, opts) {
10
+ const w = Math.max(4, opts.width);
11
+ return [
12
+ drawTop(theme, opts.title, opts.titleColor ?? "accent", w),
13
+ ...opts.lines.map((line) => drawBody(theme, line, w)),
14
+ drawBottom(theme, opts.footer, w)
15
+ ];
16
+ }
17
+ function drawTop(theme, title, color, width) {
18
+ const t = title ?? "";
19
+ const titleVis = visibleWidth(t);
20
+ const rest = Math.max(0, width - 3 - titleVis);
21
+ const titleColoured = t ? theme.bold(theme.fg(color, t)) : "";
22
+ const body = `${TL}${HR}${titleColoured}${HR.repeat(rest)}${TR}`;
23
+ return theme.fg("borderAccent", body);
24
+ }
25
+ function drawBody(theme, content, width) {
26
+ const inner = Math.max(0, width - 2);
27
+ const side = theme.fg("borderAccent", SIDE);
28
+ return `${side}${pad(content, inner)}${side}`;
29
+ }
30
+ function drawBottom(theme, footer, width) {
31
+ const start = `${BL}${HR}`;
32
+ const end = `${BR}`;
33
+ const fixedCells = 3;
34
+ if (!footer || !footer.hint && !footer.badge) {
35
+ const rest = Math.max(0, width - fixedCells);
36
+ return theme.fg("borderAccent", `${start}${HR.repeat(rest)}${end}`);
37
+ }
38
+ const hint = footer.hint ?? "";
39
+ const badge = footer.badge ?? "";
40
+ const hintVis = visibleWidth(hint);
41
+ const badgeVis = visibleWidth(badge);
42
+ const rest = Math.max(0, width - fixedCells - hintVis - badgeVis);
43
+ const left = theme.fg("dim", hint);
44
+ const right = badge ? theme.fg("muted", badge) : "";
45
+ const fill = HR.repeat(rest);
46
+ return `${theme.fg("borderAccent", start)}${left}${theme.fg("borderAccent", fill)}${right}${theme.fg("borderAccent", end)}`;
47
+ }
48
+ export function frameInner(width) {
49
+ return Math.max(0, width - 2);
50
+ }