@toddzheng024/dscode-desktop 0.0.0-stage → 0.7.33

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 (484) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -2
  3. package/THIRD_PARTY_NOTICES.md +30 -0
  4. package/bin/apply_patch +9 -0
  5. package/cordis.patch.yml +433 -0
  6. package/docs/CHANGELOG.md +641 -0
  7. package/docs/account-login.md +44 -0
  8. package/docs/auto-review.md +128 -0
  9. package/docs/browser-use.md +783 -0
  10. package/docs/computer-use.md +81 -0
  11. package/docs/custom-providers.md +88 -0
  12. package/docs/demo.md +76 -0
  13. package/docs/distribution.md +58 -0
  14. package/docs/dscode-ultra.md +75 -0
  15. package/docs/email.md +296 -0
  16. package/docs/exec.md +40 -0
  17. package/docs/hub-distribution.md +190 -0
  18. package/docs/memory.md +121 -0
  19. package/docs/opencode-go.md +61 -0
  20. package/docs/plugin-hub.md +68 -0
  21. package/docs/releases/0.2.0.md +28 -0
  22. package/docs/releases/0.3.0.md +21 -0
  23. package/docs/releases/0.4.0.md +20 -0
  24. package/docs/releases/0.5.0.md +19 -0
  25. package/docs/releases/0.6.0.md +20 -0
  26. package/docs/releases/0.7.0.md +22 -0
  27. package/docs/releases/0.7.1.md +20 -0
  28. package/docs/releases/0.7.10.md +10 -0
  29. package/docs/releases/0.7.11.md +17 -0
  30. package/docs/releases/0.7.12.md +11 -0
  31. package/docs/releases/0.7.13.md +12 -0
  32. package/docs/releases/0.7.14.md +11 -0
  33. package/docs/releases/0.7.15.md +11 -0
  34. package/docs/releases/0.7.16.md +10 -0
  35. package/docs/releases/0.7.17.md +12 -0
  36. package/docs/releases/0.7.18.md +13 -0
  37. package/docs/releases/0.7.19.md +8 -0
  38. package/docs/releases/0.7.2.md +19 -0
  39. package/docs/releases/0.7.20.md +10 -0
  40. package/docs/releases/0.7.21.md +9 -0
  41. package/docs/releases/0.7.22.md +11 -0
  42. package/docs/releases/0.7.23.md +14 -0
  43. package/docs/releases/0.7.24.md +11 -0
  44. package/docs/releases/0.7.25.md +8 -0
  45. package/docs/releases/0.7.26.md +18 -0
  46. package/docs/releases/0.7.27.md +8 -0
  47. package/docs/releases/0.7.28.md +22 -0
  48. package/docs/releases/0.7.29.md +12 -0
  49. package/docs/releases/0.7.3.md +23 -0
  50. package/docs/releases/0.7.30.md +19 -0
  51. package/docs/releases/0.7.31.md +14 -0
  52. package/docs/releases/0.7.32.md +14 -0
  53. package/docs/releases/0.7.33.md +30 -0
  54. package/docs/releases/0.7.4.md +16 -0
  55. package/docs/releases/0.7.5.md +18 -0
  56. package/docs/releases/0.7.6.md +29 -0
  57. package/docs/releases/0.7.7.md +36 -0
  58. package/docs/releases/0.7.8.md +14 -0
  59. package/docs/releases/0.7.9.md +15 -0
  60. package/docs/session-bridge.md +80 -0
  61. package/docs/session-cards.md +53 -0
  62. package/docs/session-communication.md +186 -0
  63. package/docs/session-metrics.md +41 -0
  64. package/docs/skills.md +81 -0
  65. package/docs/triggers.md +338 -0
  66. package/docs/tui-commands.md +204 -0
  67. package/extensions/browser/manifest.json +22 -0
  68. package/extensions/browser/popup.css +6 -0
  69. package/extensions/browser/popup.html +11 -0
  70. package/extensions/browser/popup.mjs +47 -0
  71. package/extensions/browser/protocol.mjs +168 -0
  72. package/extensions/browser/worker.mjs +94 -0
  73. package/icon.svg +7 -0
  74. package/locale/en.json +6 -0
  75. package/locale/zh-cn.json +6 -0
  76. package/node_modules/@deepseek-ai/node-addon-system/LICENSE +21 -0
  77. package/node_modules/@deepseek-ai/node-addon-system/README.i18n.yaml +6 -0
  78. package/node_modules/@deepseek-ai/node-addon-system/README.md +15 -0
  79. package/node_modules/@deepseek-ai/node-addon-system/README.zh.md +15 -0
  80. package/node_modules/@deepseek-ai/node-addon-system/lib/flock.d.ts +13 -0
  81. package/node_modules/@deepseek-ai/node-addon-system/lib/flock.js +51 -0
  82. package/node_modules/@deepseek-ai/node-addon-system/lib/index.d.ts +73 -0
  83. package/node_modules/@deepseek-ai/node-addon-system/lib/index.js +98 -0
  84. package/node_modules/@deepseek-ai/node-addon-system/package.json +45 -0
  85. package/node_modules/@deepseek-ai/node-addon-system/src/flock.c +164 -0
  86. package/node_modules/@deepseek-ai/node-addon-system/src/main.c +298 -0
  87. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/LICENSE +28 -0
  88. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.i18n.yaml +6 -0
  89. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.md +9 -0
  90. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.zh.md +9 -0
  91. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/bin/system.node +0 -0
  92. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/package.json +31 -0
  93. package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/prebuilds.json +6 -0
  94. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/LICENSE +28 -0
  95. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.i18n.yaml +6 -0
  96. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.md +9 -0
  97. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.zh.md +9 -0
  98. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/bin/system.node +0 -0
  99. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/package.json +31 -0
  100. package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/prebuilds.json +6 -0
  101. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/LICENSE +28 -0
  102. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.i18n.yaml +6 -0
  103. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.md +11 -0
  104. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.zh.md +11 -0
  105. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/glibc/system.node +0 -0
  106. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/landlock-run +0 -0
  107. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/musl/system.node +0 -0
  108. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/package.json +31 -0
  109. package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/prebuilds.json +24 -0
  110. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/LICENSE +28 -0
  111. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.i18n.yaml +6 -0
  112. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.md +11 -0
  113. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.zh.md +11 -0
  114. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/glibc/system.node +0 -0
  115. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/landlock-run +0 -0
  116. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/musl/system.node +0 -0
  117. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/package.json +31 -0
  118. package/node_modules/@deepseek-ai/node-addon-system-linux-x64/prebuilds.json +24 -0
  119. package/package.json +156 -4
  120. package/plugins/account/index.mjs +178 -0
  121. package/plugins/account/state.mjs +123 -0
  122. package/plugins/auto-review/audit.mjs +24 -0
  123. package/plugins/auto-review/index.mjs +267 -0
  124. package/plugins/auto-review/policy.mjs +92 -0
  125. package/plugins/browser/SKILL.md +158 -0
  126. package/plugins/browser/access.mjs +143 -0
  127. package/plugins/browser/config.mjs +60 -0
  128. package/plugins/browser/connection.mjs +385 -0
  129. package/plugins/browser/desktop-client.mjs +282 -0
  130. package/plugins/browser/desktop-host.mjs +74 -0
  131. package/plugins/browser/document-identity.mjs +57 -0
  132. package/plugins/browser/extension-relay.mjs +66 -0
  133. package/plugins/browser/files.mjs +32 -0
  134. package/plugins/browser/index.mjs +235 -0
  135. package/plugins/browser/mcp-entry.mjs +18 -0
  136. package/plugins/browser/mcp-tools.mjs +21 -0
  137. package/plugins/browser/presentation.mjs +45 -0
  138. package/plugins/browser/preview.mjs +77 -0
  139. package/plugins/browser/review.mjs +20 -0
  140. package/plugins/browser/screenshots.mjs +35 -0
  141. package/plugins/clipboard-image/clipboard-image.swift +56 -0
  142. package/plugins/clipboard-image/index.mjs +70 -0
  143. package/plugins/code-review/baseline.mjs +143 -0
  144. package/plugins/code-review/git.mjs +163 -0
  145. package/plugins/code-review/index.mjs +236 -0
  146. package/plugins/compaction/engine.mjs +304 -0
  147. package/plugins/compaction/tetris.mjs +65 -0
  148. package/plugins/compaction/threshold.mjs +98 -0
  149. package/plugins/computer-use/DESKTOP-SKILL.md +64 -0
  150. package/plugins/computer-use/desktop-host.mjs +75 -0
  151. package/plugins/computer-use/desktop-skill.mjs +28 -0
  152. package/plugins/computer-use/desktop-upstream.mjs +3 -0
  153. package/plugins/computer-use/index.mjs +37 -0
  154. package/plugins/cordis-config/plain.mjs +31 -0
  155. package/plugins/credentials/index.mjs +73 -0
  156. package/plugins/custom/adapter.mjs +110 -0
  157. package/plugins/custom/config.mjs +85 -0
  158. package/plugins/custom/desktop-client.mjs +150 -0
  159. package/plugins/custom/desktop-host.mjs +78 -0
  160. package/plugins/custom/images.mjs +32 -0
  161. package/plugins/custom/index.mjs +145 -0
  162. package/plugins/custom/wire.mjs +179 -0
  163. package/plugins/desktop/client.mjs +1041 -0
  164. package/plugins/desktop/index.mjs +10 -0
  165. package/plugins/desktop/shell-environment.mjs +9 -0
  166. package/plugins/desktop-workspace/hooks.mjs +29 -0
  167. package/plugins/desktop-workspace/index.mjs +37 -0
  168. package/plugins/dscode/board.mjs +260 -0
  169. package/plugins/dscode/child-target.mjs +11 -0
  170. package/plugins/dscode/control.mjs +17 -0
  171. package/plugins/dscode/desktop-client.mjs +55 -0
  172. package/plugins/dscode/desktop-host.mjs +37 -0
  173. package/plugins/dscode/index.mjs +270 -0
  174. package/plugins/email/cli.mjs +32 -0
  175. package/plugins/email/contacts.mjs +36 -0
  176. package/plugins/email/desktop-client.mjs +117 -0
  177. package/plugins/email/desktop-host.mjs +54 -0
  178. package/plugins/email/desktop-service.mjs +106 -0
  179. package/plugins/email/gmail-oauth.mjs +69 -0
  180. package/plugins/email/gmail-store.mjs +2 -0
  181. package/plugins/email/gmail.mjs +194 -0
  182. package/plugins/email/imap.mjs +136 -0
  183. package/plugins/email/inbox.d.mts +25 -0
  184. package/plugins/email/inbox.mjs +76 -0
  185. package/plugins/email/smtp.mjs +70 -0
  186. package/plugins/email/store.mjs +32 -0
  187. package/plugins/email-tools/index.mjs +46 -0
  188. package/plugins/exec/cli.mjs +64 -0
  189. package/plugins/exec/index.mjs +112 -0
  190. package/plugins/grok/adapter.mjs +114 -0
  191. package/plugins/grok/auth.mjs +76 -0
  192. package/plugins/grok/billing.mjs +146 -0
  193. package/plugins/grok/index.mjs +122 -0
  194. package/plugins/grok/models.mjs +129 -0
  195. package/plugins/grok/status.mjs +28 -0
  196. package/plugins/grok/wire.mjs +268 -0
  197. package/plugins/hub/catalog.mjs +82 -0
  198. package/plugins/hub/desktop-client.mjs +114 -0
  199. package/plugins/hub/desktop-host.mjs +129 -0
  200. package/plugins/i18n/messages.d.mts +21 -0
  201. package/plugins/i18n/messages.mjs +537 -0
  202. package/plugins/jev/approval.mjs +131 -0
  203. package/plugins/jev/client.mjs +74 -0
  204. package/plugins/jev/index.mjs +99 -0
  205. package/plugins/memory/content.mjs +57 -0
  206. package/plugins/memory/desktop-host.mjs +3 -0
  207. package/plugins/memory/index.mjs +168 -0
  208. package/plugins/memory/pipeline.mjs +79 -0
  209. package/plugins/memory/store.mjs +93 -0
  210. package/plugins/message-source/kind.mjs +20 -0
  211. package/plugins/opencode-go/adapter.mjs +96 -0
  212. package/plugins/opencode-go/command.mjs +128 -0
  213. package/plugins/opencode-go/index.mjs +120 -0
  214. package/plugins/opencode-go/models.mjs +58 -0
  215. package/plugins/opencode-go/oauth.mjs +194 -0
  216. package/plugins/opencode-go/search.mjs +86 -0
  217. package/plugins/opencode-go/usage.mjs +119 -0
  218. package/plugins/opencode-go/version.mjs +10 -0
  219. package/plugins/opencode-go/wire.mjs +73 -0
  220. package/plugins/openrouter/adapter.mjs +232 -0
  221. package/plugins/openrouter/index.mjs +123 -0
  222. package/plugins/openrouter/models.mjs +157 -0
  223. package/plugins/openrouter/search.mjs +113 -0
  224. package/plugins/openrouter/wire.mjs +101 -0
  225. package/plugins/providers/catalog.mjs +160 -0
  226. package/plugins/providers/chat-messages.mjs +120 -0
  227. package/plugins/providers/chat-stream.mjs +174 -0
  228. package/plugins/providers/desktop-client.mjs +84 -0
  229. package/plugins/providers/desktop-host.mjs +63 -0
  230. package/plugins/providers/effort.mjs +35 -0
  231. package/plugins/providers/http-errors.mjs +50 -0
  232. package/plugins/providers/openrouter-account.mjs +171 -0
  233. package/plugins/session-bridge/client.mjs +106 -0
  234. package/plugins/session-bridge/communication.mjs +260 -0
  235. package/plugins/session-bridge/index.mjs +68 -0
  236. package/plugins/session-bridge/mailbox.mjs +267 -0
  237. package/plugins/session-bridge/paths.mjs +21 -0
  238. package/plugins/session-bridge/server.mjs +172 -0
  239. package/plugins/session-bridge/tasks.mjs +229 -0
  240. package/plugins/session-cards/content.mjs +64 -0
  241. package/plugins/session-cards/index.mjs +38 -0
  242. package/plugins/session-cards/manager.mjs +140 -0
  243. package/plugins/session-metrics/attribution.mjs +30 -0
  244. package/plugins/session-metrics/balance.mjs +81 -0
  245. package/plugins/session-metrics/desktop-client.mjs +62 -0
  246. package/plugins/session-metrics/desktop-host.mjs +46 -0
  247. package/plugins/session-metrics/index.mjs +96 -0
  248. package/plugins/session-metrics/pricing.mjs +84 -0
  249. package/plugins/session-metrics/rate.mjs +59 -0
  250. package/plugins/session-metrics/store.mjs +75 -0
  251. package/plugins/session-metrics/turns.mjs +134 -0
  252. package/plugins/session-metrics/view.mjs +271 -0
  253. package/plugins/time-marks/desktop-host.mjs +2 -0
  254. package/plugins/time-marks/index.mjs +116 -0
  255. package/plugins/time-marks/marks.mjs +152 -0
  256. package/plugins/triggers/cli.mjs +558 -0
  257. package/plugins/triggers/commands.mjs +143 -0
  258. package/plugins/triggers/config.mjs +264 -0
  259. package/plugins/triggers/desktop-client.mjs +155 -0
  260. package/plugins/triggers/desktop-host.mjs +88 -0
  261. package/plugins/triggers/desktop-management.mjs +97 -0
  262. package/plugins/triggers/desktop-scheduler.mjs +125 -0
  263. package/plugins/triggers/host.mjs +24 -0
  264. package/plugins/triggers/index.mjs +27 -0
  265. package/plugins/triggers/job-cli.mjs +110 -0
  266. package/plugins/triggers/jobs.mjs +156 -0
  267. package/plugins/triggers/launchd.mjs +101 -0
  268. package/plugins/triggers/lease.mjs +24 -0
  269. package/plugins/triggers/log.mjs +104 -0
  270. package/plugins/triggers/management.mjs +181 -0
  271. package/plugins/triggers/options.mjs +117 -0
  272. package/plugins/triggers/overlay.mjs +7 -0
  273. package/plugins/triggers/poll.mjs +65 -0
  274. package/plugins/triggers/run.mjs +157 -0
  275. package/plugins/triggers/schedule.mjs +40 -0
  276. package/plugins/triggers/scheduler-service.mjs +46 -0
  277. package/plugins/triggers/scheduler.mjs +92 -0
  278. package/plugins/triggers/session-run.mjs +125 -0
  279. package/plugins/triggers/session.mjs +48 -0
  280. package/plugins/triggers/source-emit.mjs +19 -0
  281. package/plugins/triggers/source-host.mjs +78 -0
  282. package/plugins/triggers/source-ingress.mjs +70 -0
  283. package/plugins/triggers/source-sandbox.mjs +35 -0
  284. package/plugins/triggers/sources.mjs +45 -0
  285. package/plugins/triggers/spool.mjs +145 -0
  286. package/plugins/triggers/tools.mjs +67 -0
  287. package/plugins/triggers/unattended.mjs +18 -0
  288. package/plugins/tui-tools/composition.mjs +28 -0
  289. package/plugins/tui-tools/desktop-host.mjs +2 -0
  290. package/plugins/tui-tools/doctor-cli.mjs +22 -0
  291. package/plugins/tui-tools/doctor.mjs +148 -0
  292. package/plugins/tui-tools/hook-sources.mjs +113 -0
  293. package/plugins/tui-tools/hooks.mjs +23 -0
  294. package/plugins/tui-tools/index.mjs +268 -0
  295. package/plugins/tui-tools/sandbox-runner.mjs +123 -0
  296. package/plugins/tui-tools/shell.mjs +27 -0
  297. package/plugins/tui-tools/update.mjs +111 -0
  298. package/plugins/tui-tools/workspace-discovery.mjs +129 -0
  299. package/plugins/ultra/policy.mjs +42 -0
  300. package/plugins/worktree-subagent/worktree.mjs +56 -0
  301. package/runtime-sources.json +94 -0
  302. package/vendor/bash/LICENSE +21 -0
  303. package/vendor/bash/index.js +717 -0
  304. package/vendor/bash/types/background.d.ts +50 -0
  305. package/vendor/bash/types/index.d.ts +38 -0
  306. package/vendor/bash/types/render.d.ts +57 -0
  307. package/vendor/command-goal/LICENSE +21 -0
  308. package/vendor/command-goal/index.js +210 -0
  309. package/vendor/command-goal/types/index.d.ts +10 -0
  310. package/vendor/computer-use/LICENSE +21 -0
  311. package/vendor/computer-use/lib/approval-policy.js +12 -0
  312. package/vendor/computer-use/lib/approval-policy.js.map +1 -0
  313. package/vendor/computer-use/lib/artifacts.js +79 -0
  314. package/vendor/computer-use/lib/artifacts.js.map +1 -0
  315. package/vendor/computer-use/lib/backend.js +3 -0
  316. package/vendor/computer-use/lib/backend.js.map +1 -0
  317. package/vendor/computer-use/lib/client.js +369 -0
  318. package/vendor/computer-use/lib/client.js.map +1 -0
  319. package/vendor/computer-use/lib/config.js +159 -0
  320. package/vendor/computer-use/lib/config.js.map +1 -0
  321. package/vendor/computer-use/lib/confirmations.js +99 -0
  322. package/vendor/computer-use/lib/confirmations.js.map +1 -0
  323. package/vendor/computer-use/lib/diff.js +58 -0
  324. package/vendor/computer-use/lib/diff.js.map +1 -0
  325. package/vendor/computer-use/lib/errors.js +22 -0
  326. package/vendor/computer-use/lib/errors.js.map +1 -0
  327. package/vendor/computer-use/lib/exposure.js +244 -0
  328. package/vendor/computer-use/lib/exposure.js.map +1 -0
  329. package/vendor/computer-use/lib/index.js +66 -0
  330. package/vendor/computer-use/lib/index.js.map +1 -0
  331. package/vendor/computer-use/lib/leases.js +262 -0
  332. package/vendor/computer-use/lib/leases.js.map +1 -0
  333. package/vendor/computer-use/lib/providers/macos.js +243 -0
  334. package/vendor/computer-use/lib/providers/macos.js.map +1 -0
  335. package/vendor/computer-use/lib/providers/native-helper.js +726 -0
  336. package/vendor/computer-use/lib/providers/native-helper.js.map +1 -0
  337. package/vendor/computer-use/lib/providers/unsupported.js +51 -0
  338. package/vendor/computer-use/lib/providers/unsupported.js.map +1 -0
  339. package/vendor/computer-use/lib/service.js +608 -0
  340. package/vendor/computer-use/lib/service.js.map +1 -0
  341. package/vendor/computer-use/lib/skill.js +136 -0
  342. package/vendor/computer-use/lib/skill.js.map +1 -0
  343. package/vendor/computer-use/lib/target-resolver.js +180 -0
  344. package/vendor/computer-use/lib/target-resolver.js.map +1 -0
  345. package/vendor/computer-use/lib/tools.js +511 -0
  346. package/vendor/computer-use/lib/tools.js.map +1 -0
  347. package/vendor/computer-use/lib/types/approval-policy.d.ts +13 -0
  348. package/vendor/computer-use/lib/types/approval-policy.d.ts.map +1 -0
  349. package/vendor/computer-use/lib/types/artifacts.d.ts +10 -0
  350. package/vendor/computer-use/lib/types/artifacts.d.ts.map +1 -0
  351. package/vendor/computer-use/lib/types/backend.d.ts +124 -0
  352. package/vendor/computer-use/lib/types/backend.d.ts.map +1 -0
  353. package/vendor/computer-use/lib/types/client/index.d.ts +83 -0
  354. package/vendor/computer-use/lib/types/client/index.d.ts.map +1 -0
  355. package/vendor/computer-use/lib/types/config.d.ts +94 -0
  356. package/vendor/computer-use/lib/types/config.d.ts.map +1 -0
  357. package/vendor/computer-use/lib/types/confirmations.d.ts +23 -0
  358. package/vendor/computer-use/lib/types/confirmations.d.ts.map +1 -0
  359. package/vendor/computer-use/lib/types/diff.d.ts +5 -0
  360. package/vendor/computer-use/lib/types/diff.d.ts.map +1 -0
  361. package/vendor/computer-use/lib/types/errors.d.ts +15 -0
  362. package/vendor/computer-use/lib/types/errors.d.ts.map +1 -0
  363. package/vendor/computer-use/lib/types/exposure.d.ts +30 -0
  364. package/vendor/computer-use/lib/types/exposure.d.ts.map +1 -0
  365. package/vendor/computer-use/lib/types/index.d.ts +21 -0
  366. package/vendor/computer-use/lib/types/index.d.ts.map +1 -0
  367. package/vendor/computer-use/lib/types/leases.d.ts +81 -0
  368. package/vendor/computer-use/lib/types/leases.d.ts.map +1 -0
  369. package/vendor/computer-use/lib/types/providers/macos.d.ts +39 -0
  370. package/vendor/computer-use/lib/types/providers/macos.d.ts.map +1 -0
  371. package/vendor/computer-use/lib/types/providers/native-helper.d.ts +48 -0
  372. package/vendor/computer-use/lib/types/providers/native-helper.d.ts.map +1 -0
  373. package/vendor/computer-use/lib/types/providers/unsupported.d.ts +23 -0
  374. package/vendor/computer-use/lib/types/providers/unsupported.d.ts.map +1 -0
  375. package/vendor/computer-use/lib/types/service.d.ts +58 -0
  376. package/vendor/computer-use/lib/types/service.d.ts.map +1 -0
  377. package/vendor/computer-use/lib/types/skill.d.ts +14 -0
  378. package/vendor/computer-use/lib/types/skill.d.ts.map +1 -0
  379. package/vendor/computer-use/lib/types/target-resolver.d.ts +38 -0
  380. package/vendor/computer-use/lib/types/target-resolver.d.ts.map +1 -0
  381. package/vendor/computer-use/lib/types/tools.d.ts +6 -0
  382. package/vendor/computer-use/lib/types/tools.d.ts.map +1 -0
  383. package/vendor/computer-use/lib/types/types.d.ts +270 -0
  384. package/vendor/computer-use/lib/types/types.d.ts.map +1 -0
  385. package/vendor/computer-use/lib/types/web.d.ts +33 -0
  386. package/vendor/computer-use/lib/types/web.d.ts.map +1 -0
  387. package/vendor/computer-use/lib/types.js +9 -0
  388. package/vendor/computer-use/lib/types.js.map +1 -0
  389. package/vendor/computer-use/lib/web.js +188 -0
  390. package/vendor/computer-use/lib/web.js.map +1 -0
  391. package/vendor/computer-use/native/macos/Sources/Fixture/main.swift +412 -0
  392. package/vendor/computer-use/native/macos/Sources/Helper/CursorImage.swift +11 -0
  393. package/vendor/computer-use/native/macos/Sources/Helper/CursorMotion.swift +67 -0
  394. package/vendor/computer-use/native/macos/Sources/Helper/CursorOverlay.swift +840 -0
  395. package/vendor/computer-use/native/macos/Sources/Helper/TargetedPointer.swift +327 -0
  396. package/vendor/computer-use/native/macos/Sources/Helper/WindowNumberMatcher.swift +29 -0
  397. package/vendor/computer-use/native/macos/Sources/Helper/main.swift +1244 -0
  398. package/vendor/computer-use/native/macos/Sources/Monitor/main.swift +153 -0
  399. package/vendor/computer-use/native/macos/bin/dsh-computer-use-helper +0 -0
  400. package/vendor/computer-use/native/macos/manifest.json +14 -0
  401. package/vendor/persistent/LICENSE +21 -0
  402. package/vendor/persistent/index.js +411 -0
  403. package/vendor/persistent/types/index.d.ts +24 -0
  404. package/vendor/subagent/LICENSE +21 -0
  405. package/vendor/subagent/index.js +694 -0
  406. package/vendor/subagent/invariant.js +52 -0
  407. package/vendor/subagent/model-selection-settings.js +68 -0
  408. package/vendor/subagent/types/index.d.ts +82 -0
  409. package/vendor/subagent/types/invariant.d.ts +16 -0
  410. package/vendor/subagent/types/list-models.d.ts +10 -0
  411. package/vendor/subagent/types/model-selection-settings.d.ts +45 -0
  412. package/vendor/subagent/types/model-selection-state.d.ts +48 -0
  413. package/vendor/subagent/types/model-selection.d.ts +81 -0
  414. package/vendor/subagent-core/LICENSE +21 -0
  415. package/vendor/subagent-core/index.js +3202 -0
  416. package/vendor/subagent-core/invariant.js +76 -0
  417. package/vendor/subagent-core/typert.host.d.ts +3 -0
  418. package/vendor/subagent-core/typert.host.js +978 -0
  419. package/vendor/subagent-core/typert.remote-client.d.ts +25 -0
  420. package/vendor/subagent-core/typert.remote-client.js +114 -0
  421. package/vendor/subagent-core/types/archive-admission.d.ts +16 -0
  422. package/vendor/subagent-core/types/archive-admission.js +151 -0
  423. package/vendor/subagent-core/types/assistant-output.d.ts +48 -0
  424. package/vendor/subagent-core/types/assistant-output.js +73 -0
  425. package/vendor/subagent-core/types/catalog.d.ts +84 -0
  426. package/vendor/subagent-core/types/catalog.js +109 -0
  427. package/vendor/subagent-core/types/child-agent.d.ts +156 -0
  428. package/vendor/subagent-core/types/child-agent.js +213 -0
  429. package/vendor/subagent-core/types/client.d.ts +8 -0
  430. package/vendor/subagent-core/types/client.js +7 -0
  431. package/vendor/subagent-core/types/continuation-activation.d.ts +267 -0
  432. package/vendor/subagent-core/types/continuation-activation.js +700 -0
  433. package/vendor/subagent-core/types/continuation-messages.d.ts +62 -0
  434. package/vendor/subagent-core/types/continuation-messages.js +106 -0
  435. package/vendor/subagent-core/types/continuation.d.ts +124 -0
  436. package/vendor/subagent-core/types/continuation.js +455 -0
  437. package/vendor/subagent-core/types/control-types.d.ts +135 -0
  438. package/vendor/subagent-core/types/control-types.js +8 -0
  439. package/vendor/subagent-core/types/control.d.ts +45 -0
  440. package/vendor/subagent-core/types/control.js +79 -0
  441. package/vendor/subagent-core/types/depth.d.ts +31 -0
  442. package/vendor/subagent-core/types/depth.js +39 -0
  443. package/vendor/subagent-core/types/descriptor.d.ts +144 -0
  444. package/vendor/subagent-core/types/descriptor.js +193 -0
  445. package/vendor/subagent-core/types/error.d.ts +11 -0
  446. package/vendor/subagent-core/types/error.js +14 -0
  447. package/vendor/subagent-core/types/inbox.d.ts +43 -0
  448. package/vendor/subagent-core/types/inbox.js +61 -0
  449. package/vendor/subagent-core/types/index.d.ts +320 -0
  450. package/vendor/subagent-core/types/index.js +475 -0
  451. package/vendor/subagent-core/types/internal.d.ts +58 -0
  452. package/vendor/subagent-core/types/internal.js +58 -0
  453. package/vendor/subagent-core/types/invariant.d.ts +13 -0
  454. package/vendor/subagent-core/types/invariant.js +91 -0
  455. package/vendor/subagent-core/types/lifecycle.d.ts +113 -0
  456. package/vendor/subagent-core/types/lifecycle.js +180 -0
  457. package/vendor/subagent-core/types/list-children.d.ts +36 -0
  458. package/vendor/subagent-core/types/list-children.js +166 -0
  459. package/vendor/subagent-core/types/out-of-process.d.ts +118 -0
  460. package/vendor/subagent-core/types/out-of-process.js +219 -0
  461. package/vendor/subagent-core/types/projection-types.d.ts +82 -0
  462. package/vendor/subagent-core/types/projection-types.js +7 -0
  463. package/vendor/subagent-core/types/projection.d.ts +100 -0
  464. package/vendor/subagent-core/types/projection.js +157 -0
  465. package/vendor/subagent-core/types/run-settlement.d.ts +17 -0
  466. package/vendor/subagent-core/types/run-settlement.js +70 -0
  467. package/vendor/subagent-core/types/types.d.ts +377 -0
  468. package/vendor/subagent-core/types/types.js +19 -0
  469. package/vendor/subagent-driver/LICENSE +21 -0
  470. package/vendor/subagent-driver/index.js +255 -0
  471. package/vendor/subagent-driver/types/index.d.ts +33 -0
  472. package/vendor/subagent-driver/types/structured.d.ts +42 -0
  473. package/vendor/subagent-fork/LICENSE +21 -0
  474. package/vendor/subagent-fork/index.js +61 -0
  475. package/vendor/subagent-fork/types/index.d.ts +20 -0
  476. package/vendor/subagent-spawn/LICENSE +21 -0
  477. package/vendor/subagent-spawn/index.js +45 -0
  478. package/vendor/subagent-spawn/types/index.d.ts +19 -0
  479. package/vendor/terminal/LICENSE +21 -0
  480. package/vendor/terminal/index.js +1093 -0
  481. package/vendor/terminal/types/config.d.ts +82 -0
  482. package/vendor/terminal/types/index.d.ts +36 -0
  483. package/vendor/terminal/types/sanitize.d.ts +47 -0
  484. package/vendor/terminal/types/session.d.ts +87 -0
