@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,5 +1,410 @@
1
- import{randomUUID as T}from"node:crypto";import{chmodSync as w,existsSync as S,mkdirSync as M,readFileSync as $,renameSync as x,rmSync as E,statSync as B,writeFileSync as I}from"node:fs";import{homedir as D}from"node:os";import{dirname as L,join as O}from"node:path";import{Type as j}from"typebox";var F="http://localhost:20128",W=process.env.NINE_ROUTER_BASE_URL,G=process.env.NINE_ROUTER_API_KEY,f=O(D(),".dm","agent","9router-config.json"),A="9router-config",Y="9router-last-route",z=["x-9router-model","x-routed-model","x-actual-model","x-upstream-model","x-provider-model"];function C(e){return e.replace(/\/$/,"")}function U(e){return{baseUrl:C(W||e.baseUrl),apiKey:G||e.apiKey}}function H(){try{if(!S(f))return null;let e=JSON.parse($(f,"utf8"));if(typeof e.baseUrl!=="string"||!e.baseUrl.trim())return null;return{baseUrl:C(e.baseUrl.trim()),apiKey:typeof e.apiKey==="string"&&e.apiKey.trim()?e.apiKey.trim():void 0}}catch{return console.error("[dm-9router-ext] Failed to load persisted config"),null}}function J(e){let n={baseUrl:e.baseUrl};if(e.apiKey)n.apiKey=e.apiKey;return Buffer.from(`${JSON.stringify(n,null,2)}
2
- `)}function V(){if(!S(f))return{existed:!1};let e=B(f);return{existed:!0,contents:$(f),mode:e.mode&511}}function K(e,n=384){let s=`${f}.tmp-${process.pid}-${T()}`;try{return M(L(f),{recursive:!0,mode:448}),I(s,e,{flag:"wx",mode:n}),w(s,n),s}catch(c){throw E(s,{force:!0}),c}}function q(e){let n=V();return{tempPath:K(J(e)),snapshot:n}}function Q(e){x(e.tempPath,f),w(f,384)}function X(e){if(!e.existed){E(f,{force:!0});return}let n=K(e.contents??Buffer.alloc(0),e.mode??384);x(n,f),w(f,e.mode??384)}function Z(e){let n=e.sessionManager,s=typeof n.getBranch==="function"?n.getBranch():n.getEntries();for(let c=s.length-1;c>=0;c-=1){let t=s[c];if(t.type!=="custom"||t.customType!==A)continue;let o=t.data;if(typeof o?.baseUrl!=="string"||!o.baseUrl.trim())continue;return{baseUrl:C(o.baseUrl.trim()),apiKey:void 0}}return null}async function h(e,n){let s={Accept:"application/json"};if(e.apiKey)s.Authorization=`Bearer ${e.apiKey}`;let c=await fetch(`${e.baseUrl}/v1/models`,{method:"GET",headers:s,signal:n});if(!c.ok)throw Error(`9router returned ${c.status}: ${c.statusText}`);let t;try{t=await c.json()}catch{throw Error("9router returned invalid model metadata")}if(typeof t!=="object"||t===null||!Array.isArray(t.data)||t.data.some((o)=>typeof o!=="object"||o===null||typeof o.id!=="string"||!o.id.trim()))throw Error("9router returned invalid model metadata");return t.data}async function R(e,n){try{return await h(e,n),{ok:!0}}catch(s){return{ok:!1,error:s instanceof Error?s.message:String(s)}}}function ee(e){return{id:e.id,name:e.owned_by==="combo"?`\uD83D\uDD00 ${e.id}`:e.id,reasoning:!1,input:["text"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:128000,maxTokens:4096,compat:{supportsDeveloperRole:!1,supportsReasoningEffort:!1}}}function b(e,n,s){e.registerProvider("9router",{name:"9router",baseUrl:`${n.baseUrl}/v1`,apiKey:n.apiKey||"9router-no-api-key",api:"openai-completions",models:s.map(ee)})}function P(e){e.unregisterProvider("9router")}async function te(e){let n=H(),s=n||{baseUrl:F,apiKey:void 0},c=n!==null,t=U(s),o=[],m,v,y=!1,_=()=>({persistedConfig:s,hasPersistedConfig:c,config:t,discoveredModels:o,isConnected:y}),k=(u)=>{try{if(u.isConnected)b(e,u.config,u.discoveredModels);else P(e)}catch{console.error("[dm-9router-ext] Failed to restore the previous provider snapshot")}},N=(u,r,i,l)=>{let d=_(),a,p=!1,g=!1;try{if(a=q(u),p=!0,b(e,r,i),g=!0,Q(a),l)e.appendEntry(A,{baseUrl:u.baseUrl});s=u,c=!0,t=r,o=i,y=!0}catch{if(a)E(a.tempPath,{force:!0});if(g&&a)try{X(a.snapshot)}catch{console.error("[dm-9router-ext] Failed to restore the previous config file")}if(p)k(d);throw Error("9router configuration update failed; previous configuration remains active")}};try{o=await h(t,AbortSignal.timeout(1500)),y=!0,b(e,t,o)}catch{P(e)}e.on("session_start",async(u,r)=>{let i=Z(r);if(!c&&i){let l=U(i);try{let d=await h(l,r.signal);N(i,l,d,!1)}catch{}}if(y&&o.length>0)r.ui.notify(`9router connected — ${o.length} models available`,"info")}),e.on("after_provider_response",(u)=>{if(u.status>=400||v!=="9router")return;for(let r of z){let i=u.headers[r];if(!i||typeof i!=="string")continue;m=i,e.appendEntry(Y,{model:i,timestamp:Date.now()});break}}),e.on("model_select",async(u)=>{if(v=u.model.provider,v!=="9router")m=void 0}),e.registerCommand("9router-status",{description:"Show 9router connection status and configuration",handler:async(u,r)=>{let i=await R(t,r.signal),l=["\uD83D\uDD17 9router Status","",`Base URL: ${t.baseUrl}`,`API Key: ${t.apiKey?"set":"not set"}`,`Connection: ${i.ok?"\uD83D\uDFE2 connected":`\uD83D\uDD34 ${i.error||"disconnected"}`}`,`Models: ${o.length} available`];if(m)l.push(`Last routed: ${m}`);r.ui.notify(l.join(`
3
- `),i.ok?"info":"warning")}}),e.registerCommand("9router-models",{description:"Browse 9router available models and combos",handler:async(u,r)=>{if(o.length===0){r.ui.notify("No 9router models discovered. Check connection with /9router-status","warning");return}let i=o.map((a)=>({value:a.id,label:a.owned_by==="combo"?`\uD83D\uDD00 ${a.id}`:a.id})),l=await r.ui.select("Select a 9router model to use:",i.map((a)=>a.label)),d=i.find((a)=>a.label===l)?.value;if(!d)return;e.sendUserMessage(`/model 9router/${d}`,{deliverAs:"followUp"})}}),e.registerCommand("9router-config",{description:"Configure 9router base URL and API key",handler:async(u,r)=>{let i=await R(t,r.signal);r.ui.notify(["Current config:",` Base URL: ${t.baseUrl}`,` API Key: ${t.apiKey?"set":"not set"}`,` Status: ${i.ok?"\uD83D\uDFE2 connected":"\uD83D\uDD34 disconnected"}`,"","Enter new values. Blank keeps the current value."].join(`
4
- `),"info");let l=await r.ui.input("Base URL",t.baseUrl);if(l===void 0)return;let d=await r.ui.input("API key (blank keeps current, '-' clears):","");if(d===void 0)return;let a={baseUrl:C(l.trim()||s.baseUrl),apiKey:d.trim()===""?s.apiKey:d.trim()==="-"?void 0:d.trim()},p=U(a);try{let g=await h(p,r.signal);N(a,p,g,!0),r.ui.notify(`9router updated — ${o.length} models at ${t.baseUrl}`,"info")}catch{r.ui.notify("9router update failed; the previous configuration remains active","error")}}}),e.registerCommand("9router-reload",{description:"Reload models from 9router",handler:async(u,r)=>{let i=_(),l=!1;try{let d=await h(t,r.signal);l=!0,b(e,t,d),o=d,y=!0,r.ui.notify(`9router reloaded — ${o.length} models`,"info")}catch{if(l)k(i);r.ui.notify("9router reload failed; the previous provider and model list remain active","error")}}}),e.registerTool({name:"ninerouter_status",label:"9router Status",description:"Check 9router connection status and list available models",parameters:j.Object({}),async execute(u,r,i,l,d){let a=await R(t,d.signal),p=o.filter((g)=>g.owned_by==="combo");return{content:[{type:"text",text:[`9router: ${a.ok?"connected":`disconnected (${a.error})`}`,`Base URL: ${t.baseUrl}`,`Total models: ${o.length}`,` Regular: ${o.length-p.length}`,` Combos: ${p.length}`,m?`Last routed model: ${m}`:""].filter(Boolean).join(`
5
- `)}],details:{connected:a.ok,baseUrl:t.baseUrl,modelCount:o.length,comboCount:p.length,lastRoutedModel:m,models:o.map((g)=>g.id)}}}})}export{h as fetchModels,te as default};
1
+ import { randomUUID } from "node:crypto";
2
+ import {
3
+ chmodSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ renameSync,
8
+ rmSync,
9
+ statSync,
10
+ writeFileSync
11
+ } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import { dirname, join } from "node:path";
14
+ import { Type } from "typebox";
15
+ const DEFAULT_BASE_URL = "http://localhost:20128";
16
+ const ENV_BASE_URL = process.env.NINE_ROUTER_BASE_URL;
17
+ const ENV_API_KEY = process.env.NINE_ROUTER_API_KEY;
18
+ const CONFIG_PATH = join(homedir(), ".dm", "agent", "9router-config.json");
19
+ const CUSTOM_TYPE_CONFIG = "9router-config";
20
+ const CUSTOM_TYPE_LAST_ROUTE = "9router-last-route";
21
+ const ROUTING_HEADERS = [
22
+ "x-9router-model",
23
+ "x-routed-model",
24
+ "x-actual-model",
25
+ "x-upstream-model",
26
+ "x-provider-model"
27
+ ];
28
+ function normalizeBaseUrl(url) {
29
+ return url.replace(/\/$/, "");
30
+ }
31
+ function applyEnvOverrides(config) {
32
+ return {
33
+ baseUrl: normalizeBaseUrl(ENV_BASE_URL || config.baseUrl),
34
+ apiKey: ENV_API_KEY || config.apiKey
35
+ };
36
+ }
37
+ function loadConfigFromDisk() {
38
+ try {
39
+ if (!existsSync(CONFIG_PATH))
40
+ return null;
41
+ const data = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
42
+ if (typeof data.baseUrl !== "string" || !data.baseUrl.trim())
43
+ return null;
44
+ return {
45
+ baseUrl: normalizeBaseUrl(data.baseUrl.trim()),
46
+ apiKey: typeof data.apiKey === "string" && data.apiKey.trim() ? data.apiKey.trim() : undefined
47
+ };
48
+ } catch {
49
+ console.error("[dm-9router-ext] Failed to load persisted config");
50
+ return null;
51
+ }
52
+ }
53
+ function serializeConfig(config) {
54
+ const persisted = {
55
+ baseUrl: config.baseUrl
56
+ };
57
+ if (config.apiKey)
58
+ persisted.apiKey = config.apiKey;
59
+ return Buffer.from(`${JSON.stringify(persisted, null, 2)}
60
+ `);
61
+ }
62
+ function captureDiskConfigSnapshot() {
63
+ if (!existsSync(CONFIG_PATH))
64
+ return { existed: false };
65
+ const stat = statSync(CONFIG_PATH);
66
+ return {
67
+ existed: true,
68
+ contents: readFileSync(CONFIG_PATH),
69
+ mode: stat.mode & 511
70
+ };
71
+ }
72
+ function writeConfigTemp(contents, mode = 384) {
73
+ const tempPath = `${CONFIG_PATH}.tmp-${process.pid}-${randomUUID()}`;
74
+ try {
75
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true, mode: 448 });
76
+ writeFileSync(tempPath, contents, { flag: "wx", mode });
77
+ chmodSync(tempPath, mode);
78
+ return tempPath;
79
+ } catch (error) {
80
+ rmSync(tempPath, { force: true });
81
+ throw error;
82
+ }
83
+ }
84
+ function prepareConfigWrite(config) {
85
+ const snapshot = captureDiskConfigSnapshot();
86
+ return {
87
+ tempPath: writeConfigTemp(serializeConfig(config)),
88
+ snapshot
89
+ };
90
+ }
91
+ function commitPreparedConfig(prepared) {
92
+ renameSync(prepared.tempPath, CONFIG_PATH);
93
+ chmodSync(CONFIG_PATH, 384);
94
+ }
95
+ function restoreDiskSnapshot(snapshot) {
96
+ if (!snapshot.existed) {
97
+ rmSync(CONFIG_PATH, { force: true });
98
+ return;
99
+ }
100
+ const tempPath = writeConfigTemp(snapshot.contents ?? Buffer.alloc(0), snapshot.mode ?? 384);
101
+ renameSync(tempPath, CONFIG_PATH);
102
+ chmodSync(CONFIG_PATH, snapshot.mode ?? 384);
103
+ }
104
+ function loadConfigFromSession(ctx) {
105
+ const sessionManager = ctx.sessionManager;
106
+ const entries = typeof sessionManager.getBranch === "function" ? sessionManager.getBranch() : sessionManager.getEntries();
107
+ for (let index = entries.length - 1;index >= 0; index -= 1) {
108
+ const entry = entries[index];
109
+ if (entry.type !== "custom" || entry.customType !== CUSTOM_TYPE_CONFIG)
110
+ continue;
111
+ const data = entry.data;
112
+ if (typeof data?.baseUrl !== "string" || !data.baseUrl.trim())
113
+ continue;
114
+ return {
115
+ baseUrl: normalizeBaseUrl(data.baseUrl.trim()),
116
+ apiKey: undefined
117
+ };
118
+ }
119
+ return null;
120
+ }
121
+ export async function fetchModels(config, signal) {
122
+ const headers = { Accept: "application/json" };
123
+ if (config.apiKey)
124
+ headers.Authorization = `Bearer ${config.apiKey}`;
125
+ const response = await fetch(`${config.baseUrl}/v1/models`, {
126
+ method: "GET",
127
+ headers,
128
+ signal
129
+ });
130
+ if (!response.ok) {
131
+ throw new Error(`9router returned ${response.status}: ${response.statusText}`);
132
+ }
133
+ let payload;
134
+ try {
135
+ payload = await response.json();
136
+ } catch {
137
+ throw new Error("9router returned invalid model metadata");
138
+ }
139
+ if (typeof payload !== "object" || payload === null || !Array.isArray(payload.data) || payload.data.some((model) => typeof model !== "object" || model === null || typeof model.id !== "string" || !model.id.trim())) {
140
+ throw new Error("9router returned invalid model metadata");
141
+ }
142
+ return payload.data;
143
+ }
144
+ async function testConnection(config, signal) {
145
+ try {
146
+ await fetchModels(config, signal);
147
+ return { ok: true };
148
+ } catch (error) {
149
+ return {
150
+ ok: false,
151
+ error: error instanceof Error ? error.message : String(error)
152
+ };
153
+ }
154
+ }
155
+ function mapNineRouterModel(model) {
156
+ return {
157
+ id: model.id,
158
+ name: model.owned_by === "combo" ? `\uD83D\uDD00 ${model.id}` : model.id,
159
+ reasoning: false,
160
+ input: ["text"],
161
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
162
+ contextWindow: 128000,
163
+ maxTokens: 4096,
164
+ compat: {
165
+ supportsDeveloperRole: false,
166
+ supportsReasoningEffort: false
167
+ }
168
+ };
169
+ }
170
+ function registerNineRouterProvider(dm, config, models) {
171
+ dm.registerProvider("9router", {
172
+ name: "9router",
173
+ baseUrl: `${config.baseUrl}/v1`,
174
+ apiKey: config.apiKey || "9router-no-api-key",
175
+ api: "openai-completions",
176
+ models: models.map(mapNineRouterModel)
177
+ });
178
+ }
179
+ function unregisterNineRouterProvider(dm) {
180
+ dm.unregisterProvider("9router");
181
+ }
182
+ export default async function dm9router(dm) {
183
+ const diskConfig = loadConfigFromDisk();
184
+ let persistedConfig = diskConfig || {
185
+ baseUrl: DEFAULT_BASE_URL,
186
+ apiKey: undefined
187
+ };
188
+ let hasPersistedConfig = diskConfig !== null;
189
+ let config = applyEnvOverrides(persistedConfig);
190
+ let discoveredModels = [];
191
+ let lastRoutedModel;
192
+ let activeProvider;
193
+ let isConnected = false;
194
+ const snapshotRuntime = () => ({
195
+ persistedConfig,
196
+ hasPersistedConfig,
197
+ config,
198
+ discoveredModels,
199
+ isConnected
200
+ });
201
+ const restoreProviderSnapshot = (snapshot) => {
202
+ try {
203
+ if (snapshot.isConnected) {
204
+ registerNineRouterProvider(dm, snapshot.config, snapshot.discoveredModels);
205
+ } else {
206
+ unregisterNineRouterProvider(dm);
207
+ }
208
+ } catch {
209
+ console.error("[dm-9router-ext] Failed to restore the previous provider snapshot");
210
+ }
211
+ };
212
+ const commitConfigTransaction = (nextPersistedConfig, nextConfig, nextModels, appendSessionEntry) => {
213
+ const previous = snapshotRuntime();
214
+ let prepared;
215
+ let providerTouched = false;
216
+ let diskTouched = false;
217
+ try {
218
+ prepared = prepareConfigWrite(nextPersistedConfig);
219
+ providerTouched = true;
220
+ registerNineRouterProvider(dm, nextConfig, nextModels);
221
+ diskTouched = true;
222
+ commitPreparedConfig(prepared);
223
+ if (appendSessionEntry) {
224
+ dm.appendEntry(CUSTOM_TYPE_CONFIG, {
225
+ baseUrl: nextPersistedConfig.baseUrl
226
+ });
227
+ }
228
+ persistedConfig = nextPersistedConfig;
229
+ hasPersistedConfig = true;
230
+ config = nextConfig;
231
+ discoveredModels = nextModels;
232
+ isConnected = true;
233
+ } catch {
234
+ if (prepared)
235
+ rmSync(prepared.tempPath, { force: true });
236
+ if (diskTouched && prepared) {
237
+ try {
238
+ restoreDiskSnapshot(prepared.snapshot);
239
+ } catch {
240
+ console.error("[dm-9router-ext] Failed to restore the previous config file");
241
+ }
242
+ }
243
+ if (providerTouched)
244
+ restoreProviderSnapshot(previous);
245
+ throw new Error("9router configuration update failed; previous configuration remains active");
246
+ }
247
+ };
248
+ try {
249
+ discoveredModels = await fetchModels(config, AbortSignal.timeout(1500));
250
+ isConnected = true;
251
+ registerNineRouterProvider(dm, config, discoveredModels);
252
+ } catch {
253
+ unregisterNineRouterProvider(dm);
254
+ }
255
+ dm.on("session_start", async (_event, ctx) => {
256
+ const restored = loadConfigFromSession(ctx);
257
+ if (!hasPersistedConfig && restored) {
258
+ const restoredConfig = applyEnvOverrides(restored);
259
+ try {
260
+ const nextModels = await fetchModels(restoredConfig, ctx.signal);
261
+ commitConfigTransaction(restored, restoredConfig, nextModels, false);
262
+ } catch {}
263
+ }
264
+ if (isConnected && discoveredModels.length > 0) {
265
+ ctx.ui.notify(`9router connected — ${discoveredModels.length} models available`, "info");
266
+ }
267
+ });
268
+ dm.on("after_provider_response", (event) => {
269
+ if (event.status >= 400 || activeProvider !== "9router")
270
+ return;
271
+ for (const header of ROUTING_HEADERS) {
272
+ const value = event.headers[header];
273
+ if (!value || typeof value !== "string")
274
+ continue;
275
+ lastRoutedModel = value;
276
+ dm.appendEntry(CUSTOM_TYPE_LAST_ROUTE, {
277
+ model: value,
278
+ timestamp: Date.now()
279
+ });
280
+ break;
281
+ }
282
+ });
283
+ dm.on("model_select", async (event) => {
284
+ activeProvider = event.model.provider;
285
+ if (activeProvider !== "9router")
286
+ lastRoutedModel = undefined;
287
+ });
288
+ dm.registerCommand("9router-status", {
289
+ description: "Show 9router connection status and configuration",
290
+ handler: async (_args, ctx) => {
291
+ const test = await testConnection(config, ctx.signal);
292
+ const lines = [
293
+ "\uD83D\uDD17 9router Status",
294
+ "",
295
+ `Base URL: ${config.baseUrl}`,
296
+ `API Key: ${config.apiKey ? "set" : "not set"}`,
297
+ `Connection: ${test.ok ? "\uD83D\uDFE2 connected" : `\uD83D\uDD34 ${test.error || "disconnected"}`}`,
298
+ `Models: ${discoveredModels.length} available`
299
+ ];
300
+ if (lastRoutedModel)
301
+ lines.push(`Last routed: ${lastRoutedModel}`);
302
+ ctx.ui.notify(lines.join(`
303
+ `), test.ok ? "info" : "warning");
304
+ }
305
+ });
306
+ dm.registerCommand("9router-models", {
307
+ description: "Browse 9router available models and combos",
308
+ handler: async (_args, ctx) => {
309
+ if (discoveredModels.length === 0) {
310
+ ctx.ui.notify("No 9router models discovered. Check connection with /9router-status", "warning");
311
+ return;
312
+ }
313
+ const items = discoveredModels.map((model) => ({
314
+ value: model.id,
315
+ label: model.owned_by === "combo" ? `\uD83D\uDD00 ${model.id}` : model.id
316
+ }));
317
+ const selected = await ctx.ui.select("Select a 9router model to use:", items.map((item) => item.label));
318
+ const modelId = items.find((item) => item.label === selected)?.value;
319
+ if (!modelId)
320
+ return;
321
+ dm.sendUserMessage(`/model 9router/${modelId}`, { deliverAs: "followUp" });
322
+ }
323
+ });
324
+ dm.registerCommand("9router-config", {
325
+ description: "Configure 9router base URL and API key",
326
+ handler: async (_args, ctx) => {
327
+ const test = await testConnection(config, ctx.signal);
328
+ ctx.ui.notify([
329
+ "Current config:",
330
+ ` Base URL: ${config.baseUrl}`,
331
+ ` API Key: ${config.apiKey ? "set" : "not set"}`,
332
+ ` Status: ${test.ok ? "\uD83D\uDFE2 connected" : "\uD83D\uDD34 disconnected"}`,
333
+ "",
334
+ "Enter new values. Blank keeps the current value."
335
+ ].join(`
336
+ `), "info");
337
+ const newBaseUrl = await ctx.ui.input("Base URL", config.baseUrl);
338
+ if (newBaseUrl === undefined)
339
+ return;
340
+ const newApiKey = await ctx.ui.input("API key (blank keeps current, '-' clears):", "");
341
+ if (newApiKey === undefined)
342
+ return;
343
+ const nextPersistedConfig = {
344
+ baseUrl: normalizeBaseUrl(newBaseUrl.trim() || persistedConfig.baseUrl),
345
+ apiKey: newApiKey.trim() === "" ? persistedConfig.apiKey : newApiKey.trim() === "-" ? undefined : newApiKey.trim()
346
+ };
347
+ const nextConfig = applyEnvOverrides(nextPersistedConfig);
348
+ try {
349
+ const nextModels = await fetchModels(nextConfig, ctx.signal);
350
+ commitConfigTransaction(nextPersistedConfig, nextConfig, nextModels, true);
351
+ ctx.ui.notify(`9router updated — ${discoveredModels.length} models at ${config.baseUrl}`, "info");
352
+ } catch {
353
+ ctx.ui.notify("9router update failed; the previous configuration remains active", "error");
354
+ }
355
+ }
356
+ });
357
+ dm.registerCommand("9router-reload", {
358
+ description: "Reload models from 9router",
359
+ handler: async (_args, ctx) => {
360
+ const previous = snapshotRuntime();
361
+ let providerTouched = false;
362
+ try {
363
+ const nextModels = await fetchModels(config, ctx.signal);
364
+ providerTouched = true;
365
+ registerNineRouterProvider(dm, config, nextModels);
366
+ discoveredModels = nextModels;
367
+ isConnected = true;
368
+ ctx.ui.notify(`9router reloaded — ${discoveredModels.length} models`, "info");
369
+ } catch {
370
+ if (providerTouched)
371
+ restoreProviderSnapshot(previous);
372
+ ctx.ui.notify("9router reload failed; the previous provider and model list remain active", "error");
373
+ }
374
+ }
375
+ });
376
+ dm.registerTool({
377
+ name: "ninerouter_status",
378
+ label: "9router Status",
379
+ description: "Check 9router connection status and list available models",
380
+ parameters: Type.Object({}),
381
+ async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
382
+ const test = await testConnection(config, ctx.signal);
383
+ const combos = discoveredModels.filter((model) => model.owned_by === "combo");
384
+ return {
385
+ content: [
386
+ {
387
+ type: "text",
388
+ text: [
389
+ `9router: ${test.ok ? "connected" : `disconnected (${test.error})`}`,
390
+ `Base URL: ${config.baseUrl}`,
391
+ `Total models: ${discoveredModels.length}`,
392
+ ` Regular: ${discoveredModels.length - combos.length}`,
393
+ ` Combos: ${combos.length}`,
394
+ lastRoutedModel ? `Last routed model: ${lastRoutedModel}` : ""
395
+ ].filter(Boolean).join(`
396
+ `)
397
+ }
398
+ ],
399
+ details: {
400
+ connected: test.ok,
401
+ baseUrl: config.baseUrl,
402
+ modelCount: discoveredModels.length,
403
+ comboCount: combos.length,
404
+ lastRoutedModel,
405
+ models: discoveredModels.map((model) => model.id)
406
+ }
407
+ };
408
+ }
409
+ });
410
+ }