@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,1041 @@
1
+ // Generated from the shared Desktop component sources.
2
+ (() => {
3
+ const components = [];
4
+ // Shared Web/Desktop client. Draft keys live only in this mounted form.
5
+ components.push({
6
+ id: '@toddzheng024/dscode-desktop',
7
+ factory: require => {
8
+ const { createElement: h, useState, useEffect, useRef } = require('react');
9
+ const control = { padding: '8px 10px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', font: 'inherit', boxSizing: 'border-box' };
10
+ const row = { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' };
11
+ const resetModel = model => {
12
+ const next = { ...model, thinking: 'default', inputModalities: ['text'] };
13
+ for (const [field, source] of [['contextWindow', 'contextSource'], ['maxTokens', 'outputSource']]) {
14
+ if (next[source] === 'server') { delete next[field]; delete next[source]; }
15
+ }
16
+ return next;
17
+ };
18
+ function Settings({ execute }) {
19
+ const [snapshot, setSnapshot] = useState(null), [draft, setDraft] = useState(null), [key, setKey] = useState('');
20
+ const [busy, setBusy] = useState(false), [error, setError] = useState(''), [notice, setNotice] = useState(''), [report, setReport] = useState(null), [removing, setRemoving] = useState(false);
21
+ const pending = useRef(false), alive = useRef(true), request = useRef(null);
22
+ const run = async (task, cancellable = false) => {
23
+ if (pending.current) return;
24
+ const controller = cancellable ? new AbortController() : null;
25
+ request.current = controller;
26
+ pending.current = true; setBusy(true); setError(''); setNotice('');
27
+ let cancellationFailed = false;
28
+ try { await task(controller?.signal); }
29
+ catch (error) {
30
+ cancellationFailed = error.cancellationFailed === true;
31
+ if (alive.current && (!controller?.signal.aborted || cancellationFailed)) setError(error.message);
32
+ }
33
+ finally {
34
+ request.current = null; pending.current = false;
35
+ if (alive.current) {
36
+ setBusy(false);
37
+ if (controller?.signal.aborted) setNotice(cancellationFailed ? 'Cancellation could not be confirmed.' : 'Request cancelled.');
38
+ }
39
+ }
40
+ };
41
+ const select = profile => { setDraft(profile ? { ...profile, models: profile.models.map(model => ({ ...model })) } : null); setKey(''); setReport(null); setRemoving(false); setNotice(''); };
42
+ const accept = value => { if (!alive.current) return; setSnapshot(value); select(value.providers.find(p => p.id === draft?.id) ?? value.providers[0]); };
43
+ useEffect(() => {
44
+ alive.current = true;
45
+ void run(async () => { accept(await execute('list')); });
46
+ return () => { alive.current = false; request.current?.abort(); };
47
+ }, []);
48
+ const change = values => { setDraft({ ...draft, ...values }); setReport(null); setRemoving(false); setNotice('Unsaved changes'); };
49
+ const modelChange = (index, values) => change({ models: draft.models.map((model, i) => i === index ? { ...model, ...values } : model) });
50
+ const button = (text, action, disabled = false, cancellable = false) => h('button', { type: 'button', style: { ...control, cursor: 'pointer' }, disabled: busy || disabled, onClick: () => run(action, cancellable) }, text);
51
+ const input = (label, value, onChange, options = {}) => h('label', { style: { display: 'grid', gap: 5, minWidth: 180, flex: 1 } }, label,
52
+ h('input', { 'aria-label': label, style: { ...control, width: '100%' }, value: value ?? '', onChange: event => onChange(event.target.value), ...options }));
53
+ const choice = (label, value, choices, onChange) => h('label', { style: { display: 'grid', gap: 5, flex: 1 } }, label,
54
+ h('select', { 'aria-label': label, value, onChange: event => onChange(event.target.value), style: control }, ...choices.map(([id, text]) => h('option', { key: id, value: id }, text))));
55
+ return h('section', { 'aria-label': 'DSCODE model settings', style: { padding: 20, display: 'grid', gap: 16, maxWidth: 850, color: 'inherit' } },
56
+ h('h2', { style: { margin: 0 } }, 'DSCODE models'),
57
+ h('p', { style: { margin: 0 } }, 'Connect a private or self-hosted model service. Saved models appear in the model picker.'),
58
+ error && h('div', { role: 'alert', style: { color: '#d85b5b', whiteSpace: 'pre-wrap' } }, error),
59
+ notice && h('div', { role: 'status' }, notice),
60
+ busy && request.current && h('button', { type: 'button', style: control, disabled: request.current.signal.aborted,
61
+ onClick: () => { request.current?.abort(); setNotice('Cancelling request…'); },
62
+ }, 'Cancel request'),
63
+ h('div', { style: row },
64
+ button('Reload (discard draft)', async () => accept(await execute('list'))),
65
+ button('Add provider', async () => { const value = await execute('new'); if (alive.current) select(value); }, !snapshot)),
66
+ h('fieldset', { disabled: busy || !snapshot, style: { border: 0, padding: 0, margin: 0, display: 'grid', gap: 16 } },
67
+ choice('Saved provider', snapshot?.providers.some(p => p.id === draft?.id) ? draft.id : '', [['', 'Choose a provider'], ...(snapshot?.providers ?? []).map(p => [p.id, p.name])], id => select(snapshot.providers.find(p => p.id === id))),
68
+ draft && h('div', { style: { display: 'grid', gap: 16 } },
69
+ h('div', { style: row }, input('Provider name', draft.name, name => change({ name })),
70
+ input('API base URL', draft.baseURL, baseURL => change({ baseURL, backend: 'generic', models: draft.models.map(resetModel) }), { placeholder: 'https://example.com/v1', type: 'url' })),
71
+ h('div', { style: row }, choice('API format', draft.api, [['chat-completions', 'Chat Completions'], ['responses', 'Responses'], ['anthropic', 'Anthropic Messages']], api => change({ api, auth: api === 'anthropic' ? 'x-api-key' : 'bearer', models: draft.models.map(resetModel) })),
72
+ choice('Authentication', draft.auth, [['bearer', 'Bearer token'], ['x-api-key', 'API key header'], ['none', 'None']], auth => change({ auth }))),
73
+ draft.auth !== 'none' && input('API key', key, value => { setKey(value); setReport(null); setRemoving(false); setNotice('Unsaved changes'); }, { type: 'password', autoComplete: 'off', spellCheck: false, placeholder: 'Leave blank to keep the saved key' }),
74
+ h('small', null, draft.credentialStatus ?? 'Not saved'),
75
+ h('div', { style: row }, button('Discover models', async signal => {
76
+ const found = await execute('discover', { profile: draft, key }, signal);
77
+ if (!alive.current || signal.aborted) return;
78
+ const models = [...draft.models];
79
+ for (const model of found.models) {
80
+ const index = models.findIndex(previous => previous.id === model.id), previous = models[index];
81
+ if (index < 0) models.push(model);
82
+ else models[index] = {
83
+ ...previous, ...model, thinking: previous.thinking,
84
+ ...(previous.contextSource === 'user' ? { contextWindow: previous.contextWindow, contextSource: 'user' } : {}),
85
+ ...(previous.outputSource === 'user' ? { maxTokens: previous.maxTokens, outputSource: 'user' } : {}),
86
+ };
87
+ }
88
+ change({ backend: found.backend, models });
89
+ setNotice(`Found ${found.models.length} models. Confirm the context size and image support before saving.`);
90
+ }, false, true), button('Add model', async () => change({ models: [...draft.models, { id: '', thinking: 'default', inputModalities: ['text'] }] }))),
91
+ ...draft.models.map((model, index) => h('fieldset', { key: index, style: { border: '1px solid #8886', borderRadius: 8, padding: 14, display: 'grid', gap: 12 } },
92
+ h('legend', null, `Model ${index + 1}`),
93
+ h('div', { style: row }, input(`Model ${index + 1} ID`, model.id, id => change({ models: draft.models.map((entry, i) => i === index ? { ...resetModel(entry), id } : entry) })),
94
+ input(`Model ${index + 1} context`, model.contextWindow, value => modelChange(index, { contextWindow: value ? Number(value) : undefined, contextSource: 'user' }), { type: 'number', min: 1, step: 1 }),
95
+ input(`Model ${index + 1} output limit`, model.maxTokens, value => modelChange(index, { maxTokens: value ? Number(value) : undefined, outputSource: 'user' }), { type: 'number', min: 1, step: 1 })),
96
+ h('div', { style: row }, choice(`Model ${index + 1} thinking`, model.thinking ?? 'default', [['default', 'Provider default'], ['off', 'Off'], ['on', 'On']], thinking => modelChange(index, { thinking })),
97
+ h('label', null, h('input', { type: 'checkbox', 'aria-label': `Model ${index + 1} accepts images`, checked: model.inputModalities?.includes('image') ?? false,
98
+ onChange: event => modelChange(index, { inputModalities: event.target.checked ? ['text', 'image'] : ['text'] }) }), ' Accepts images')),
99
+ h('div', { style: row }, button(`Test model ${index + 1}`, async signal => {
100
+ setReport(null);
101
+ const stages = await execute('test', { profile: draft, model: model.id, key }, signal);
102
+ if (alive.current && !signal.aborted) setReport({ model: model.id, stages });
103
+ }, !model.id || !model.contextWindow, true),
104
+ button(`Remove model ${index + 1}`, async () => change({ models: draft.models.filter((_, i) => i !== index) }))))),
105
+ h('p', { style: { margin: 0, fontSize: 13 } }, 'Tests send text and tool calls to this service and may incur usage. Image support is a manual setting; the test does not verify vision.'),
106
+ report && h('div', null,
107
+ h('h3', { style: { margin: 0 } }, `Test results: ${report.model}`),
108
+ h('ul', { 'aria-label': 'Model test results' }, ...report.stages.map((stage, index) => h('li', { key: index }, `${stage.name}: ${stage.status}${stage.message ? ` — ${stage.message}` : ''}`)))),
109
+ h('div', { style: row }, button('Save provider', async () => {
110
+ const saved = await execute('save', { profile: draft, key, revision: snapshot.revision });
111
+ if (alive.current) { accept(saved); setNotice('Provider saved. Select its model in a session.'); }
112
+ }, draft.models.length === 0 || draft.models.some(model => !model.contextWindow)),
113
+ snapshot?.providers.some(p => p.id === draft.id) && button(removing ? 'Confirm remove provider and key' : 'Remove provider', async () => {
114
+ if (!removing) { setRemoving(true); return; }
115
+ const saved = await execute('remove', { id: draft.id, revision: snapshot.revision });
116
+ if (alive.current) { accept(saved); setNotice('Provider removed.'); }
117
+ })))));
118
+ }
119
+ return {
120
+ inject: ['slots', 'connection'],
121
+ apply(ctx) {
122
+ ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dscode-models', order: 11, label: () => 'DSCODE models',
123
+ inject: () => ({ execute: async (action, args = {}, signal) => {
124
+ signal?.throwIfAborted();
125
+ const requestId = signal ? crypto.randomUUID() : undefined;
126
+ let cancellation;
127
+ const cancel = () => {
128
+ cancellation ??= ctx.connection.rpc.call('/api', 'dscode-custom', { action: 'cancel', requestId }, AbortSignal.timeout(10000))
129
+ .then(response => response.ok ? null : response.error.message, error => error.message);
130
+ };
131
+ signal?.addEventListener('abort', cancel, { once: true });
132
+ let response, requestError;
133
+ try {
134
+ response = await ctx.connection.rpc.call('/api', 'dscode-custom', { action, ...args, ...(requestId ? { requestId } : {}) }, signal);
135
+ } catch (error) { requestError = error; }
136
+ finally { signal?.removeEventListener('abort', cancel); }
137
+ if (cancellation) {
138
+ const failure = await cancellation;
139
+ if (failure) {
140
+ const error = Error('Could not confirm server cancellation. The request may still be running: ' + failure);
141
+ error.cancellationFailed = true;
142
+ throw error;
143
+ }
144
+ }
145
+ if (requestError) throw requestError;
146
+ if (!response.ok) throw Error(response.error.message);
147
+ return response.value;
148
+ } }),
149
+ }, Settings));
150
+ },
151
+ };
152
+ },
153
+ });
154
+
155
+ // Official Harness client-module format; React is supplied by the shared UI.
156
+ components.push({
157
+ id: '@toddzheng024/dscode-browser-desktop',
158
+ factory: require => {
159
+ const { createElement: h, useState, useEffect, useRef } = require('react');
160
+ const id = '@toddzheng024/dscode-browser-desktop';
161
+ const button = { padding: '7px 11px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', cursor: 'pointer' };
162
+ function previewButton(props, label) {
163
+ return h('button', { ...props, style: { ...button, ...props.style, ...(props.disabled ? { opacity: 0.5, cursor: 'not-allowed' } : {}) } }, label);
164
+ }
165
+ const sitePermissions = (permissions, origin) => origin && JSON.stringify([
166
+ permissions?.developerMode, permissions?.sites?.[origin], permissions?.sessionSites?.includes(origin),
167
+ ]);
168
+ function SessionPreview({ execute, draft }) {
169
+ const { pageId, setPageId, text, setText, draftRevision } = draft;
170
+ const [pages, setPages] = useState([]);
171
+ const [permissions, setPermissions] = useState(null);
172
+ const [handoff, setHandoff] = useState(null);
173
+ const [frame, setFrame] = useState(null), [point, setPoint] = useState(null);
174
+ const [expanded, setExpanded] = useState(false);
175
+ const [expired, setExpired] = useState(false);
176
+ const mounted = useRef(true);
177
+ useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
178
+ const [error, setError] = useState(''), [notice, setNotice] = useState(''), [busy, setBusy] = useState(false), [live, setLive] = useState(false);
179
+ const pending = useRef(false), controlPending = useRef(false), controlEpoch = useRef(0);
180
+ const activityEpoch = useRef(0);
181
+ const browserAvailable = useRef(!!pageId);
182
+ const [controlBusy, setControlBusy] = useState(false);
183
+ const disconnected = () => {
184
+ browserAvailable.current = false;
185
+ setHandoff(null); setPages([]); setPageId(''); setPermissions(null);
186
+ setFrame(null); setPoint(null); setLive(false);
187
+ setNotice('Browser disconnected. Your draft is kept. Start or reconnect the browser and capture a new preview.');
188
+ };
189
+ const run = async action => {
190
+ if (pending.current || controlPending.current) return;
191
+ activityEpoch.current++;
192
+ const epoch = controlEpoch.current;
193
+ const current = () => epoch === controlEpoch.current && !controlPending.current;
194
+ const operation = {};
195
+ pending.current = operation; setBusy(true); setError('');
196
+ try { await action(current); } catch (error) {
197
+ if (!current()) return;
198
+ setError(error.message); setLive(false); setFrame(null); setPoint(null); setPermissions(null);
199
+ }
200
+ finally { if (pending.current === operation) { pending.current = false; setBusy(false); } }
201
+ };
202
+ const list = async (current, { preserveDraft = false } = {}) => {
203
+ const revision = draftRevision.current;
204
+ const result = await execute('tabs');
205
+ if (!current() || !mounted.current) return;
206
+ if (result.connected === false) { disconnected(); return; }
207
+ browserAvailable.current = true;
208
+ setPages(result.pages);
209
+ setPermissions(result.permissions ?? null);
210
+ setHandoff(result.handoff ?? null);
211
+ if (result.handoff) { setFrame(null); setPoint(null); setLive(false); }
212
+ const selected = result.pages.find(page => String(page.id) === pageId) ?? result.pages.find(page => page.selected) ?? result.pages[0];
213
+ if (String(selected?.id ?? '') !== pageId) {
214
+ setPageId(selected ? String(selected.id) : ''); setFrame(null); setPoint(null); setLive(false);
215
+ // A first selection can follow browser startup or an empty tab list.
216
+ // Keep text written before any page was assigned to the draft.
217
+ if (pageId && !preserveDraft && !result.handoff && draftRevision.current === revision) setText('');
218
+ } else if (frame && (selected?.url !== frame.url || selected?.documentId !== frame.documentId)) {
219
+ setFrame(null); setPoint(null); setLive(false);
220
+ setNotice('The page changed or could not be verified. Refresh the preview; your draft is kept.');
221
+ }
222
+ };
223
+ const refresh = async current => {
224
+ if (!pageId || handoff) return;
225
+ setPoint(null);
226
+ const result = await execute('capture', { pageId: Number(pageId) });
227
+ if (current()) {
228
+ browserAvailable.current = true;
229
+ setPages(previous => previous.map(page => page.id === result.pageId ? { ...page, url: result.url, documentId: result.documentId } : page));
230
+ setPermissions(result.permissions ?? null);
231
+ setFrame(result); setNotice('');
232
+ }
233
+ };
234
+ let origin;
235
+ try {
236
+ const url = new URL(pages.find(page => String(page.id) === pageId)?.url);
237
+ if (['http:', 'https:'].includes(url.protocol) && !url.username && !url.password) origin = url.origin;
238
+ } catch { /* Blank and unavailable tabs have no site grant. */ }
239
+ useEffect(() => { void run(list); }, []);
240
+ useEffect(() => {
241
+ let active = true, reading = false;
242
+ const timer = setInterval(async () => {
243
+ if (reading || pending.current || controlPending.current) return;
244
+ const epoch = activityEpoch.current;
245
+ reading = true;
246
+ try {
247
+ // Read Host state and grants: no Chrome request or image capture.
248
+ const result = await execute('handoff');
249
+ if (!active || !mounted.current || epoch !== activityEpoch.current) return;
250
+ if (result.connected === false) {
251
+ if (!browserAvailable.current && !handoff) return;
252
+ disconnected();
253
+ return;
254
+ }
255
+ if (result.connected === true) browserAvailable.current = true;
256
+ if (result.permissions && JSON.stringify(result.permissions) !== JSON.stringify(permissions)) {
257
+ if (sitePermissions(result.permissions, origin) !== sitePermissions(permissions, origin)) {
258
+ setFrame(null); setPoint(null); setLive(false);
259
+ setNotice('Site permissions changed. Your draft is kept. Capture a new preview after access is granted.');
260
+ }
261
+ setPermissions(result.permissions);
262
+ }
263
+ const next = result.handoff ?? null;
264
+ if (JSON.stringify(next) === JSON.stringify(handoff)) return;
265
+ setHandoff(next); setFrame(null); setPoint(null); setLive(false);
266
+ } catch { /* The next visible tick retries; browser actions still enforce handoff. */ }
267
+ finally { reading = false; }
268
+ }, 2500);
269
+ return () => { active = false; clearInterval(timer); };
270
+ }, [execute, handoff, permissions, origin]);
271
+ const expire = () => { setExpired(true); setPoint(null); setLive(false); };
272
+ useEffect(() => {
273
+ setExpired(false);
274
+ if (!frame) return;
275
+ const remaining = frame.capturedAt + 60000 - Date.now();
276
+ if (remaining <= 0) { expire(); return; }
277
+ let active = true;
278
+ const timer = setTimeout(() => { if (active) expire(); }, remaining);
279
+ return () => { active = false; clearTimeout(timer); };
280
+ }, [frame]);
281
+ useEffect(() => {
282
+ if (!live || point || !pageId || handoff) return;
283
+ const timer = setInterval(() => { void run(refresh); }, 2500);
284
+ return () => clearInterval(timer);
285
+ }, [live, point, pageId, handoff, execute]);
286
+ const canSelect = () => {
287
+ if (!frame || handoff || pending.current || controlPending.current || expired) return false;
288
+ if (Date.now() - frame.capturedAt >= 60000) { expire(); return false; }
289
+ return true;
290
+ };
291
+ const selectPoint = event => {
292
+ if (!canSelect()) return;
293
+ setLive(false);
294
+ const rect = event.currentTarget.getBoundingClientRect();
295
+ setPoint({ x: Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width)), y: Math.min(1, Math.max(0, (event.clientY - rect.top) / rect.height)) });
296
+ };
297
+ const selectPointKey = event => {
298
+ if (event.altKey || event.ctrlKey || event.metaKey || !['Enter', ' ', 'Escape', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key)) return;
299
+ event.preventDefault();
300
+ if (!canSelect()) return;
301
+ if (event.key === 'Escape') { setPoint(null); return; }
302
+ setLive(false);
303
+ setPoint(previous => {
304
+ const xMax = frame.width - 1, yMax = frame.height - 1;
305
+ const base = ['Enter', ' '].includes(event.key) ? null : previous;
306
+ let x = Math.round((base?.x ?? 0.5) * xMax), y = Math.round((base?.y ?? 0.5) * yMax);
307
+ const step = event.shiftKey ? 10 : 1;
308
+ if (event.key === 'ArrowLeft') x -= step;
309
+ if (event.key === 'ArrowRight') x += step;
310
+ if (event.key === 'ArrowUp') y -= step;
311
+ if (event.key === 'ArrowDown') y += step;
312
+ return { x: xMax ? Math.min(xMax, Math.max(0, x)) / xMax : 0, y: yMax ? Math.min(yMax, Math.max(0, y)) / yMax : 0 };
313
+ });
314
+ };
315
+ const rule = permissions?.sites?.[origin];
316
+ const access = rule?.access === 'block' ? 'Blocked' : rule?.access === 'allow' ? 'Always allowed' :
317
+ permissions?.sessionSites?.includes(origin) ? 'Allowed until browser stops' : permissions ? 'Permission required' : 'Refresh tabs to load permissions';
318
+ const changeControl = async action => {
319
+ if (controlPending.current) return;
320
+ // Stop and revocation must not wait for a slow ordinary request. The epoch
321
+ // prevents earlier responses from restoring pixels or stale grants.
322
+ controlPending.current = true; controlEpoch.current++;
323
+ activityEpoch.current++;
324
+ // Superseded requests may remain in transport indefinitely. Release their
325
+ // UI lock; only the currently owned operation may clear a later lock.
326
+ pending.current = false; setBusy(false);
327
+ setControlBusy(true); setError(''); setNotice('');
328
+ setFrame(null); setPoint(null); setLive(false);
329
+ try {
330
+ await action();
331
+ } catch (error) {
332
+ setError(error.message); setPermissions(null);
333
+ } finally { controlPending.current = false; setControlBusy(false); }
334
+ };
335
+ const changePermission = (change, value = origin) => changeControl(async () => {
336
+ const result = await execute('permission', { change, value });
337
+ setPermissions(result.permissions);
338
+ setNotice(change === 'developer-mode' ? `Developer mode ${value}. Each site also needs its own grant.` : `Permissions updated for ${value}.`);
339
+ });
340
+ const stop = () => changeControl(async () => {
341
+ browserAvailable.current = false;
342
+ setPages([]); setPageId(''); setPermissions(null); setHandoff(null);
343
+ await execute('stop');
344
+ setNotice('Browser stopped. Your draft is kept. Start the browser and capture a new preview to continue.');
345
+ });
346
+ const resume = () => run(async current => {
347
+ setNotice(''); setFrame(null); setPoint(null); setLive(false);
348
+ await execute('resume');
349
+ if (!current() || !mounted.current) return;
350
+ setHandoff(null);
351
+ setNotice('Browser control resumed. Your draft is kept. Capture a new preview before sending.');
352
+ await list(current, { preserveDraft: true });
353
+ });
354
+ return h('section', { 'aria-label': 'DSCODE browser preview', style: { padding: 12, boxSizing: 'border-box', overflow: 'auto', height: '100%', display: 'flex', flexDirection: 'column', gap: 8, color: 'inherit' } },
355
+ h('strong', null, 'Browser preview'),
356
+ handoff && h('div', { 'aria-label': 'Browser handoff', role: 'status', style: { border: '1px solid #8886', borderRadius: 8, padding: 10, overflowWrap: 'anywhere' } },
357
+ h('strong', null, 'Waiting for you'),
358
+ h('p', null, handoff.reason),
359
+ handoff.invalidated && h('p', null, 'Chrome restarted. The original tab is unavailable; choose a live tab after resuming.'),
360
+ handoff.focusError && h('p', null, handoff.focusError),
361
+ h('p', null, 'Complete the manual step in Chrome, then resume browser control.'),
362
+ previewButton({ style: button, disabled: busy || controlBusy, onClick: resume }, 'Resume browser control')),
363
+ h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
364
+ previewButton({ style: button, disabled: busy || controlBusy || !!handoff, onClick: () => run(async current => { setNotice(''); await execute('start'); if (current()) await list(current); }) }, 'Start browser'),
365
+ previewButton({ style: button, disabled: controlBusy, onClick: stop }, 'Stop browser'),
366
+ previewButton({ style: button, disabled: busy || controlBusy, onClick: () => run(list) }, 'Refresh tabs'),
367
+ previewButton({ style: button, disabled: busy || controlBusy || !pageId || !!handoff, onClick: () => run(refresh) }, 'Refresh preview'),
368
+ frame && previewButton({ style: button, onClick: () => setExpanded(!expanded) }, expanded ? 'Fit image' : 'Expand image')),
369
+ h('select', { 'aria-label': 'Browser tab', value: pageId, disabled: busy || controlBusy, onChange: e => { setPageId(e.target.value); setFrame(null); setPoint(null); setText(''); setLive(false); }, style: { ...button, width: '100%' } }, h('option', { value: '' }, 'Choose a tab'), ...pages.map(page => h('option', { key: page.id, value: String(page.id) }, `${page.id} · ${page.url}`))),
370
+ origin && h('details', { 'aria-label': 'Browser site permissions', style: { border: '1px solid #8886', borderRadius: 8, padding: '8px 10px', margin: 0, minWidth: 0 } },
371
+ h('summary', { 'aria-label': `Site permissions · ${access}`, style: { cursor: 'pointer' } }, 'Site permissions · ', h('span', { 'aria-label': 'Site access status' }, access)),
372
+ h('div', { style: { fontSize: 12, overflowWrap: 'anywhere', margin: '8px 0' } }, origin),
373
+ h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 6 } },
374
+ ...[['Allow this session', 'once'], ['Always allow', 'allow'], ['Block site', 'block'], ['Forget decision', 'forget']].map(([label, change]) =>
375
+ previewButton({ key: change, style: button, disabled: controlBusy || change === 'once' && ['block', 'allow'].includes(rule?.access), onClick: () => changePermission(change) }, label))),
376
+ h('details', { style: { marginTop: 10 } },
377
+ h('summary', { style: { cursor: 'pointer' } }, 'Developer access'),
378
+ h('p', { 'aria-label': 'Developer access status', style: { fontSize: 12 } }, permissions ?
379
+ `Developer mode: ${permissions.developerMode ? 'on globally' : 'off globally'}. This site: ${rule?.developer ? 'granted' : 'not granted'}. Scripts, CSS, console and network inspection require both.` : 'Refresh tabs to load Developer permissions.'),
380
+ h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 6 } },
381
+ previewButton({ style: button, disabled: controlBusy || !permissions, onClick: () => changePermission('developer-mode', permissions.developerMode ? 'off' : 'on') }, permissions?.developerMode ? 'Disable Developer mode' : 'Enable Developer mode'),
382
+ previewButton({ style: button, disabled: controlBusy || !permissions || rule?.access === 'block', onClick: () => changePermission(rule?.developer ? 'developer-block' : 'developer-allow') }, rule?.developer ? 'Revoke site Developer access' : 'Grant site Developer access'))),
383
+ h('p', { style: { fontSize: 12, opacity: 0.75, marginBottom: 0 } }, 'Changes apply to this exact origin. Action approvals still apply. Permissions sync while this pane is visible and idle; Refresh tabs updates them immediately.')),
384
+ h('label', null, h('input', { type: 'checkbox', checked: live, disabled: controlBusy || !pageId || !!point || !!handoff, onChange: e => setLive(e.target.checked) }), ' Refresh while visible'),
385
+ error && h('div', { role: 'alert', style: { whiteSpace: 'pre-wrap', color: '#db6868' } }, error),
386
+ frame && h('div', null,
387
+ h('div', { style: { fontSize: 12, overflowWrap: 'anywhere', marginBottom: 8 } }, `${frame.url} · ${new Date(frame.capturedAt).toLocaleTimeString()}`),
388
+ h('div', { style: { position: 'relative', lineHeight: 0, display: 'inline-block', maxWidth: '100%', ...(expanded ? { width: '100%' } : {}) } },
389
+ h('img', { src: `data:${frame.image.mimeType};base64,${frame.image.data}`, alt: expired ? 'Expired browser preview; refresh before annotating' : 'Captured browser page; click a point to annotate', role: 'button', tabIndex: 0, 'aria-disabled': busy || controlBusy || expired, 'aria-description': 'Enter or Space selects the center. Arrow keys move one pixel; Shift moves ten. Escape clears the point.', onClick: selectPoint, onKeyDown: selectPointKey, style: { maxWidth: '100%', maxHeight: expanded ? 'none' : '30vh', width: expanded ? '100%' : 'auto', height: 'auto', display: 'block', cursor: expired ? 'default' : 'crosshair', borderRadius: 6 } }),
390
+ point && h('span', { 'aria-label': 'Selected annotation point', style: { position: 'absolute', left: `${point.x * 100}%`, top: `${point.y * 100}%`, width: 20, height: 20, border: '3px solid #f97316', borderRadius: '50%', transform: 'translate(-50%, -50%)', pointerEvents: 'none', boxShadow: '0 0 0 2px white' } }))),
391
+ h('p', { style: { fontSize: 12, margin: 0, opacity: 0.75 } }, 'Preview shows the controlled Chrome tab. Click a point, or focus the image and press Enter or Space to select the center. Arrow keys move one pixel; Shift moves ten. Escape clears the point. Refresh after navigation or after one minute.'),
392
+ frame && point && h('p', { 'aria-label': 'Annotation coordinates', 'aria-live': 'polite', style: { fontSize: 12, margin: 0 } }, `Selected pixel: (${Math.round(point.x * (frame.width - 1))}, ${Math.round(point.y * (frame.height - 1))}) in ${frame.width}×${frame.height}.`),
393
+ frame && expired && h('p', { 'aria-label': 'Preview freshness', role: 'status', style: { fontSize: 12, margin: 0 } }, 'This preview has expired. Choose Refresh preview, then select the point again. Your draft is kept.'),
394
+ h('textarea', { 'aria-label': 'Browser annotation', placeholder: 'Describe what should change here…', value: text, maxLength: 4000, onChange: e => { draftRevision.current++; setText(e.target.value); }, style: { ...button, minHeight: 76, width: '100%', boxSizing: 'border-box', cursor: 'text' } }),
395
+ previewButton({ style: button, disabled: busy || controlBusy || expired || !!handoff || !frame || !point || !text.trim(), onClick: () => run(async current => {
396
+ if (!frame || handoff || expired || !point || !text.trim()) return;
397
+ // Background windows may throttle timers; check again at submission.
398
+ if (Date.now() - frame.capturedAt >= 60000) { expire(); return; }
399
+ const revision = draftRevision.current;
400
+ const result = await execute('annotate', { annotation: { token: frame.token, ...point, text } });
401
+ if (!current()) return;
402
+ setNotice(result.message); setPoint(null); setFrame(null);
403
+ // The editable field may already contain the user's next annotation.
404
+ if (draftRevision.current === revision) setText('');
405
+ }) }, 'Send annotation'),
406
+ notice && h('div', { role: 'status' }, notice));
407
+ }
408
+ function SessionPane({ execute, visible }) {
409
+ // Keep only the draft and selected tab while this session's pane is hidden.
410
+ // Sharing the revision also lets an in-flight send settle without clearing
411
+ // text edited after reopening. Pixels and refresh timers remain disposable.
412
+ const [pageId, setPageId] = useState(''), [text, setText] = useState('');
413
+ const draftRevision = useRef(0);
414
+ return visible ? h(SessionPreview, { execute, draft: { pageId, setPageId, text, setText, draftRevision } }) : null;
415
+ }
416
+ function Preview({ sessionId, execute, useTabInfo }) {
417
+ const { tab } = useTabInfo();
418
+ // Session changes discard the draft. Hidden panes discard their request
419
+ // lock and preview, while late responses stay bound to the original session.
420
+ return h(SessionPane, { key: sessionId, execute, visible: tab.visible });
421
+ }
422
+ return {
423
+ inject: ['slots', 'sidebarRightTabs', 'connection'],
424
+ apply(ctx) {
425
+ ctx.effect(() => ctx.sidebarRightTabs.register({ id, kind: 'dscode-browser', multiple: false, keepMounted: true, title: () => 'Browser preview', guide: [{ id: 'open', order: 31, title: () => 'Browser preview', description: () => 'Inspect the controlled Chrome tab and send a visual annotation.' }] }), 'dscode-browser-preview.type');
426
+ ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({ name: 'sidebar.right.pane.tab', key: id,
427
+ inject: sessionId => ({ sessionId, execute: async (action, args = {}) => {
428
+ const response = await ctx.connection.rpc.call('/api', 'dscode-browser', { sessionId, action, ...args });
429
+ if (!response.ok) throw Error(response.error.message);
430
+ return response.value;
431
+ } }),
432
+ }, Preview)), 'dscode-browser-preview.body');
433
+ },
434
+ };
435
+ },
436
+ });
437
+
438
+ components.push({
439
+ id: '@toddzheng024/dscode-desktop',
440
+ factory: require => {
441
+ const { createElement: h, useState, useEffect, useRef } = require('react');
442
+ const control = { padding: '8px 10px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', font: 'inherit' };
443
+ function Accounts({ execute }) {
444
+ const [state, setState] = useState(null), [key, setKey] = useState(''), [busy, setBusy] = useState(false);
445
+ const [error, setError] = useState(''), [notice, setNotice] = useState(''), [confirm, setConfirm] = useState(null);
446
+ const [refreshError, setRefreshError] = useState('');
447
+ const pending = useRef(null), foreground = useRef(false), alive = useRef(true);
448
+ const run = async (action, args = {}, background = false) => {
449
+ if (background ? pending.current : foreground.current) return;
450
+ if (!background) { foreground.current = true; setBusy(true); setError(''); }
451
+ const previous = pending.current;
452
+ const operation = (async () => {
453
+ if (previous) await previous;
454
+ if (!alive.current) return;
455
+ try {
456
+ const value = await execute(action, args);
457
+ if (!alive.current) return;
458
+ setState(value); setRefreshError('');
459
+ if (action === 'status' && !value.opencode.pending) setNotice('');
460
+ if (action !== 'status') { setNotice(value.notice ?? ''); setConfirm(null); }
461
+ if (action === 'save-openrouter' || action === 'remove-openrouter') setKey('');
462
+ } catch (error) {
463
+ if (alive.current) {
464
+ if (background) setRefreshError(`Unable to refresh accounts: ${error.message}`);
465
+ else setError(error.message);
466
+ }
467
+ }
468
+ })();
469
+ pending.current = operation;
470
+ try { await operation; } finally {
471
+ if (pending.current === operation) pending.current = null;
472
+ if (!background) { foreground.current = false; if (alive.current) setBusy(false); }
473
+ }
474
+ };
475
+ useEffect(() => { alive.current = true; void run('status'); return () => { alive.current = false; }; }, []);
476
+ useEffect(() => {
477
+ if (!state?.opencode.pending) return;
478
+ const timer = setInterval(() => { void run('status', {}, true); }, 2000);
479
+ return () => clearInterval(timer);
480
+ }, [state?.opencode.pending]);
481
+ const button = (text, action, disabled = false) => h('button', { type: 'button', style: control, disabled: busy || disabled, onClick: action }, text);
482
+ const destructive = (id, label) => button(confirm === id ? `Confirm ${label.toLowerCase()}` : label, () => confirm === id ? run(id) : setConfirm(id));
483
+ return h('section', { 'aria-label': 'DSCODE account settings', style: { padding: 20, display: 'grid', gap: 16, maxWidth: 720 } },
484
+ h('h2', { style: { margin: 0 } }, 'DSCODE accounts'),
485
+ error && h('div', { role: 'alert' }, error),
486
+ refreshError && h('div', { role: 'alert' }, refreshError),
487
+ notice && h('div', { role: 'status', style: { whiteSpace: 'pre-wrap' } }, notice),
488
+ button('Refresh account status', () => run('status')),
489
+ state && h('fieldset', { disabled: busy, style: { border: '1px solid #8886', borderRadius: 8, padding: 16 } },
490
+ h('legend', null, 'DSCODE OpenRouter'),
491
+ h('p', null, state.openrouter.configured ? `Configured (${state.openrouter.source ?? 'saved'})` : 'No API key saved'),
492
+ h('p', null, 'The key is shared with DSCODE terminal sessions and native routes using the same credential. Select DSCODE OpenRouter in the model picker for DSCODE reasoning controls.'),
493
+ h('label', null, 'OpenRouter API key', h('input', { 'aria-label': 'OpenRouter API key', type: 'password', autoComplete: 'new-password', style: { ...control, display: 'block', width: '100%', boxSizing: 'border-box' },
494
+ value: key, disabled: !state.openrouter.writable, onChange: event => { setKey(event.target.value); setConfirm(null); } })),
495
+ h('div', { style: { display: 'flex', gap: 8, marginTop: 12 } },
496
+ button('Save OpenRouter key', () => run('save-openrouter', { key }), !state.openrouter.writable || !key.trim()),
497
+ state.openrouter.configured && state.openrouter.writable && destructive('remove-openrouter', 'Remove OpenRouter key'))),
498
+ state && h('fieldset', { disabled: busy, style: { border: '1px solid #8886', borderRadius: 8, padding: 16 } },
499
+ h('legend', null, 'Grok'),
500
+ h('p', null, state.grok.configured ? 'Local Grok CLI login available' : 'No usable local Grok CLI login'),
501
+ h('p', null, 'Run grok login in a terminal, then refresh this status. DSCODE reads ~/.grok/auth.json without changing or refreshing its tokens.')),
502
+ state && h('fieldset', { disabled: busy, style: { border: '1px solid #8886', borderRadius: 8, padding: 16 } },
503
+ h('legend', null, 'OpenCode Go'),
504
+ h('p', { style: { whiteSpace: 'pre-wrap' } }, state.opencode.lines.join('\n')),
505
+ h('p', null, 'Sign in opens the OpenCode consent page in your browser. It identifies the OpenCode CLI client used for this login.'),
506
+ h('div', { style: { display: 'flex', gap: 8 } },
507
+ button('Sign in to OpenCode Go', () => run('login-opencode'), state.opencode.pending),
508
+ state.opencode.pending && button('Cancel OpenCode login', () => run('cancel-opencode')),
509
+ state.opencode.configured && destructive('logout-opencode', 'Sign out of OpenCode Go'))));
510
+ }
511
+ return { inject: ['slots', 'connection'], apply(ctx) {
512
+ ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dscode-accounts', order: 12, label: () => 'DSCODE accounts',
513
+ inject: () => ({ execute: async (action, args = {}) => {
514
+ const response = await ctx.connection.rpc.call('/api', 'dscode-accounts', { action, ...args });
515
+ if (!response.ok) throw Error(response.error.message);
516
+ return response.value;
517
+ } }),
518
+ }, Accounts));
519
+ } };
520
+ },
521
+ });
522
+
523
+ components.push({
524
+ id: '@toddzheng024/dscode-desktop',
525
+ factory: require => {
526
+ const { createElement: h, useState, useEffect, useRef } = require('react');
527
+ const control = { padding: '8px 10px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', font: 'inherit', boxSizing: 'border-box', minWidth: 0, maxWidth: '100%' };
528
+ const row = { display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' };
529
+ const initial = () => ({ id: '', prompt: '', objective: '', kind: 'interval', seconds: '3600', cron: '0 9 * * 1-5', timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
530
+ command: '[]', scriptMode: 'poll', persistent: true, permission: 'read-only', enabled: true });
531
+ function Schedules({ execute }) {
532
+ const [snapshot, setSnapshot] = useState(null), [session, setSession] = useState(''), [workspace, setWorkspace] = useState(null);
533
+ const [draft, setDraft] = useState(initial), [editing, setEditing] = useState(null), [busy, setBusy] = useState(false), [error, setError] = useState(''), [log, setLog] = useState('');
534
+ const [refreshError, setRefreshError] = useState('');
535
+ const pending = useRef(null), foreground = useRef(false), alive = useRef(true), selected = useRef(''), eventKeys = useRef(new Map()), actionError = useRef(null);
536
+ // Foreground failures must be visible even after submitting a long form.
537
+ // Polling errors use separate state and must not steal the user's focus.
538
+ useEffect(() => { if (error) actionError.current?.focus(); }, [error]);
539
+ const refresh = async () => {
540
+ const value = await execute('status');
541
+ if (!alive.current) return;
542
+ setSnapshot(value);
543
+ const id = value.sessions.some(item => item.id === selected.current) ? selected.current : value.sessions[0]?.id ?? '';
544
+ if (id !== selected.current) { setWorkspace(null); setEditing(null); setDraft(initial()); setLog(''); }
545
+ selected.current = id; setSession(id);
546
+ if (id) {
547
+ const result = await execute('workspace', { sessionId: id });
548
+ if (alive.current && selected.current === id) setWorkspace(result);
549
+ } else setWorkspace(null);
550
+ if (alive.current) setRefreshError('');
551
+ };
552
+ const run = async (task, background = false) => {
553
+ if (background ? pending.current : foreground.current) return;
554
+ if (!background) { foreground.current = true; setBusy(true); setError(''); }
555
+ const previous = pending.current;
556
+ const operation = (async () => {
557
+ if (previous) await previous;
558
+ if (!alive.current) return;
559
+ try { await task(); } catch (error) {
560
+ if (alive.current) {
561
+ if (background) setRefreshError(`Unable to refresh schedules: ${error.message}`);
562
+ else setError(error.message);
563
+ }
564
+ }
565
+ })();
566
+ pending.current = operation;
567
+ try { await operation; } finally {
568
+ if (pending.current === operation) pending.current = null;
569
+ if (!background) { foreground.current = false; if (alive.current) setBusy(false); }
570
+ }
571
+ };
572
+ useEffect(() => {
573
+ alive.current = true; void run(refresh);
574
+ const timer = setInterval(() => { void run(refresh, true); }, 5000);
575
+ return () => { alive.current = false; clearInterval(timer); };
576
+ }, []);
577
+ const mutate = (action, args) => run(async () => {
578
+ await execute(action, { sessionId: selected.current, args }); await refresh();
579
+ });
580
+ const button = (label, action, disabled = false) => h('button', { type: 'button', style: control, disabled: busy || disabled, onClick: action }, label);
581
+ const field = (label, key, type = 'text') => h('label', { style: { display: 'grid', gap: 4 } }, label,
582
+ h('input', { 'aria-label': label, style: control, type, value: draft[key], disabled: key === 'id' && !!editing,
583
+ onChange: event => setDraft(previous => ({ ...previous, [key]: event.target.value })) }));
584
+ const select = (label, key, values) => h('label', null, label, h('select', { 'aria-label': label, style: control, value: draft[key],
585
+ onChange: event => setDraft(previous => ({ ...previous, [key]: event.target.value })) }, ...values.map(([value, label]) => h('option', { key: value, value }, label))));
586
+ const edit = definition => {
587
+ setEditing(definition);
588
+ setDraft({ ...initial(), id: definition.id, prompt: definition.prompt, objective: definition.goal.objective, kind: definition.source.kind,
589
+ seconds: String(definition.source.seconds ?? definition.source.everySeconds ?? 3600), cron: definition.source.cron ?? '0 9 * * 1-5',
590
+ timezone: definition.source.timezone ?? initial().timezone, command: JSON.stringify(definition.source.command ?? []),
591
+ scriptMode: definition.source.mode ?? 'poll', persistent: definition.session.mode === 'persistent', permission: definition.permission, enabled: definition.enabled });
592
+ };
593
+ const save = () => run(async () => {
594
+ const source = draft.kind === 'calendar' ? { kind: 'calendar', cron: draft.cron, timezone: draft.timezone }
595
+ : draft.kind === 'interval' ? { kind: 'interval', seconds: Number(draft.seconds) }
596
+ : draft.kind === 'script' ? { kind: 'script', mode: draft.scriptMode, command: JSON.parse(draft.command),
597
+ ...(draft.scriptMode === 'poll' ? { everySeconds: Number(draft.seconds) } : {}) } : { kind: 'external' };
598
+ const previousSource = editing?.source.kind === draft.kind ? { ...editing.source } : {};
599
+ if (draft.kind === 'script' && draft.scriptMode === 'daemon') { delete previousSource.everySeconds; delete previousSource.timeoutSeconds; }
600
+ await execute('manage', { sessionId: selected.current, args: { action: editing ? 'update' : 'create', trigger_id: draft.id,
601
+ definition: { prompt: draft.prompt, source: { ...previousSource, ...source },
602
+ goal: { ...(editing?.goal ?? {}), objective: draft.objective.trim() || draft.prompt }, session: { mode: draft.persistent ? 'persistent' : 'new' },
603
+ permission: draft.permission, enabled: draft.enabled } } });
604
+ if (alive.current) { setEditing(null); setDraft(initial()); }
605
+ await refresh();
606
+ });
607
+ const runIdentity = definition => JSON.stringify([definition.workspace, definition.id]);
608
+ const runOnce = definition => run(async () => {
609
+ const identity = runIdentity(definition);
610
+ if (!eventKeys.current.has(identity)) eventKeys.current.set(identity, crypto.randomUUID());
611
+ await execute('jobs', { sessionId: selected.current, args: { action: 'schedule', trigger_id: definition.id, after: '1s', idempotency_key: eventKeys.current.get(identity) } });
612
+ // Keep this task's key through a lost reply or failed refresh, even if
613
+ // the user runs another task or changes workspace before retrying.
614
+ await refresh(); eventKeys.current.delete(identity);
615
+ });
616
+ return h('section', { 'aria-label': 'DSCODE scheduling settings', style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', gap: 16, padding: 20, maxWidth: 960, minWidth: 0, boxSizing: 'border-box', overflowWrap: 'anywhere' } },
617
+ h('h2', { style: { margin: 0 } }, 'DSCODE schedules'),
618
+ h('p', null, 'Enabling delivery runs due tasks for every registered project and remembers this choice when Desktop opens again. Keep the application open. Stopping delivery interrupts active tasks and script sources; pending jobs stay queued.'),
619
+ error && h('div', { role: 'alert', tabIndex: -1, ref: actionError }, error),
620
+ refreshError && h('div', { role: 'alert' }, refreshError),
621
+ snapshot && h('div', { role: 'status' }, `Delivery: ${snapshot.scheduler.running ? 'running' : snapshot.scheduler.stopping ? 'stopping' : 'stopped'} · Resume on launch: ${snapshot.scheduler.enabled ? 'on' : 'off'}${snapshot.scheduler.owner === 'external' ? ' · controlled by another process' : ''}`),
622
+ snapshot?.scheduler.error && h('div', { role: 'alert' }, snapshot.scheduler.error),
623
+ h('div', { style: row },
624
+ button('Enable delivery and resume on launch', () => run(async () => { await execute('start'); await refresh(); }), !snapshot || snapshot.scheduler.owner === 'external' || snapshot.scheduler.running || snapshot.scheduler.stopping),
625
+ button('Stop delivery and disable resume', () => run(async () => { await execute('stop'); await refresh(); }), !snapshot || (!snapshot.scheduler.enabled && !snapshot.scheduler.running)),
626
+ button('Refresh schedules', () => run(refresh))),
627
+ snapshot?.scheduler.owner === 'external' && h('p', null, snapshot.scheduler.next_step),
628
+ h('label', null, 'Workspace session', h('select', { 'aria-label': 'Workspace session', style: { ...control, display: 'block', width: '100%' }, value: session, disabled: busy,
629
+ onChange: event => { selected.current = event.target.value; setSession(event.target.value); setWorkspace(null); setEditing(null); setDraft(initial()); setLog(''); void run(refresh); } },
630
+ h('option', { value: '' }, 'Choose an open DSCODE session'), ...(snapshot?.sessions ?? []).map(item => h('option', { key: item.id, value: item.id }, `${item.workspace} · ${item.id.slice(0, 8)}`)))),
631
+ !session && h('p', null, 'Open a DSCODE session in the project you want to schedule.'),
632
+ workspace && h('div', null,
633
+ !workspace.writable && h('p', null, 'This session can inspect schedules. Use a writable, non-plan session to change them.'),
634
+ ...workspace.problems.map(problem => h('div', { role: 'alert', key: problem.path }, `${problem.path}: ${problem.message}`)),
635
+ h('h3', null, 'Tasks'),
636
+ ...workspace.definitions.map(item => h('article', { key: item.id, style: { padding: '12px 0', borderBottom: '1px solid #8884' } },
637
+ h('strong', null, item.id), h('p', null, `${item.enabled ? 'Enabled' : 'Paused'} · ${item.source.kind} · ${item.permission}`), h('p', { style: { whiteSpace: 'pre-wrap' } }, item.prompt),
638
+ h('div', { style: row }, button(`Edit ${item.id}`, () => edit(item), !workspace.writable || item.origin !== 'project'),
639
+ button(`${item.enabled ? 'Pause' : 'Enable'} ${item.id}`, () => mutate('manage', { action: item.enabled ? 'disable' : 'enable', trigger_id: item.id }), !workspace.writable || item.origin !== 'project'),
640
+ button(`${eventKeys.current.has(runIdentity(item)) ? 'Retry' : 'Run'} ${item.id} once`, () => runOnce(item), !workspace.writable || !item.enabled)),
641
+ item.source.kind === 'script' && h('div', { style: row },
642
+ h('span', null, `Source: ${workspace.sources.find(source => source.triggerId === item.id)?.status ?? 'unregistered'}`),
643
+ ...['start', 'stop', 'restart'].map(action => button(`${action} ${item.id} source`, () => mutate('source', { action, trigger_id: item.id }), !workspace.writable)),
644
+ button(`Read ${item.id} source log`, () => run(async () => { const result = await execute('source', { sessionId: selected.current, args: { action: 'logs', trigger_id: item.id } }); if (alive.current) setLog(result.log || 'No source output recorded.'); }))))),
645
+ log && h('pre', { 'aria-label': 'Source log', style: { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' } }, log),
646
+ !workspace.definitions.length && h('p', null, 'No tasks in this workspace.'),
647
+ h('fieldset', { disabled: busy || !workspace.writable, style: { display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', minWidth: 0, gap: 12, border: '1px solid #8886', borderRadius: 8, padding: 16 } },
648
+ h('legend', null, editing ? `Edit ${editing.id}` : 'Create task'), field('Task ID', 'id'),
649
+ h('label', null, 'Task instructions', h('textarea', { 'aria-label': 'Task instructions', style: { ...control, display: 'block', width: '100%', minHeight: 90 }, value: draft.prompt,
650
+ onChange: event => setDraft(previous => ({ ...previous, prompt: event.target.value })) })),
651
+ field('Goal (defaults to task instructions)', 'objective'),
652
+ select('Schedule', 'kind', [['interval', 'Interval'], ['calendar', 'Calendar'], ['external', 'Manual or external event'], ['script', 'Script events']]),
653
+ draft.kind === 'calendar' && h('div', { style: { display: 'grid', gap: 8 } }, field('Cron expression', 'cron'), field('Timezone', 'timezone')),
654
+ draft.kind === 'script' && h('div', { style: { display: 'grid', gap: 8 } }, field('Script command (JSON argument array)', 'command'), select('Script mode', 'scriptMode', [['poll', 'Poll'], ['daemon', 'Daemon']])),
655
+ (draft.kind === 'interval' || (draft.kind === 'script' && draft.scriptMode === 'poll')) && field('Interval seconds', 'seconds', 'number'),
656
+ select('Run permission', 'permission', [['read-only', 'Read only'], ['workspace-write', 'Write in this workspace']]),
657
+ ...[['persistent', 'Continue the task’s own session'], ['enabled', 'Enable this task']].map(([key, label]) => h('label', { key }, h('input', { type: 'checkbox', checked: draft[key],
658
+ onChange: event => setDraft(previous => ({ ...previous, [key]: event.target.checked })) }), label)),
659
+ h('div', { style: row }, button(editing ? 'Save task changes' : 'Create task', save, !draft.id.trim() || !draft.prompt.trim()),
660
+ editing && button('Cancel editing', () => { setEditing(null); setDraft(initial()); }))),
661
+ h('h3', null, 'Recent jobs'),
662
+ ...workspace.jobs.map(job => h('div', { key: job.id, style: { ...row, padding: '8px 0' } },
663
+ h('span', { style: { flex: 1 } }, `${job.triggerId}: ${job.state}${job.reason ? ` (${job.reason})` : ''} · ${new Date(job.dueAt).toLocaleString()}`),
664
+ job.state === 'pending' && button(`Cancel ${job.triggerId} job`, () => mutate('jobs', { action: 'cancel', job_id: job.id }), !workspace.writable))),
665
+ !workspace.jobs.length && h('p', null, 'No jobs in this workspace.')));
666
+ }
667
+ return { inject: ['slots', 'connection'], apply(ctx) {
668
+ ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dscode-schedules', order: 13, label: () => 'DSCODE schedules',
669
+ inject: () => ({ execute: async (action, args = {}) => {
670
+ const response = await ctx.connection.rpc.call('/api', 'dscode-triggers', { action, ...args });
671
+ if (!response.ok) throw Error(response.error.message);
672
+ return response.value;
673
+ } }),
674
+ }, Schedules));
675
+ } };
676
+ },
677
+ });
678
+
679
+ components.push({
680
+ id: '@toddzheng024/dscode-desktop',
681
+ factory: require => {
682
+ const { createElement: h, useState, useEffect, useRef } = require('react');
683
+ const control = { padding: '7px 10px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', font: 'inherit' };
684
+ function Inbox({ execute }) {
685
+ const [state, setState] = useState(null), [mail, setMail] = useState(null), [offset, setOffset] = useState(0);
686
+ const [error, setError] = useState(''), [notice, setNotice] = useState(''), [busy, setBusy] = useState(false), [sent, setSent] = useState(false);
687
+ const [form, setForm] = useState({ account: '', host: 'imap.gmail.com', port: '993', mailbox: 'INBOX', password: '' });
688
+ const [path, setPath] = useState('');
689
+ const active = useRef(null), foreground = useRef(false), alive = useRef(true), requestId = useRef(''), formTouched = useRef(false);
690
+ const run = async (work, background = false) => {
691
+ if (background ? active.current : foreground.current) return;
692
+ if (!background) { foreground.current = true; setBusy(true); setError(''); }
693
+ const previous = active.current;
694
+ const operation = (async () => {
695
+ if (previous) await previous;
696
+ if (!alive.current) return;
697
+ try { await work(); } catch (error) { if (alive.current) setError(error.message); }
698
+ })();
699
+ active.current = operation;
700
+ try { await operation; } finally {
701
+ if (active.current === operation) active.current = null;
702
+ if (!background) { foreground.current = false; if (alive.current) setBusy(false); }
703
+ }
704
+ };
705
+ const refresh = async page => {
706
+ const result = await execute('list', { offset: page });
707
+ if (!alive.current) return;
708
+ setState(result); setOffset(page);
709
+ if (!formTouched.current && result.imap.connected) setForm(old => ({ ...old, account: result.imap.account ?? '',
710
+ host: result.imap.host ?? 'imap.gmail.com', port: String(result.imap.port ?? 993), mailbox: result.imap.mailbox ?? 'INBOX' }));
711
+ };
712
+ useEffect(() => { alive.current = true; void run(() => refresh(0)); return () => { alive.current = false; }; }, []);
713
+ useEffect(() => {
714
+ const timer = setInterval(() => { void run(() => refresh(offset), true); }, 2000);
715
+ return () => clearInterval(timer);
716
+ }, [offset]);
717
+ const action = (name, args = {}) => run(async () => {
718
+ await execute(name, args);
719
+ if (alive.current) await refresh(offset);
720
+ });
721
+ const button = (text, click, disabled = false) => h('button', { type: 'button', style: control, disabled: busy || disabled, onClick: click }, text);
722
+ const field = (name, label, type = 'text') => h('label', { style: { display: 'grid', gap: 4 } }, label,
723
+ h('input', { 'aria-label': label, type, autoComplete: type === 'password' ? 'new-password' : 'off', style: { ...control, width: '100%', boxSizing: 'border-box' },
724
+ value: form[name], onChange: event => { formTouched.current = true; setForm(old => ({ ...old, [name]: event.target.value })); } }));
725
+ const select = key => run(async () => {
726
+ setMail(null); setSent(false); setNotice('');
727
+ const value = await execute('read', { key });
728
+ if (alive.current) { setMail(value); requestId.current = crypto.randomUUID(); }
729
+ });
730
+ return h('section', { 'aria-label': 'DSCODE email inbox', style: { padding: 16, overflow: 'auto', height: '100%', boxSizing: 'border-box', display: 'flex', flexDirection: 'column', gap: 12 } },
731
+ h('strong', null, 'Email inbox'),
732
+ h('p', { style: { margin: 0, fontSize: 12 } }, 'Shared local inbox. Reading mail does not start the agent. Add only the message you want this session to use as external context.'),
733
+ error && h('div', { role: 'alert' }, error),
734
+ notice && h('div', { role: 'status' }, notice),
735
+ h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
736
+ button('Refresh inbox', () => run(() => refresh(offset))),
737
+ button('Sync now', () => action('sync'), !!state?.pending || !(state?.imap.connected || state?.gmail.connected)),
738
+ state?.pending && button('Cancel email operation', () => action('cancel'))),
739
+ state && h('div', { style: { fontSize: 12 } }, state.pending ? `Running: ${state.pending}`
740
+ : state.last?.message ?? (state.imap.connected ? `IMAP · ${state.imap.account} · ${state.imap.mailbox}` : state.gmail.connected ? `Gmail · ${state.gmail.account}` : 'No mailbox connected')),
741
+ state?.rejected > 0 && h('div', { role: 'alert' }, `${state.rejected} unreadable local records were skipped.`),
742
+ h('details', null, h('summary', null, 'Mailbox connection'),
743
+ h('div', { style: { display: 'grid', gap: 10, marginTop: 10 } },
744
+ h('label', null, h('input', { type: 'checkbox', checked: state?.enabled ?? false, disabled: busy || !state,
745
+ onChange: event => action('background', { enabled: event.target.checked }) }), ' Sync in the background while Desktop is open'),
746
+ h('p', { style: { fontSize: 12, margin: 0 } }, 'IMAP takes precedence over Gmail OAuth. Only new [ToAgent] plain-text mail is imported. The first connection records a starting point without importing old mail.'),
747
+ h('form', { onSubmit: event => { event.preventDefault(); void run(async () => {
748
+ try { await execute('connect-imap', { config: { ...form, port: Number(form.port) } }); }
749
+ finally { if (alive.current) setForm(old => ({ ...old, password: '' })); }
750
+ if (alive.current) await refresh(offset);
751
+ }); } }, h('fieldset', { disabled: busy || !!state?.pending, style: { display: 'grid', gap: 8, border: '1px solid #8886', borderRadius: 6 } },
752
+ h('legend', null, 'IMAP with an application password'),
753
+ field('account', 'Mailbox login'), field('host', 'IMAP host'), field('port', 'TLS port'), field('mailbox', 'Mailbox folder'), field('password', 'Application password', 'password'),
754
+ h('p', { style: { margin: 0, fontSize: 12 } }, 'Uses verified TLS and reads the selected folder without changing flags. The password is saved locally after a successful connection and never enters the conversation. Reconnect the same account and folder to update it.'),
755
+ h('button', { style: control, type: 'submit', disabled: !form.account.trim() || !form.password }, 'Connect IMAP'))),
756
+ h('fieldset', { disabled: busy || !!state?.pending, style: { display: 'grid', gap: 8, border: '1px solid #8886', borderRadius: 6 } },
757
+ h('legend', null, 'Optional Gmail OAuth'),
758
+ h('label', null, 'Google Desktop client JSON path', h('input', { 'aria-label': 'Google Desktop client JSON path', value: path,
759
+ onChange: event => setPath(event.target.value), style: { ...control, width: '100%', boxSizing: 'border-box' } })),
760
+ button('Import OAuth client', () => action('configure-gmail', { path }), !path.trim()),
761
+ h('p', { style: { margin: 0, fontSize: 12 } }, 'Authorization opens Google in your browser and requests mail read access plus label and filter management. It creates or reuses the ToAgent label and subject filter. Use your Google account settings to revoke access.'),
762
+ button('Authorize Gmail', () => action('connect-gmail'), !state?.gmail.configured)))),
763
+ h('div', { 'aria-label': 'Received emails', style: { display: 'grid', gap: 6, maxHeight: '26vh', overflow: 'auto' } },
764
+ ...(state?.emails ?? []).map(row => h('button', { key: row.key, type: 'button', style: { ...control, textAlign: 'left', overflowWrap: 'anywhere', borderColor: mail?.key === row.key ? '#5794ff' : '#8886' },
765
+ disabled: busy, onClick: () => select(row.key) }, h('strong', null, row.subject), h('div', { style: { fontSize: 12 } }, `${row.from} · ${new Date(row.updatedAt).toLocaleString()}`))),
766
+ state?.emails.length === 0 && h('p', null, 'No emails received.')),
767
+ state && h('div', { style: { display: 'flex', gap: 8 } },
768
+ button('Previous emails', () => run(() => refresh(Math.max(0, offset - 50))), offset === 0),
769
+ button('Next emails', () => run(() => refresh(offset + 50)), offset + 50 >= state.total)),
770
+ mail && h('article', { 'aria-label': 'Email preview', style: { overflowWrap: 'anywhere' } },
771
+ h('h3', null, mail.subject), h('p', null, `From: ${mail.from}`), h('p', { style: { fontSize: 12 } }, `${mail.account} · ${mail.connector}`),
772
+ h('pre', { style: { whiteSpace: 'pre-wrap', font: 'inherit', maxHeight: '32vh', overflow: 'auto' } }, mail.body),
773
+ button(sent ? 'Added to this session' : 'Add email to this session', () => run(async () => {
774
+ const result = await execute('inject', { key: mail.key, revision: mail.revision, requestId: requestId.current });
775
+ if (alive.current) { setSent(true); setNotice(result.message); }
776
+ }), sent)));
777
+ }
778
+ function EmailPane({ sessionId, execute, useTabInfo }) {
779
+ const { tab } = useTabInfo();
780
+ return tab.visible ? h(Inbox, { key: sessionId, execute }) : null;
781
+ }
782
+ const id = '@toddzheng024/dscode-email-desktop';
783
+ return { inject: ['slots', 'sidebarRightTabs', 'connection'], apply(ctx) {
784
+ ctx.effect(() => ctx.sidebarRightTabs.register({ id, kind: 'dscode-email', multiple: false, title: () => 'Email inbox',
785
+ guide: [{ id: 'open', order: 32, title: () => 'Email inbox', description: () => 'Read local mail and add a chosen message to this session.' }] }), 'dscode-email.type');
786
+ ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({ name: 'sidebar.right.pane.tab', key: id,
787
+ inject: sessionId => ({ sessionId, execute: async (action, args = {}) => {
788
+ const response = await ctx.connection.rpc.call('/api', 'dscode-email', { sessionId, action, ...args });
789
+ if (!response.ok) throw Error(response.error.message);
790
+ return response.value;
791
+ } }),
792
+ }, EmailPane)), 'dscode-email.body');
793
+ } };
794
+ },
795
+ });
796
+
797
+ components.push({
798
+ id: '@toddzheng024/dscode-desktop',
799
+ factory: require => {
800
+ const { createElement: h, useState, useEffect, useRef } = require('react');
801
+ const money = (cost, partial = false) => cost === null || partial && cost === 0 ? 'Unknown' : `$${cost.toFixed(4)}${partial ? '+' : ''}`;
802
+ const percent = value => value === null ? 'Unknown' : `${value.toFixed(1)}%`;
803
+ const speed = value => value === null ? 'Unknown' : `${value.toFixed(1)} tokens/s`;
804
+ const control = { padding: '7px 10px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', font: 'inherit' };
805
+ function Usage({ execute }) {
806
+ const [state, setState] = useState(null), [error, setError] = useState('');
807
+ const active = useRef(false), alive = useRef(true);
808
+ const refresh = async () => {
809
+ if (active.current) return; active.current = true;
810
+ try { const next = await execute(); if (alive.current) { setState(next); setError(''); } }
811
+ catch (error) { if (alive.current) { setState(null); setError(error.message); } }
812
+ finally { active.current = false; }
813
+ };
814
+ useEffect(() => { alive.current = true; void refresh(); const timer = setInterval(refresh, 1000);
815
+ return () => { alive.current = false; clearInterval(timer); }; }, []);
816
+ const fact = (label, value) => h('div', { key: label, style: { padding: 10, border: '1px solid #8884', borderRadius: 6 } },
817
+ h('dt', { style: { fontSize: 12, marginBottom: 5 } }, label), h('dd', { style: { margin: 0, fontVariantNumeric: 'tabular-nums' } }, value));
818
+ return h('section', { 'aria-label': 'DSCODE session usage', style: { padding: 16, height: '100%', overflow: 'auto', boxSizing: 'border-box' } },
819
+ h('h2', { style: { marginTop: 0 } }, 'Session usage'),
820
+ h('p', { style: { fontSize: 12 } }, 'Recorded model usage for this DSCODE session, including attributed child and background calls. Costs are estimates, not an invoice.'),
821
+ h('button', { type: 'button', style: control, onClick: refresh }, 'Refresh usage'),
822
+ error && h('p', { role: 'alert' }, error),
823
+ !state && !error && h('p', { role: 'status' }, 'Loading usage…'),
824
+ state && h('div', null,
825
+ h('p', { style: { overflowWrap: 'anywhere', fontSize: 12 } }, `${state.provider} / ${state.model}`),
826
+ h('p', { role: 'status' }, state.requestActive ? 'Model request in progress' : 'No active model request'),
827
+ h('dl', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: 8 } },
828
+ fact('Recorded cost', money(state.cost, state.partial)), fact('Model calls', state.calls ?? 'Unknown'),
829
+ fact('Pending calls', state.pending ?? 'Unknown'), fact('Context estimate', percent(state.contextPercent)),
830
+ fact('Input cache share', percent(state.cachePercent)), fact('Smoothed request speed', speed(state.currentTps)),
831
+ fact('Session average speed', speed(state.averageTps)),
832
+ state.balance !== null && fact('Cached provider balance', money(state.balance)),
833
+ state.subscription && fact('Cached subscription usage', state.subscription),
834
+ state.budget && fact('Configured cost budget', `${money(state.budget.spent)} / ${money(state.budget.limit)} · ${state.budget.state}`)),
835
+ h('p', { style: { fontSize: 12 } }, 'Unknown means a reading or price is unavailable. + marks a partial cost. Pending calls have not settled. Cached account figures may lag the provider.'),
836
+ h('h3', null, 'Cost by turn'),
837
+ state.turns.length ? h('table', { style: { width: '100%', borderCollapse: 'collapse', textAlign: 'left', fontVariantNumeric: 'tabular-nums' } },
838
+ h('thead', null, h('tr', null, ...['Turn', 'Calls', 'Cost'].map(label => h('th', { key: label, style: { padding: 6 } }, label)))),
839
+ h('tbody', null, ...state.turns.map(row => h('tr', { key: row.turn }, h('td', { style: { padding: 6 } }, row.turn), h('td', { style: { padding: 6 } }, row.calls), h('td', { style: { padding: 6 } }, money(row.cost, row.partial))))))
840
+ : h('p', null, 'No turn costs recorded yet.'),
841
+ state.totalTurns > 50 && h('p', { style: { fontSize: 12 } }, `Showing the latest 50 of ${state.totalTurns} turns.`),
842
+ h('p', { style: { fontSize: 12 } }, `Cost outside recorded turn windows: ${money(state.unattributed, state.unattributedPartial)}`),
843
+ h('p', { style: { fontSize: 12 } }, 'Refreshes once per second while visible. Reading usage does not start the agent or request new account data.')));
844
+ }
845
+ function UsagePane({ sessionId, execute, useTabInfo }) { const { tab } = useTabInfo(); return tab.visible ? h(Usage, { key: sessionId, execute }) : null; }
846
+ const id = '@toddzheng024/dscode-metrics-desktop';
847
+ return { inject: ['slots', 'sidebarRightTabs', 'connection'], apply(ctx) {
848
+ ctx.effect(() => ctx.sidebarRightTabs.register({ id, kind: 'dscode-metrics', multiple: false, title: () => 'Session usage',
849
+ guide: [{ id: 'open', order: 33, title: () => 'Session usage', description: () => 'Inspect DSCODE cost, context, cache and request speed.' }] }), 'dscode-metrics.type');
850
+ ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({ name: 'sidebar.right.pane.tab', key: id,
851
+ inject: sessionId => ({ sessionId, execute: async () => {
852
+ const response = await ctx.connection.rpc.call('/api', 'dscode-metrics', { action: 'status', sessionId });
853
+ if (!response.ok) throw Error(response.error.message); return response.value;
854
+ } }),
855
+ }, UsagePane)), 'dscode-metrics.body');
856
+ } };
857
+ },
858
+ });
859
+
860
+ components.push({
861
+ id: '@toddzheng024/dscode-desktop',
862
+ factory: require => {
863
+ const { createElement: h, useState, useEffect, useRef } = require('react');
864
+ const columns = [['pending', 'Pending'], ['running', 'Running'], ['verifying', 'Verifying'], ['complete', 'Complete']];
865
+ const control = { padding: '7px 10px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', font: 'inherit' };
866
+ function Board({ execute }) {
867
+ const [state, setState] = useState(null), [error, setError] = useState('');
868
+ const active = useRef(false), alive = useRef(true);
869
+ const refresh = async () => {
870
+ if (active.current) return; active.current = true;
871
+ try { const next = await execute(); if (alive.current) { setState(next); setError(''); } }
872
+ catch (error) { if (alive.current) { setState(null); setError(error.message); } }
873
+ finally { active.current = false; }
874
+ };
875
+ useEffect(() => { alive.current = true; void refresh(); const timer = setInterval(refresh, 1000);
876
+ return () => { alive.current = false; clearInterval(timer); }; }, []);
877
+ const card = (task, column) => h('li', { key: task.id, style: { listStyle: 'none', border: '1px solid #8885', borderRadius: 6, padding: 10, marginTop: 8, overflowWrap: 'anywhere' } },
878
+ h('strong', null, `${task.id} · ${task.title}`),
879
+ h('p', { style: { margin: '6px 0', fontSize: 12 } }, `/${task.child} · ${task.priority} priority`),
880
+ task.waiting && h('p', null, 'Waiting for the coordinator’s answer'),
881
+ task.blockedBy.length > 0 ? h('p', null, `Blocked by ${task.blockedBy.join(', ')}`) : column === 'pending' && h('p', null, 'Ready to launch'),
882
+ column === 'verifying' && h('p', null, 'Awaiting coordinator verification'),
883
+ task.dependsOn.length > 0 && h('p', { style: { fontSize: 12 } }, `Depends on ${task.dependsOn.join(', ')}`),
884
+ (task.detail || task.note || task.verification || task.worktree) && h('details', null, h('summary', null, 'Task details'),
885
+ ...[['Scope and checks', task.detail], ['Reopened because', task.note], ['Verification evidence', task.verification], ['Worktree', task.worktree]]
886
+ .filter(([, value]) => value).map(([label, value]) => h('div', { key: label, style: { marginTop: 8 } }, h('strong', null, label), h('p', { style: { whiteSpace: 'pre-wrap', margin: '4px 0' } }, value)))));
887
+ return h('section', { 'aria-label': 'DSCODE delegation board', style: { padding: 16, height: '100%', overflow: 'auto', boxSizing: 'border-box' } },
888
+ h('h2', { style: { marginTop: 0 } }, 'Delegation board'),
889
+ h('p', { style: { fontSize: 12 } }, 'Use /delegate <task> in the main session to coordinate work in isolated Git worktrees. The coordinator plans tasks and records verified results.'),
890
+ h('button', { type: 'button', style: control, onClick: refresh }, 'Refresh board'),
891
+ error && h('p', { role: 'alert' }, error),
892
+ !state && !error && h('p', { role: 'status' }, 'Loading delegation board…'),
893
+ state && h('div', null,
894
+ h('p', { role: 'status' }, `${state.running} of ${state.limit} child slots running`),
895
+ !columns.some(([key]) => state.columns[key].length) && h('p', null, 'No delegated tasks yet.'),
896
+ h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 12 } },
897
+ ...columns.map(([key, label]) => h('section', { key, 'aria-label': label }, h('h3', null, `${label} (${state.columns[key].length})`),
898
+ state.columns[key].length ? h('ul', { style: { padding: 0, margin: 0 } }, ...state.columns[key].map(task => card(task, key))) : h('p', { style: { fontSize: 12 } }, 'None')))),
899
+ h('p', { style: { fontSize: 12 } }, 'Refreshes once per second while visible. Reading the board does not launch children, change tasks or merge work.')));
900
+ }
901
+ function BoardPane({ sessionId, execute, useTabInfo }) { const { tab } = useTabInfo(); return tab.visible ? h(Board, { key: sessionId, execute }) : null; }
902
+ const id = '@toddzheng024/dscode-delegation-desktop';
903
+ return { inject: ['slots', 'sidebarRightTabs', 'connection'], apply(ctx) {
904
+ ctx.effect(() => ctx.sidebarRightTabs.register({ id, kind: 'dscode-delegation', multiple: false, title: () => 'Delegation board',
905
+ guide: [{ id: 'open', order: 34, title: () => 'Delegation board', description: () => 'Follow DSCODE delegated tasks, dependencies and verification.' }] }), 'dscode-delegation.type');
906
+ ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({ name: 'sidebar.right.pane.tab', key: id,
907
+ inject: sessionId => ({ sessionId, execute: async () => {
908
+ const response = await ctx.connection.rpc.call('/api', 'dscode-delegation', { action: 'status', sessionId });
909
+ if (!response.ok) throw Error(response.error.message); return response.value;
910
+ } }),
911
+ }, BoardPane)), 'dscode-delegation.body');
912
+ } };
913
+ },
914
+ });
915
+
916
+ components.push({
917
+ id: '@toddzheng024/dscode-desktop',
918
+ factory: require => {
919
+ const { createElement: h, useState, useEffect, useRef } = require('react');
920
+ const control = { padding: '8px 10px', border: '1px solid #8886', borderRadius: 6, background: 'transparent', color: 'inherit', font: 'inherit' };
921
+ const box = { border: '1px solid #8886', borderRadius: 8, padding: 16 };
922
+ function Hub({ execute, manage, close }) {
923
+ const [query, setQuery] = useState(''), [category, setCategory] = useState(''), [categories, setCategories] = useState([]);
924
+ const [results, setResults] = useState(null), [detail, setDetail] = useState(null), [preview, setPreview] = useState(null);
925
+ const [operation, setOperation] = useState(null), [error, setError] = useState(''), [busy, setBusy] = useState(false);
926
+ const alive = useRef(true), locked = useRef(false), pageQuery = useRef({ query: '', category: '' });
927
+ const run = async fn => {
928
+ if (locked.current) return;
929
+ locked.current = true; setBusy(true); setError('');
930
+ try { await fn(); } catch (error) { if (alive.current) setError(error.message); }
931
+ finally { locked.current = false; if (alive.current) setBusy(false); }
932
+ };
933
+ const search = async (more = false) => {
934
+ const input = more ? pageQuery.current : { query, category };
935
+ if (!more) { setDetail(null); setPreview(null); setResults(null); }
936
+ const value = await execute('search', { ...input, ...(more ? results.nextPage ? { page: results.nextPage } : { cursor: results.nextCursor } : {}) });
937
+ if (!alive.current) return;
938
+ pageQuery.current = input;
939
+ setResults(more ? { ...value, items: [...results.items, ...value.items.filter(item => !results.items.some(previous => previous.packageName === item.packageName))] } : value);
940
+ };
941
+ const load = () => run(async () => {
942
+ const [list, initial, status] = await Promise.all([execute('categories'), execute('search', { query: '', category: '' }), execute('status')]);
943
+ if (alive.current) { setCategories(list.items); setResults(initial); setOperation(status.operation); pageQuery.current = { query: '', category: '' }; }
944
+ });
945
+ useEffect(() => { alive.current = true; void load(); return () => { alive.current = false; }; }, []);
946
+ useEffect(() => {
947
+ if (operation?.status !== 'running') return;
948
+ let stopped = false, polling = false;
949
+ const timer = setInterval(async () => {
950
+ if (polling) return; polling = true;
951
+ try {
952
+ const value = await execute('operation', { requestId: operation.requestId });
953
+ if (!stopped && alive.current) setOperation(value);
954
+ } catch (error) { if (!stopped && alive.current) setError(`Could not refresh installation: ${error.message}`); }
955
+ finally { polling = false; }
956
+ }, 1000);
957
+ return () => { stopped = true; clearInterval(timer); };
958
+ }, [operation?.requestId, operation?.status]);
959
+ const installing = operation?.status === 'running';
960
+ const button = (label, action, disabled = false) => h('button', { type: 'button', style: control, disabled: busy || disabled, onClick: action }, label);
961
+ const openManagement = packageName => run(async () => { manage(packageName); close?.(); });
962
+ const show = name => run(async () => {
963
+ setDetail(null); setPreview(null);
964
+ const value = await execute('detail', { packageName: name }); if (alive.current) setDetail(value);
965
+ });
966
+ const installed = detail?.installed || (operation?.packageName === detail?.packageName && operation?.result?.application === 'applied' && operation.result.bundle === detail?.packageName);
967
+ const security = item => `Hub scan: ${item.security?.status ?? 'not reported'}`;
968
+ const result = operation?.result;
969
+ return h('section', { 'aria-label': 'Plugin Hub', style: { padding: 20, display: 'grid', gap: 16, maxWidth: 900 } },
970
+ h('h2', { style: { margin: 0 } }, 'Plugin Hub'),
971
+ h('p', null, 'Discover community plugins from DSH Plugin Hub. Install here, then enable and configure in Settings → Plugins.'),
972
+ h('form', { style: { display: 'flex', flexWrap: 'wrap', gap: 8 }, onSubmit: event => { event.preventDefault(); void run(() => search()); } },
973
+ h('input', { type: 'search', 'aria-label': 'Search plugins', placeholder: 'Search plugins or describe a capability', value: query, maxLength: 300, style: { ...control, flex: 1 }, disabled: busy || installing, onChange: event => setQuery(event.target.value) }),
974
+ h('select', { 'aria-label': 'Plugin category', value: category, style: control, disabled: busy || installing, onChange: event => setCategory(event.target.value) },
975
+ h('option', { value: '' }, 'All categories'), ...categories.map(item => h('option', { key: item.name, value: item.name }, `${item.displayName} (${item.count})`))),
976
+ h('button', { type: 'submit', disabled: busy || installing, style: control }, busy ? 'Loading…' : 'Search')),
977
+ error && h('div', { role: 'alert' }, error, ' ', button('Refresh catalog', load, installing)),
978
+ operation && h('div', { role: 'status', style: box },
979
+ h('strong', null, operation.spec),
980
+ h('p', null, installing ? 'Installing with the official plugin manager…' : operation.error ? operation.error : result?.application === 'applied' ? 'Installation completed. Enable the plugin in Settings → Plugins.' :
981
+ result?.application === 'restart-required' ? 'The plugin manager requires a Desktop restart. Check Settings → Plugins after restarting.' :
982
+ result?.application === 'cancelled' ? 'Installation cancelled.' : `Installation outcome: ${result?.application ?? 'unknown'}. Check Settings → Plugins.`),
983
+ result?.error && h('p', null, result.error.message),
984
+ result?.warnings?.map((warning, index) => h('p', { key: index }, warning)),
985
+ result?.pendingBuilds?.length > 0 && h('p', null, `Build approval is required for ${result.pendingBuilds.join(', ')}. Review it through the official plugin manager.`),
986
+ installing ? button('Cancel installation', () => run(async () => { const value = await execute('cancel', { requestId: operation.requestId }); if (alive.current) setOperation(value); })) :
987
+ button('Manage installed plugin', () => openManagement(operation.packageName))),
988
+ detail && h('article', { style: box, 'aria-label': 'Plugin details' },
989
+ h('h3', null, detail.displayName), h('code', null, `${detail.packageName}@${detail.latestVersion}`),
990
+ h('p', null, detail.summary), h('p', { style: { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' } }, detail.description),
991
+ h('p', null, `${detail.categories.join(' · ')} · ${detail.weeklyDownloads} weekly downloads`),
992
+ h('p', null, `Repository: ${detail.repository} · License: ${detail.license ?? 'not reported'}`),
993
+ h('p', null, `${detail.claimed ? 'Publisher claimed' : 'Publisher unclaimed'} · ${detail.verified ? 'Publisher verified' : 'Publisher unverified'}`),
994
+ h('p', null, security(detail), '. Publisher verification and scans do not guarantee safety.'),
995
+ detail.security && h('ul', null, ...[['dependencyInventoryComplete', 'Complete dependency inventory'], ['advisoryScanned', 'Vulnerability advisories checked'], ['behaviorAnalyzed', 'Behavior analysis completed']].map(([key, label]) => h('li', { key }, `${label}: ${detail.security[key] === true ? 'yes' : 'no'}`))),
996
+ h('a', { href: detail.url, target: '_blank', rel: 'noreferrer' }, 'View on DSH Plugin Hub'),
997
+ h('p', null, `Your Desktop: DSH ${detail.environment.runtime} · ${detail.environment.platform}`),
998
+ detail.candidate.compatibility && h('p', null, `Declared compatibility: DSH ${detail.candidate.compatibility.dsh} · ${detail.candidate.compatibility.surfaces.join(', ')} · ${detail.candidate.compatibility.platforms.join(', ') || 'all platforms'}`),
999
+ detail.candidate.reasons.length > 0 && h('ul', null, ...detail.candidate.reasons.map(reason => h('li', { key: reason }, reason))),
1000
+ installed ? button('Manage in Plugins', () => openManagement(detail.packageName), installing) :
1001
+ button('Review installation', () => run(async () => { const value = await execute('prepare', { packageName: detail.packageName }); if (alive.current) { setDetail(value.detail); setPreview(value); } }), installing || detail.candidate.reasons.length > 0),
1002
+ preview && h('div', { style: { ...box, marginTop: 16 }, role: 'group', 'aria-label': 'Confirm plugin installation' },
1003
+ h('p', null, `Install ${preview.detail.candidate.spec} from ${preview.inspection.registry ?? 'your configured npm registry'}?`),
1004
+ h('p', null, 'The official manager will download the package and its dependencies. It will remain disabled until you enable it in Plugins. Enabling third-party plugins runs their code in the Host.'),
1005
+ button('Confirm installation', () => run(async () => { const value = await execute('install', { token: preview.token, confirm: true }); if (alive.current) { setOperation(value); setPreview(null); } }), installing),
1006
+ ' ', button('Back', () => setPreview(null), installing))),
1007
+ results && h('div', { 'aria-label': 'Plugin search results', style: { display: 'grid', gap: 12 } },
1008
+ results.items.length === 0 && h('p', null, 'No plugins match this search. Try another phrase or category.'),
1009
+ ...results.items.map(item => h('article', { key: item.packageName, style: box },
1010
+ h('h3', { style: { margin: 0 } }, item.displayName), h('code', null, `${item.packageName}@${item.latestVersion}`),
1011
+ h('p', null, item.summary), h('p', null, `${security(item)}${item.deprecated ? ' · Deprecated' : ''}`),
1012
+ button('View details', () => show(item.packageName), installing))),
1013
+ (results.nextPage || results.nextCursor) && button('Load more plugins', () => run(() => search(true)), installing)));
1014
+ }
1015
+ return { inject: ['slots', 'connection'], apply(ctx) {
1016
+ ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dscode-hub', order: 13, label: () => 'Plugin Hub',
1017
+ inject: () => ({ execute: async (action, args = {}) => {
1018
+ const response = await ctx.connection.rpc.call('/api', 'dscode-hub', { ...args, action });
1019
+ if (!response.ok) throw Error(response.error.message);
1020
+ return response.value;
1021
+ }, manage: packageName => {
1022
+ const navigation = ctx.get('pluginNavigation');
1023
+ if (!navigation) throw Error('Plugin management is unavailable. Close settings and open Plugins from the sidebar.');
1024
+ navigation.openBundle(packageName);
1025
+ } }),
1026
+ }, Hub));
1027
+ } };
1028
+ },
1029
+ });
1030
+
1031
+ globalThis.__ModuleLoader__.load({
1032
+ id: "@toddzheng024/dscode-desktop",
1033
+ factory: require => {
1034
+ const plugins = components.map(component => component.factory(require));
1035
+ return {
1036
+ inject: [...new Set(plugins.flatMap(plugin => plugin.inject ?? []))],
1037
+ apply(ctx) { for (const plugin of plugins) plugin.apply(ctx); },
1038
+ };
1039
+ },
1040
+ });
1041
+ })();