@@ -0,0 +1,232 @@
1
+ import { IMAGE_OFFLOAD_REQUIRED_CODE, LlmAdapter, LlmError, ReasoningEffortId, attributionHeaders, contentHasImage, offloadedImageText, projectOffloadedImages, requiredImageOffload } from '@deepseek-ai/dsh-llm';
2
+ import { requestImageDimensions } from '@deepseek-ai/dsh-attachment';
3
+ import { listOpenRouterModels, openRouterModel } from './models.mjs';
4
+ import { PROVIDER, effortInfo, errorCode, errorMessage, modelReasoning, requestBody, retryAfterMs, sseData, translate } from './wire.mjs';
5
+
6
+ export { PROVIDER };
7
+ /** Context assumed for a model the listing does not size. */
8
+ export const DEFAULT_CONTEXT_WINDOW = 262144;
9
+ /** Output cap materialized when a caller names none; OpenRouter reserves credit for it. */
10
+ export const DEFAULT_OUTPUT_CAP = 131072;
11
+ const APP_URL = 'https://github.com/qiz029/dscode';
12
+ const IMAGE_POLICY = Object.freeze({ maxPixels: 2048 * 2048, maxBytes: 1024 * 1024 });
13
+
14
+ /**
15
+ * What `/model` offers on this route: the labs DSCODE tunes and tests, plus the current
16
+ * flagship line of the mainstream Western labs. OpenRouter carries hundreds of tool-calling
17
+ * models, and an alphabetical wall of them is not a picker, so the list is curated by hand;
18
+ * an id OpenRouter retires drops out of the picker until this list is edited.
19
+ * Only the picker narrows: `resolveModel` still serves every id the listing knows, so a
20
+ * session already on a trimmed model, `exec --model` and provider switches keep working.
21
+ */
22
+ export const LISTED_MODELS = Object.freeze([
23
+ 'deepseek/deepseek-v4-pro', 'deepseek/deepseek-v4-flash', 'deepseek/deepseek-v4.1-flash',
24
+ 'z-ai/glm-5.3', 'z-ai/glm-5.3-flash', 'z-ai/glm-5.3-flashx',
25
+ 'moonshotai/kimi-k3', 'moonshotai/kimi-k2.7-code', 'moonshotai/kimi-k2.6',
26
+ 'qwen/qwen3.8-max-0902', 'qwen/qwen3.8-flash', 'qwen/qwen3.8-27b', 'qwen/qwen3.7-plus',
27
+ 'xiaomi/mimo-v2.6-pro', 'xiaomi/mimo-v2.6-flash', 'xiaomi/mimo-v2.6-pro-ultraspeed',
28
+ 'anthropic/claude-opus-5', 'anthropic/claude-sonnet-5', 'anthropic/claude-fable-5.1', 'anthropic/claude-haiku-4.5',
29
+ 'openai/gpt-6-astra-pro', 'openai/gpt-6-astra', 'openai/gpt-5.6-luna-pro', 'openai/gpt-5.6-terra-pro',
30
+ 'google/gemini-3.8-flash', 'google/gemini-3.1-pro-preview',
31
+ 'x-ai/grok-4.7', 'x-ai/grok-build-0.1',
32
+ ]);
33
+ const LISTED = new Set(LISTED_MODELS);
34
+
35
+ function modelInfo(provider, id, entry) {
36
+ return { provider, id, name: entry?.name ?? id, inputModalities: entry?.inputModalities?.length ? [...entry.inputModalities] : ['text'] };
37
+ }
38
+
39
+ function collectImages(blocks, refs) {
40
+ for (const block of blocks) {
41
+ if (block.type === 'image') refs.set(block.attachment.attachmentId, block.attachment);
42
+ else if (block.type === 'tool-result') collectImages(block.content, refs);
43
+ }
44
+ }
45
+
46
+ /**
47
+ * OpenRouter chat completions as a harness adapter: fetch + SSE with the model
48
+ * directory, reasoning controls and prices of OpenRouter's live listing. Connection
49
+ * facts and the key resolve per request, so settings and `/login` changes reach the
50
+ * next call.
51
+ */
52
+ export class OpenRouterAdapter extends LlmAdapter {
53
+ /**
54
+ * @param config - `options()` connection facts, `resolveApiKey(connection)`, `ensureModels()`,
55
+ * optional `resolveAttachments()`, `resolveImageAccess(attachments, ref)` and `fetch`.
56
+ */
57
+ constructor(config) {
58
+ super();
59
+ this.config = config;
60
+ }
61
+
62
+ providerInfo(provider) {
63
+ return { id: provider, name: this.config.displayName ?? 'OpenRouter' };
64
+ }
65
+
66
+ providerRetryPolicy() {
67
+ return this.config.options().retryPolicy;
68
+ }
69
+
70
+ /** The curated picker list, narrowed to entries that can drive an agent: text output and tool calls. */
71
+ async listModels(provider) {
72
+ await this.config.ensureModels();
73
+ return listOpenRouterModels().filter(([id, entry]) => LISTED.has(id) && entry.tools !== false && entry.textOutput !== false).map(([id, entry]) => modelInfo(provider, id, entry));
74
+ }
75
+
76
+ async resolveModel(provider, model) {
77
+ await this.config.ensureModels();
78
+ const entry = openRouterModel(model);
79
+ const reasoning = modelReasoning(model, entry);
80
+ return {
81
+ ...modelInfo(provider, model, entry),
82
+ context: { contextWindow: entry?.contextWindow ?? DEFAULT_CONTEXT_WINDOW },
83
+ ...(entry?.maxOutput ? { defaultMaxTokens: Math.min(entry.maxOutput, DEFAULT_OUTPUT_CAP) } : {}),
84
+ ...(reasoning ? { reasoning: {
85
+ efforts: reasoning.levels.map(id => ({ ...effortInfo(id), id: ReasoningEffortId(id) })),
86
+ ...(reasoning.defaultEffort ? { defaultEffort: ReasoningEffortId(reasoning.defaultEffort) } : {}),
87
+ } } : {}),
88
+ };
89
+ }
90
+
91
+ /** Name in error messages; a subclass serving another route renames it. */
92
+ get label() {
93
+ return 'OpenRouter';
94
+ }
95
+
96
+ /** The model's catalog entry, after the catalog is loaded. */
97
+ async modelEntry(model) {
98
+ await this.config.ensureModels();
99
+ return openRouterModel(model);
100
+ }
101
+
102
+ /** The chat-completions body for one prepared request. */
103
+ requestBody(options, context) {
104
+ return requestBody(options, context);
105
+ }
106
+
107
+ /** Route headers beyond auth, content type and the user agent. */
108
+ requestHeaders() {
109
+ return { 'HTTP-Referer': APP_URL, 'X-OpenRouter-Title': 'DSCODE', 'X-OpenRouter-Categories': 'cli-agent' };
110
+ }
111
+
112
+ /**
113
+ * Where and how the next request authenticates: the bearer secret, the base URL and any
114
+ * extra headers. A subclass whose credential carries its own endpoint overrides this.
115
+ */
116
+ async resolveAuth(connection) {
117
+ return { apiKey: await this.config.resolveApiKey(connection), baseURL: connection.baseURL, headers: {} };
118
+ }
119
+
120
+ /** Options for the stream translation: the replay kind and error label. */
121
+ translateOptions(options) {
122
+ return { model: options.model };
123
+ }
124
+
125
+ async *stream(options) {
126
+ const connection = this.config.options();
127
+ const label = this.label;
128
+ // The endpoint the request went to, for error messages: the route's own until auth names another.
129
+ let endpoint = connection.baseURL;
130
+ const idle = new AbortController(), consumer = new AbortController();
131
+ let timer;
132
+ const pulse = () => {
133
+ clearTimeout(timer);
134
+ timer = setTimeout(() => idle.abort(new Error(`${label} stream idle`)), connection.streamIdleTimeoutMs);
135
+ timer.unref?.();
136
+ };
137
+ const signal = AbortSignal.any([idle.signal, consumer.signal, ...(options.signal ? [options.signal] : [])]);
138
+ try {
139
+ const auth = await this.resolveAuth(connection);
140
+ endpoint = auth.baseURL;
141
+ const entry = await this.modelEntry(options.model);
142
+ pulse();
143
+ const images = await this.prepareImages(options, entry, connection, signal);
144
+ const body = this.requestBody(images?.options ?? options, { entry, images });
145
+ const fetchImpl = this.config.fetch ?? globalThis.fetch;
146
+ let response;
147
+ try {
148
+ response = await fetchImpl(`${auth.baseURL}/chat/completions`, {
149
+ method: 'POST',
150
+ headers: {
151
+ authorization: `Bearer ${auth.apiKey}`,
152
+ 'content-type': 'application/json',
153
+ accept: 'text/event-stream',
154
+ ...attributionHeaders(),
155
+ ...this.requestHeaders(options),
156
+ ...auth.headers,
157
+ },
158
+ body: JSON.stringify(body),
159
+ signal,
160
+ });
161
+ } catch (error) {
162
+ if (signal.aborted) throw error;
163
+ throw new LlmError(`${label} request to ${endpoint} failed`, 'TRANSPORT', { cause: error });
164
+ }
165
+ // A rejected request, or a 200 whose JSON body holds only an error.
166
+ if (!response.ok || !response.headers.get('content-type')?.includes('text/event-stream')) {
167
+ const raw = await response.text();
168
+ let error;
169
+ try { error = JSON.parse(raw)?.error; } catch { /* not JSON */ }
170
+ if (response.ok && error === undefined) throw new LlmError(`${label} returned a non-stream response: ${raw.slice(0, 120)}`, 'MALFORMED_RESPONSE');
171
+ const delay = retryAfterMs(response.headers.get('retry-after'));
172
+ const status = response.ok ? (Number.isInteger(error?.code) ? error.code : undefined) : response.status;
173
+ throw new LlmError(errorMessage(error, `${label} API error (HTTP ${response.status})`), errorCode(response.ok ? undefined : response.status, error), {
174
+ cause: new Error(raw.length > 0 ? raw : `${label} HTTP ${response.status}`),
175
+ ...(status === undefined ? {} : { status }),
176
+ ...(delay === undefined ? {} : { providerRetryAfterMs: delay }),
177
+ });
178
+ }
179
+ if (!response.body) throw new LlmError(`${label} returned no response body`, 'EMPTY_RESPONSE');
180
+ for await (const chunk of translate(sseData(response.body, pulse), this.translateOptions(options))) {
181
+ // The idle clock measures the provider, not a slow consumer.
182
+ clearTimeout(timer);
183
+ yield chunk;
184
+ pulse();
185
+ }
186
+ } catch (error) {
187
+ if (idle.signal.aborted && !options.signal?.aborted) throw new LlmError(`${label} stream idle timeout after ${connection.streamIdleTimeoutMs}ms`, 'TIMEOUT', { cause: error });
188
+ if (options.signal?.aborted) throw new LlmError(`${label} request aborted by caller`, 'ABORTED', { cause: error });
189
+ if (error instanceof LlmError) throw error;
190
+ throw new LlmError(`${label} API stream from ${endpoint} failed`, 'TRANSPORT', { cause: error });
191
+ } finally {
192
+ clearTimeout(timer);
193
+ consumer.abort(`${label} stream consumer stopped`);
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Read request versions of every retained image.
199
+ *
200
+ * DSH 0.1.7 made the offloaded set a durable surface fact: a route projects what the
201
+ * surface already offloaded into text and reports how much more must go with
202
+ * `IMAGE_OFFLOAD_REQUIRED`, instead of dropping the oldest occurrences itself. The
203
+ * `compaction-image-offload` executor records that choice and retries, so the decision
204
+ * survives a route change, resume and replay rather than being remade per request.
205
+ */
206
+ async prepareImages(options, entry, connection, signal) {
207
+ const attachments = this.config.resolveAttachments?.();
208
+ const access = ref => (attachments === undefined ? undefined : this.config.resolveImageAccess?.(attachments, ref));
209
+ const messages = projectOffloadedImages(options.messages, ref => offloadedImageText(ref, access(ref)));
210
+ const withImages = messages.some(message => contentHasImage(message.content));
211
+ if (!withImages) return messages === options.messages ? undefined : { options: { ...options, messages: [...messages] }, versions: new Map(), access };
212
+ if (!entry?.inputModalities?.includes('image')) throw new LlmError(`${this.label} model "${options.model}" does not accept image input.`, 'UNSUPPORTED_CONTENT');
213
+ if (attachments === undefined) throw new LlmError(`${this.label} image input requires the durable attachment service.`, 'UNSUPPORTED_CONTENT');
214
+ const refs = new Map();
215
+ for (const message of messages) collectImages(message.content, refs);
216
+ const versions = new Map(await Promise.all([...refs.values()].map(async ref => [ref.attachmentId, await attachments.readImageRequest(ref,
217
+ { ...requestImageDimensions(ref.width, ref.height, IMAGE_POLICY.maxPixels), maxBytes: IMAGE_POLICY.maxBytes }, signal)])));
218
+ const offloadImages = requiredImageOffload(
219
+ messages,
220
+ { representation: 'base64', maxBytes: connection.maxRequestImageBytes, byteQuantum: 1 },
221
+ block => versions.get(block.attachment.attachmentId).bytes,
222
+ );
223
+ if (offloadImages > 0) {
224
+ throw new LlmError(
225
+ `${this.label} request images exceed ${connection.maxRequestImageBytes} bytes; ${offloadImages} more oldest occurrence(s) must be offloaded.`,
226
+ IMAGE_OFFLOAD_REQUIRED_CODE,
227
+ { offloadImages },
228
+ );
229
+ }
230
+ return { options: { ...options, messages: [...messages] }, versions, access };
231
+ }
232
+ }
@@ -0,0 +1,123 @@
1
+ import z from '@deepseek-ai/schemastery';
2
+ import { LlmError, RetryPolicySchema, assertUsableApiKey, resolveImageAttachmentAccess, resolveRetryPolicy } from '@deepseek-ai/dsh-llm';
3
+ import { credentialRef } from '@deepseek-ai/dsh-credentials';
4
+ import { deepEqualJson } from '@deepseek-ai/dsh-util-values';
5
+ import { plainConfig } from '../cordis-config/plain.mjs';
6
+ import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment';
7
+ import { OpenRouterAdapter, PROVIDER } from './adapter.mjs';
8
+ import { ensureOpenRouterModels } from './models.mjs';
9
+ import { OpenRouterSearchProvider, RoutedSearchProvider } from './search.mjs';
10
+ import { ExaSearchProvider } from '../opencode-go/search.mjs';
11
+ import { dscodeVersion } from '../opencode-go/version.mjs';
12
+
13
+ // The `openrouter` route: DSCODE's own OpenRouter adapter over its live model
14
+ // listing, configured by this entry's own profile row, plus web search that
15
+ // follows the session's route (including Exa for an OpenCode Go session).
16
+ export const name = 'dscode-openrouter';
17
+ export const inject = ['llm'];
18
+ const NS = 'llm-openrouter';
19
+ const DEFAULT_BASE_URL = 'https://openrouter.ai/api/v1';
20
+
21
+ // DSH 0.1.7 replaced settings sections with volatile configuration: a field marked
22
+ // `.volatile()` reaches the plugin as a reference the Settings form and a profile edit
23
+ // both write, so each read below sees the current value without a change callback.
24
+ export const Config = z.object({
25
+ providerName: z.union(['openrouter', 'dscode-openrouter']).default('openrouter'),
26
+ apiKeyEnv: z.string().role('credential-ref').default('OPENROUTER_API_KEY').volatile(),
27
+ baseURL: z.string().default(DEFAULT_BASE_URL).volatile(),
28
+ streamIdleTimeoutMs: z.number().min(1).default(300000).volatile(),
29
+ maxRequestImageBytes: z.number().step(1).min(1).default(20 * 1024 * 1024).volatile(),
30
+ searchModel: z.string().default('deepseek/deepseek-v4-flash').volatile(),
31
+ retryPolicy: RetryPolicySchema.volatile(),
32
+ });
33
+
34
+
35
+ /** Validated connection facts from one config snapshot. */
36
+ export function resolveOptions(config = {}) {
37
+ const streamIdleTimeoutMs = config.streamIdleTimeoutMs ?? 300000;
38
+ if (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0) throw new Error(`${name}: streamIdleTimeoutMs must be a positive number`);
39
+ const baseURL = (config.baseURL || DEFAULT_BASE_URL).replace(/\/+$/, '');
40
+ if (!URL.canParse(baseURL)) throw new Error(`${name}: baseURL must be a URL`);
41
+ return {
42
+ apiKeyEnv: credentialRef(config.apiKeyEnv || 'OPENROUTER_API_KEY'),
43
+ baseURL,
44
+ streamIdleTimeoutMs,
45
+ maxRequestImageBytes: config.maxRequestImageBytes ?? 20 * 1024 * 1024,
46
+ searchModel: config.searchModel || 'deepseek/deepseek-v4-flash',
47
+ retryPolicy: resolveRetryPolicy(config.retryPolicy, `${name}: retryPolicy`),
48
+ };
49
+ }
50
+
51
+ export function apply(ctx, config = {}) {
52
+ const provider = config.providerName ?? PROVIDER;
53
+ const displayName = provider === 'dscode-openrouter' ? 'DSCODE OpenRouter' : 'OpenRouter';
54
+ // The TUI renders this route's own page, so Settings must not generate a form for it.
55
+ ctx.inject(['settings'], settingsCtx => {
56
+ settingsCtx.effect(() => settingsCtx.settings.configure({ auto: false }, ctx.fiber));
57
+ });
58
+ let lastGood;
59
+ const options = () => {
60
+ try {
61
+ lastGood = resolveOptions(plainConfig(config));
62
+ return lastGood;
63
+ } catch (error) {
64
+ if (lastGood === undefined) throw error;
65
+ ctx.logger.error(`${name}: keeping the last good configuration after an invalid edit`);
66
+ ctx.logger.error(error);
67
+ return lastGood;
68
+ }
69
+ };
70
+ options();
71
+ const resolveKey = async ref => {
72
+ const credentials = ctx.get('credentials');
73
+ if (credentials !== undefined) return (await credentials.resolve(ref))?.value || undefined;
74
+ return launchEnvironmentOf(ctx).get(ref)?.value || undefined;
75
+ };
76
+ const home = process.env.DSH_HOME;
77
+ const ensureModels = () => ensureOpenRouterModels({ home });
78
+ const adapter = new OpenRouterAdapter({
79
+ displayName,
80
+ options,
81
+ ensureModels,
82
+ resolveApiKey: async connection => {
83
+ const key = await resolveKey(connection.apiKeyEnv);
84
+ if (key !== undefined) return assertUsableApiKey(key, name, connection.apiKeyEnv);
85
+ throw new LlmError(`${name}: no API key for OpenRouter; run /login openrouter, or export ${connection.apiKeyEnv} in the launching environment`, 'MISSING_CREDENTIAL');
86
+ },
87
+ resolveAttachments: () => ctx.get('attachments'),
88
+ resolveImageAccess: (attachments, ref) => resolveImageAttachmentAccess(attachments, hostPath => ctx.get('fs')?.processPathFromHostPath(hostPath), ref),
89
+ });
90
+ ctx.llm.registerConfigurableProviders([{ provider, displayName, settingsNs: ctx.fiber.entry?.options.id ?? NS, settingsPath: [] }]);
91
+ const registration = ctx.llm.registerAdapter([provider], adapter);
92
+ // The retry policy is captured at registration, and a reference carries no change
93
+ // callback: re-register the route when an edit lands on a different policy.
94
+ let registeredPolicy = options().retryPolicy;
95
+ ctx.on('loader/volatile-update', () => {
96
+ let policy;
97
+ try { policy = options().retryPolicy; } catch (error) { ctx.logger.warn(error); return; }
98
+ if (deepEqualJson(policy, registeredPolicy)) return;
99
+ registration.replace([provider]);
100
+ registeredPolicy = policy;
101
+ });
102
+ ctx.inject(['web'], webCtx => {
103
+ const openrouter = new OpenRouterSearchProvider(() => {
104
+ const connection = options();
105
+ return { baseURL: connection.baseURL, model: connection.searchModel, resolveApiKey: () => resolveKey(connection.apiKeyEnv) };
106
+ });
107
+ webCtx.web.registerSearchProvider(openrouter);
108
+ webCtx.web.registerSearchProvider(new RoutedSearchProvider({
109
+ openrouter,
110
+ // `ctx.web` keeps no per-call selection; the DeepSeek provider is looked up in its registry.
111
+ deepseek: () => webCtx.web.searchProviders?.get?.('deepseek-official'),
112
+ // An OpenCode Go session searches through Exa, as OpenCode's own client does.
113
+ exa: new ExaSearchProvider(() => ({ resolveApiKey: () => resolveKey(credentialRef('EXA_API_KEY')), userAgent: `dscode/${dscodeVersion() ?? '0.0.0'}` })),
114
+ currentProvider: () => {
115
+ const agent = ctx.get('agents')?.currentInitiator?.();
116
+ return agent?.session?.requestHeader?.()?.config?.provider ?? agent?.options?.provider;
117
+ },
118
+ hasKey: async ref => (await resolveKey(credentialRef(ref))) !== undefined,
119
+ }));
120
+ });
121
+ // Warm the listing for an OpenRouter user, so the first /model opens with every model.
122
+ void resolveKey(options().apiKeyEnv).then(key => key === undefined ? undefined : ensureModels(), () => {});
123
+ }
@@ -0,0 +1,157 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ // OpenRouter's public model listing: context sizes, input modalities, reasoning
5
+ // controls and list prices for every model it serves. The OpenRouter adapter
6
+ // resolves models from it and the session metrics price calls from it, so one
7
+ // table, kept for a day in DSH_HOME, backs both. The listing quotes USD per
8
+ // token; the table keeps USD per million.
9
+ export const OPENROUTER_MODELS_URL = 'https://openrouter.ai/api/v1/models';
10
+ /** How long a first model lookup waits for the listing before answering without it. */
11
+ export const FORCED_LOAD_TIMEOUT_MS = 10_000;
12
+ const MAX_AGE_MS = 24 * 60 * 60 * 1000;
13
+ /** How long a failed listing (or an unusable cache file) waits before the next attempt. */
14
+ export const RETRY_MS = 10 * 60 * 1000;
15
+ const FILE = 'openrouter-models.json';
16
+ const VERSION = 2;
17
+ const EFFORTS = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
18
+ let table = { fetchedAt: 0, models: {} };
19
+ /** When the on-disk cache was last consulted; an unusable file waits, then may retry, like the listing. */
20
+ let cacheReadAt = 0;
21
+ let attemptedAt = 0, pending;
22
+
23
+ const perMillion = value => {
24
+ const number = Number(value);
25
+ return value != null && value !== '' && Number.isFinite(number) && number >= 0 ? number * 1e6 : undefined;
26
+ };
27
+ const positive = value => Number.isSafeInteger(value) && value > 0 ? value : undefined;
28
+ const ratesOf = pricing => {
29
+ const input = perMillion(pricing?.prompt), output = perMillion(pricing?.completion);
30
+ if (input === undefined || output === undefined) return undefined;
31
+ return { input, output, cacheRead: perMillion(pricing.input_cache_read), cacheWrite: perMillion(pricing.input_cache_write) };
32
+ };
33
+
34
+ function reasoningOf(reasoning) {
35
+ if (reasoning === null || typeof reasoning !== 'object') return undefined;
36
+ const offered = Array.isArray(reasoning.supported_efforts) ? EFFORTS.filter(level => reasoning.supported_efforts.includes(level)) : undefined;
37
+ return {
38
+ mandatory: reasoning.mandatory === true,
39
+ ...(offered?.length ? { efforts: offered } : {}),
40
+ ...(EFFORTS.includes(reasoning.default_effort) ? { defaultEffort: reasoning.default_effort } : {}),
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Models from an OpenRouter `/models` body.
46
+ * @returns `{ [id]: { input, output, cacheRead?, cacheWrite?, tiers?, name?, contextWindow?, maxOutput?,
47
+ * inputModalities?, tools?, textOutput?, reasoning? } }` with prices in USD per million tokens;
48
+ * a tier applies once a request's prompt reaches its `minPromptTokens`.
49
+ */
50
+ export function parseOpenRouterModels(body) {
51
+ const models = {};
52
+ for (const model of Array.isArray(body?.data) ? body.data : []) {
53
+ const base = typeof model?.id === 'string' ? ratesOf(model.pricing) : undefined;
54
+ if (!base) continue;
55
+ const tiers = (Array.isArray(model.pricing.overrides) ? model.pricing.overrides : [])
56
+ .map(tier => ({ minPromptTokens: Number(tier?.min_prompt_tokens), ...ratesOf({ ...model.pricing, ...tier }) }))
57
+ .filter(tier => Number.isFinite(tier.minPromptTokens) && tier.input !== undefined)
58
+ .sort((left, right) => left.minPromptTokens - right.minPromptTokens);
59
+ // The routed endpoint can serve less than the model's nominal context.
60
+ const windows = [model.context_length, model.top_provider?.context_length].map(positive).filter(Boolean);
61
+ const input = model.architecture?.input_modalities, output = model.architecture?.output_modalities;
62
+ const reasoning = reasoningOf(model.reasoning);
63
+ models[model.id] = {
64
+ ...base, ...(tiers.length ? { tiers } : {}),
65
+ ...(typeof model.name === 'string' && model.name.length > 0 ? { name: model.name } : {}),
66
+ ...(windows.length ? { contextWindow: Math.min(...windows) } : {}),
67
+ ...(positive(model.top_provider?.max_completion_tokens) ? { maxOutput: model.top_provider.max_completion_tokens } : {}),
68
+ ...(Array.isArray(input) ? { inputModalities: ['text', 'image'].filter(modality => input.includes(modality)) } : {}),
69
+ ...(Array.isArray(model.supported_parameters) ? { tools: model.supported_parameters.includes('tools') } : {}),
70
+ ...(Array.isArray(output) ? { textOutput: output.includes('text') } : {}),
71
+ ...(reasoning ? { reasoning } : {}),
72
+ };
73
+ }
74
+ return models;
75
+ }
76
+
77
+ /** One model's entry, or undefined when the table does not list it. */
78
+ export function openRouterModel(id) {
79
+ return Object.hasOwn(table.models, id) ? table.models[id] : undefined;
80
+ }
81
+
82
+ /** Every listed model as `[id, entry]` pairs, in listing order. */
83
+ export function listOpenRouterModels() {
84
+ return Object.entries(table.models);
85
+ }
86
+
87
+ /** Prices for one model at a request's prompt size, or undefined when the table lists none. */
88
+ export function openRouterRates(model, promptTokens = 0) {
89
+ const entry = openRouterModel(model);
90
+ if (!entry) return undefined;
91
+ return entry.tiers?.findLast(tier => promptTokens >= tier.minPromptTokens) ?? entry;
92
+ }
93
+
94
+ /** Version stamp for rows priced from the live table: the day it was fetched. */
95
+ export function openRouterPriceVersion() {
96
+ return table.fetchedAt > 0 ? `openrouter-models-${new Date(table.fetchedAt).toISOString().slice(0, 10)}` : undefined;
97
+ }
98
+
99
+ /** Replace the table (and forget the last attempt); for tests and cache loads. */
100
+ export function setOpenRouterModels(models, fetchedAt = Date.now()) {
101
+ table = { fetchedAt, models };
102
+ cacheReadAt = 0;
103
+ attemptedAt = 0;
104
+ }
105
+
106
+ /**
107
+ * Load the cached table, then refetch it once it is a day old. A failure keeps the
108
+ * last table and waits ten minutes before trying again; never throws.
109
+ */
110
+ export async function refreshOpenRouterModels({ home, fetch: fetchImpl = globalThis.fetch, now = Date.now() } = {}) {
111
+ const path = home ? join(home, FILE) : undefined;
112
+ // A version bump or a corrupt body leaves the table empty: parse that file once per throttle window, not per request.
113
+ if (table.fetchedAt === 0 && path && now - cacheReadAt >= RETRY_MS) {
114
+ cacheReadAt = now;
115
+ try {
116
+ const cached = JSON.parse(readFileSync(path, 'utf8'));
117
+ if (cached?.version === VERSION && Number.isFinite(cached.fetchedAt) && cached.models && typeof cached.models === 'object') table = { fetchedAt: cached.fetchedAt, models: cached.models };
118
+ } catch { /* no usable cache */ }
119
+ }
120
+ if (now - table.fetchedAt < MAX_AGE_MS || now - attemptedAt < RETRY_MS || typeof fetchImpl !== 'function') return table;
121
+ if (pending) return pending;
122
+ attemptedAt = now;
123
+ pending = (async () => {
124
+ try {
125
+ const response = await fetchImpl(OPENROUTER_MODELS_URL, { headers: { Accept: 'application/json' } });
126
+ if (!response.ok) return table;
127
+ const models = parseOpenRouterModels(await response.json());
128
+ if (Object.keys(models).length === 0) return table;
129
+ table = { fetchedAt: now, models };
130
+ if (path) {
131
+ mkdirSync(home, { recursive: true });
132
+ writeFileSync(`${path}.tmp`, JSON.stringify({ version: VERSION, ...table }));
133
+ renameSync(`${path}.tmp`, path);
134
+ }
135
+ return table;
136
+ } catch {
137
+ return table;
138
+ } finally {
139
+ pending = undefined;
140
+ }
141
+ })();
142
+ return pending;
143
+ }
144
+
145
+ /**
146
+ * The table before a model lookup or a priced call: an empty table waits up to
147
+ * `timeoutMs` for the listing, a stale one refreshes in the background. Waits at
148
+ * most once per retry window, so an unreachable listing never stalls every call.
149
+ */
150
+ export async function ensureOpenRouterModels({ timeoutMs = FORCED_LOAD_TIMEOUT_MS, ...options } = {}) {
151
+ const refresh = refreshOpenRouterModels(options);
152
+ if (Object.keys(table.models).length > 0) return table;
153
+ let timer;
154
+ await Promise.race([refresh, new Promise(resolve => { timer = setTimeout(resolve, timeoutMs); timer.unref?.(); })]);
155
+ clearTimeout(timer);
156
+ return table;
157
+ }
@@ -0,0 +1,113 @@
1
+ import { WebError } from '@deepseek-ai/dsh-web';
2
+ import { canonicalProvider, isOpenRouterRoute } from '../providers/catalog.mjs';
3
+
4
+ // Web search through OpenRouter's web plugin, and the provider `ctx.web` is pinned to:
5
+ // it searches with the session's own route, so an OpenRouter session never needs a
6
+ // DeepSeek key and a DeepSeek session never bills OpenRouter.
7
+ export const OPENROUTER_SEARCH_ID = 'openrouter';
8
+ export const ROUTED_SEARCH_ID = 'dscode-web';
9
+ const DEFAULT_MAX_RESULTS = 8;
10
+
11
+ /** Sources from the `url_citation` annotations of an OpenRouter message, de-duplicated by URL. */
12
+ export function citationSources(message) {
13
+ const seen = new Set(), sources = [];
14
+ for (const annotation of Array.isArray(message?.annotations) ? message.annotations : []) {
15
+ const citation = annotation?.type === 'url_citation' ? annotation.url_citation : undefined;
16
+ if (typeof citation?.url !== 'string' || citation.url.length === 0 || seen.has(citation.url)) continue;
17
+ seen.add(citation.url);
18
+ sources.push({
19
+ url: citation.url,
20
+ ...(typeof citation.title === 'string' && citation.title.length > 0 ? { title: citation.title } : {}),
21
+ ...(typeof citation.content === 'string' && citation.content.length > 0 ? { snippet: citation.content } : {}),
22
+ });
23
+ }
24
+ return sources;
25
+ }
26
+
27
+ const aborted = (signal, error) => new WebError('OpenRouter search was cancelled', 'WEB_ABORTED', { cause: error ?? signal?.reason });
28
+
29
+ /** Exa search through OpenRouter's `web` plugin on a small model whose answer is discarded. */
30
+ export class OpenRouterSearchProvider {
31
+ id = OPENROUTER_SEARCH_ID;
32
+
33
+ /** @param resolveOptions - `{ baseURL, model, resolveApiKey(), fetch? }` for the next search. */
34
+ constructor(resolveOptions) {
35
+ this.resolveOptions = resolveOptions;
36
+ }
37
+
38
+ available() {
39
+ return URL.canParse(this.resolveOptions().baseURL);
40
+ }
41
+
42
+ async search(request, signal) {
43
+ const options = this.resolveOptions();
44
+ const apiKey = await options.resolveApiKey();
45
+ if (signal?.aborted) throw aborted(signal);
46
+ if (!apiKey) throw new WebError('OpenRouter search has no API key: run /login openrouter or set OPENROUTER_API_KEY.', 'WEB_PROVIDER_CREDENTIAL_MISSING');
47
+ const body = {
48
+ model: options.model,
49
+ stream: false,
50
+ messages: [{ role: 'user', content: `Search the web for: ${request.query}` }],
51
+ plugins: [{ id: 'web', engine: 'exa', max_results: request.maxResults ?? DEFAULT_MAX_RESULTS }],
52
+ reasoning: { effort: 'none' },
53
+ max_tokens: 256,
54
+ };
55
+ let response, text;
56
+ try {
57
+ response = await (options.fetch ?? globalThis.fetch)(`${options.baseURL}/chat/completions`, {
58
+ method: 'POST', redirect: 'error', signal,
59
+ headers: { authorization: `Bearer ${apiKey}`, 'content-type': 'application/json', accept: 'application/json', 'HTTP-Referer': 'https://github.com/qiz029/dscode', 'X-OpenRouter-Title': 'DSCODE' },
60
+ body: JSON.stringify(body),
61
+ });
62
+ text = await response.text();
63
+ } catch (error) {
64
+ if (signal?.aborted) throw aborted(signal, error);
65
+ throw new WebError(`OpenRouter search request failed: ${error instanceof Error ? error.message : String(error)}`, 'WEB_PROVIDER_ERROR', { cause: error });
66
+ }
67
+ let json;
68
+ try { json = JSON.parse(text); } catch { /* reported below */ }
69
+ if (!response.ok || json?.error || !Array.isArray(json?.choices)) {
70
+ const detail = typeof json?.error?.message === 'string' ? json.error.message : text.slice(0, 200);
71
+ throw new WebError(`OpenRouter search failed (HTTP ${response.status}): ${detail}`, 'WEB_PROVIDER_ERROR');
72
+ }
73
+ return { sources: citationSources(json.choices[0]?.message), truncated: false };
74
+ }
75
+ }
76
+
77
+ /**
78
+ * The pinned search provider: OpenRouter for an OpenRouter session, DeepSeek for a
79
+ * DeepSeek one, Exa for an OpenCode Go one (Go has no search of its own), and otherwise
80
+ * whichever provider has a key (DeepSeek first).
81
+ */
82
+ export class RoutedSearchProvider {
83
+ id = ROUTED_SEARCH_ID;
84
+
85
+ /**
86
+ * @param routes - `openrouter` provider, `deepseek()` provider lookup, optional `exa` provider,
87
+ * `currentProvider()` of the calling session, and `hasKey(ref)` for credential presence.
88
+ */
89
+ constructor(routes) {
90
+ this.routes = routes;
91
+ }
92
+
93
+ available() {
94
+ return this.routes.openrouter.available() || this.routes.deepseek()?.available() === true;
95
+ }
96
+
97
+ async pick() {
98
+ const { openrouter, deepseek: lookup, exa, currentProvider, hasKey } = this.routes;
99
+ const deepseek = lookup();
100
+ const route = canonicalProvider(currentProvider());
101
+ if (route?.startsWith('custom-')) throw new WebError('Web search is not configured for Custom. Configure a separate search provider explicitly.', 'WEB_PROVIDER_CREDENTIAL_MISSING');
102
+ if (isOpenRouterRoute(route)) return openrouter;
103
+ if (route === 'opencode-go' && exa) return exa;
104
+ if (route === 'deepseek-official' && deepseek) return deepseek;
105
+ if (deepseek && await hasKey('DEEPSEEK_API_KEY')) return deepseek;
106
+ if (await hasKey('OPENROUTER_API_KEY')) return openrouter;
107
+ return deepseek ?? openrouter;
108
+ }
109
+
110
+ async search(request, signal) {
111
+ return (await this.pick()).search(request, signal);
112
+ }
113
+ }