wherdr 2.4.0 → 2.6.0

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 (287) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{CPlkxpge.js → 2fsYF-LK.js} +41 -41
  3. package/.output/public/_nuxt/2fsYF-LK.js.br +0 -0
  4. package/.output/public/_nuxt/2fsYF-LK.js.gz +0 -0
  5. package/.output/public/_nuxt/{CV6ukk-M.js → 3bE7s96N.js} +1 -1
  6. package/.output/public/_nuxt/6YTlf8ot.js +1 -0
  7. package/.output/public/_nuxt/6YTlf8ot.js.br +0 -0
  8. package/.output/public/_nuxt/6YTlf8ot.js.gz +0 -0
  9. package/.output/public/_nuxt/{BYZVrreW.js → 7KozZy5Y.js} +1 -1
  10. package/.output/public/_nuxt/AgentView.BwyQIUef.css +1 -0
  11. package/.output/public/_nuxt/AgentView.BwyQIUef.css.br +0 -0
  12. package/.output/public/_nuxt/AgentView.BwyQIUef.css.gz +0 -0
  13. package/.output/public/_nuxt/{BdJIc1Xf.js → B40shTLf.js} +1 -1
  14. package/.output/public/_nuxt/{BdJIc1Xf.js.br → B40shTLf.js.br} +0 -0
  15. package/.output/public/_nuxt/B40shTLf.js.gz +0 -0
  16. package/.output/public/_nuxt/B5cSia3P.js +62 -0
  17. package/.output/public/_nuxt/B5cSia3P.js.br +0 -0
  18. package/.output/public/_nuxt/B5cSia3P.js.gz +0 -0
  19. package/.output/public/_nuxt/BK1_9FXg.js +1 -0
  20. package/.output/public/_nuxt/BK1_9FXg.js.br +0 -0
  21. package/.output/public/_nuxt/BK1_9FXg.js.gz +0 -0
  22. package/.output/public/_nuxt/{CgUSTIM1.js → Bqz3nVVB.js} +1 -1
  23. package/.output/public/_nuxt/{CgUSTIM1.js.br → Bqz3nVVB.js.br} +0 -0
  24. package/.output/public/_nuxt/Bqz3nVVB.js.gz +0 -0
  25. package/.output/public/_nuxt/{B2KlcBQ-.js → ByLPJEo6.js} +1 -1
  26. package/.output/public/_nuxt/ByLPJEo6.js.br +0 -0
  27. package/.output/public/_nuxt/ByLPJEo6.js.gz +0 -0
  28. package/.output/public/_nuxt/C8tHIeGN.js +3 -0
  29. package/.output/public/_nuxt/C8tHIeGN.js.br +0 -0
  30. package/.output/public/_nuxt/C8tHIeGN.js.gz +0 -0
  31. package/.output/public/_nuxt/CAkH_IfS.js +9 -0
  32. package/.output/public/_nuxt/CAkH_IfS.js.br +0 -0
  33. package/.output/public/_nuxt/CAkH_IfS.js.gz +0 -0
  34. package/.output/public/_nuxt/COWXF9OU.js +123 -0
  35. package/.output/public/_nuxt/COWXF9OU.js.br +0 -0
  36. package/.output/public/_nuxt/COWXF9OU.js.gz +0 -0
  37. package/.output/public/_nuxt/{BDTOz37p.js → CiG2Lovu.js} +1 -1
  38. package/.output/public/_nuxt/CiG2Lovu.js.br +0 -0
  39. package/.output/public/_nuxt/CiG2Lovu.js.gz +0 -0
  40. package/.output/public/_nuxt/CnjxEm0H.js +27 -0
  41. package/.output/public/_nuxt/CnjxEm0H.js.br +0 -0
  42. package/.output/public/_nuxt/CnjxEm0H.js.gz +0 -0
  43. package/.output/public/_nuxt/DNs2cMBZ.js +23 -0
  44. package/.output/public/_nuxt/DNs2cMBZ.js.br +0 -0
  45. package/.output/public/_nuxt/DNs2cMBZ.js.gz +0 -0
  46. package/.output/public/_nuxt/D_x7Rysx.js +31 -0
  47. package/.output/public/_nuxt/D_x7Rysx.js.br +0 -0
  48. package/.output/public/_nuxt/D_x7Rysx.js.gz +0 -0
  49. package/.output/public/_nuxt/{CLf2i_DM.js → T-qXNaMY.js} +1 -1
  50. package/.output/public/_nuxt/{board.fZYj3O-m.css → board.CpFnG_Ho.css} +1 -1
  51. package/.output/public/_nuxt/board.CpFnG_Ho.css.br +0 -0
  52. package/.output/public/_nuxt/board.CpFnG_Ho.css.gz +0 -0
  53. package/.output/public/_nuxt/builds/latest.json +1 -1
  54. package/.output/public/_nuxt/builds/meta/e2ee8d99-6a6a-4754-934f-8b7dbecfcdcb.json +1 -0
  55. package/.output/public/_nuxt/entry.C_hImCpq.css +2 -0
  56. package/.output/public/_nuxt/entry.C_hImCpq.css.br +0 -0
  57. package/.output/public/_nuxt/entry.C_hImCpq.css.gz +0 -0
  58. package/.output/public/_nuxt/{settings.Cdb21nNw.css → settings.ycbeituV.css} +1 -1
  59. package/.output/public/_nuxt/settings.ycbeituV.css.br +0 -0
  60. package/.output/public/_nuxt/settings.ycbeituV.css.gz +0 -0
  61. package/.output/public/_nuxt/useViewport.D9mmYA6A.css +1 -0
  62. package/.output/public/_nuxt/useWorktrees.BWpb3OO0.css +1 -0
  63. package/.output/public/_nuxt/useWorktrees.BWpb3OO0.css.br +0 -0
  64. package/.output/public/_nuxt/useWorktrees.BWpb3OO0.css.gz +0 -0
  65. package/.output/public/sw.js +6 -2
  66. package/.output/server/chunks/_/branding.mjs +1 -1
  67. package/.output/server/chunks/_/fileDownload.mjs +1 -1
  68. package/.output/server/chunks/_/files.mjs +26 -3
  69. package/.output/server/chunks/_/machineOrder.mjs +1 -1
  70. package/.output/server/chunks/_/onboarding.mjs +1 -1
  71. package/.output/server/chunks/_/package.mjs +1 -1
  72. package/.output/server/chunks/_/plugins.mjs +1 -1
  73. package/.output/server/chunks/_/restart.mjs +1 -1
  74. package/.output/server/chunks/_/revoke.mjs +22 -0
  75. package/.output/server/chunks/_/selfupdate.mjs +2 -1
  76. package/.output/server/chunks/_/splitShell.mjs +42 -0
  77. package/.output/server/chunks/_/updates.mjs +18 -4
  78. package/.output/server/chunks/_/uploadHashes.mjs +46 -0
  79. package/.output/server/chunks/_/worktrees.mjs +1 -1
  80. package/.output/server/chunks/_/ws.mjs +3 -3
  81. package/.output/server/chunks/nitro/nitro.mjs +15815 -15082
  82. package/.output/server/chunks/raw/check-leaks.mjs +1 -1
  83. package/.output/server/chunks/raw/ci-browser-plan.mjs +5 -0
  84. package/.output/server/chunks/raw/engine-changes.mjs +5 -0
  85. package/.output/server/chunks/raw/engine-result.mjs +7 -0
  86. package/.output/server/chunks/raw/herdr-plugin.mjs +1 -1
  87. package/.output/server/chunks/raw/prerelease.mjs +1 -1
  88. package/.output/server/chunks/raw/test-affected.mjs +1 -1
  89. package/.output/server/chunks/routes/api/_..._.mjs +5 -5
  90. package/.output/server/chunks/routes/api/agents.post.mjs +5 -5
  91. package/.output/server/chunks/routes/api/ask-tab.post.mjs +5 -5
  92. package/.output/server/chunks/routes/api/attach.post.mjs +5 -5
  93. package/.output/server/chunks/routes/api/auth/disable.post.mjs +14 -7
  94. package/.output/server/chunks/routes/api/auth/lock-all.post.mjs +12 -6
  95. package/.output/server/chunks/routes/api/auth/lock.post.mjs +5 -5
  96. package/.output/server/chunks/routes/api/auth/login/options.post.mjs +7 -7
  97. package/.output/server/chunks/routes/api/auth/login/verify.post.mjs +5 -5
  98. package/.output/server/chunks/routes/api/auth/max-session.post.mjs +5 -5
  99. package/.output/server/chunks/routes/api/auth/register/options.post.mjs +5 -5
  100. package/.output/server/chunks/routes/api/auth/register/verify.post.mjs +5 -5
  101. package/.output/server/chunks/routes/api/auth/status.get.mjs +10 -10
  102. package/.output/server/chunks/routes/api/changes.get.mjs +6 -6
  103. package/.output/server/chunks/routes/api/chat/image.get.mjs +6 -6
  104. package/.output/server/chunks/routes/api/chat.get.mjs +20 -49
  105. package/.output/server/chunks/routes/api/choose.post.mjs +6 -6
  106. package/.output/server/chunks/routes/api/claude-setup.post.mjs +6 -6
  107. package/.output/server/chunks/routes/api/claude-statusline.post.mjs +6 -6
  108. package/.output/server/chunks/routes/api/clear-queued-input.post.mjs +6 -6
  109. package/.output/server/chunks/routes/api/close/status.get.mjs +6 -6
  110. package/.output/server/chunks/routes/api/close-preview.get.mjs +62 -0
  111. package/.output/server/chunks/routes/api/close.post.mjs +5 -5
  112. package/.output/server/chunks/routes/api/codex-update.post.mjs +6 -6
  113. package/.output/server/chunks/routes/api/commands.get.mjs +6 -6
  114. package/.output/server/chunks/routes/api/config.get.mjs +6 -6
  115. package/.output/server/chunks/routes/api/dirs.get.mjs +6 -6
  116. package/.output/server/chunks/routes/api/dismiss-stopped.post.mjs +6 -6
  117. package/.output/server/chunks/routes/api/dismiss.post.mjs +6 -6
  118. package/.output/server/chunks/routes/api/effort.post.mjs +6 -6
  119. package/.output/server/chunks/routes/api/efforts.get.mjs +6 -6
  120. package/.output/server/chunks/routes/api/file.post.mjs +6 -6
  121. package/.output/server/chunks/routes/api/files/download.get.mjs +5 -5
  122. package/.output/server/chunks/routes/api/files/download.head.mjs +5 -5
  123. package/.output/server/chunks/routes/api/files/read.get.mjs +6 -6
  124. package/.output/server/chunks/routes/api/files.get.mjs +6 -6
  125. package/.output/server/chunks/routes/api/health.get.mjs +5 -5
  126. package/.output/server/chunks/routes/api/input.post.mjs +6 -6
  127. package/.output/server/chunks/routes/api/interrupt.post.mjs +6 -6
  128. package/.output/server/chunks/routes/api/isgit.get.mjs +6 -6
  129. package/.output/server/chunks/routes/api/machine/awake.get.mjs +6 -6
  130. package/.output/server/chunks/routes/api/machine/awake.post.mjs +6 -6
  131. package/.output/server/chunks/routes/api/machine/order.get.mjs +5 -5
  132. package/.output/server/chunks/routes/api/machine/order.post.mjs +5 -5
  133. package/.output/server/chunks/routes/api/machine/reconnect.post.mjs +6 -6
  134. package/.output/server/chunks/routes/api/machine/rename.post.mjs +6 -6
  135. package/.output/server/chunks/routes/api/machine/status.post.mjs +6 -6
  136. package/.output/server/chunks/routes/api/menu-left.post.mjs +6 -6
  137. package/.output/server/chunks/routes/api/menu.post.mjs +6 -6
  138. package/.output/server/chunks/routes/api/model.post.mjs +6 -6
  139. package/.output/server/chunks/routes/api/models.get.mjs +6 -6
  140. package/.output/server/chunks/routes/api/nav.post.mjs +6 -6
  141. package/.output/server/chunks/routes/api/onboarding.get.mjs +5 -5
  142. package/.output/server/chunks/routes/api/onboarding.post.mjs +5 -5
  143. package/.output/server/chunks/routes/api/phone.get.mjs +6 -6
  144. package/.output/server/chunks/routes/api/phone.post.mjs +6 -6
  145. package/.output/server/chunks/routes/api/plugins/actions.get.mjs +5 -5
  146. package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +5 -5
  147. package/.output/server/chunks/routes/api/plugins/projects.get.mjs +6 -6
  148. package/.output/server/chunks/routes/api/plugins/projects.post.mjs +6 -6
  149. package/.output/server/chunks/routes/api/prompt.post.mjs +6 -6
  150. package/.output/server/chunks/routes/api/push/devices.post.mjs +49 -0
  151. package/.output/server/chunks/routes/api/push/quiet.post.mjs +6 -6
  152. package/.output/server/chunks/routes/api/push/subscribe.post.mjs +8 -7
  153. package/.output/server/chunks/routes/api/push/test.post.mjs +6 -6
  154. package/.output/server/chunks/routes/api/push/unsubscribe.post.mjs +6 -6
  155. package/.output/server/chunks/routes/api/quotas.get.mjs +6 -6
  156. package/.output/server/chunks/routes/api/rename.post.mjs +5 -5
  157. package/.output/server/chunks/routes/api/requeue.post.mjs +6 -6
  158. package/.output/server/chunks/routes/api/restart.get.mjs +5 -5
  159. package/.output/server/chunks/routes/api/restart.post.mjs +5 -5
  160. package/.output/server/chunks/routes/api/reveal.post.mjs +6 -6
  161. package/.output/server/chunks/routes/api/screen-key.post.mjs +6 -6
  162. package/.output/server/chunks/routes/api/screen.get.mjs +6 -6
  163. package/.output/server/chunks/routes/api/search.get.mjs +6 -6
  164. package/.output/server/chunks/routes/api/seen.post.mjs +6 -6
  165. package/.output/server/chunks/routes/api/send-now.post.mjs +6 -6
  166. package/.output/server/chunks/routes/api/sessions.get.mjs +6 -6
  167. package/.output/server/chunks/routes/api/sessions.post.mjs +6 -6
  168. package/.output/server/chunks/routes/api/settings-tab.post.mjs +6 -6
  169. package/.output/server/chunks/routes/api/settings.get.mjs +6 -6
  170. package/.output/server/chunks/routes/api/settings.post.mjs +6 -6
  171. package/.output/server/chunks/routes/api/space.post.mjs +6 -6
  172. package/.output/server/chunks/routes/api/split-command.post.mjs +9 -44
  173. package/.output/server/chunks/routes/api/state.get.mjs +6 -6
  174. package/.output/server/chunks/routes/api/tasks/action.post.mjs +6 -6
  175. package/.output/server/chunks/routes/api/tasks/add.post.mjs +6 -6
  176. package/.output/server/chunks/routes/api/tasks/after.post.mjs +6 -6
  177. package/.output/server/chunks/routes/api/tasks/approve.post.mjs +6 -6
  178. package/.output/server/chunks/routes/api/tasks/archive-done.post.mjs +6 -6
  179. package/.output/server/chunks/routes/api/tasks/archived.get.mjs +6 -6
  180. package/.output/server/chunks/routes/api/tasks/autopilot.get.mjs +6 -6
  181. package/.output/server/chunks/routes/api/tasks/autopilot.post.mjs +6 -6
  182. package/.output/server/chunks/routes/api/tasks/board-settings.get.mjs +6 -6
  183. package/.output/server/chunks/routes/api/tasks/board-settings.post.mjs +6 -6
  184. package/.output/server/chunks/routes/api/tasks/coordinator.get.mjs +6 -6
  185. package/.output/server/chunks/routes/api/tasks/coordinator.post.mjs +6 -6
  186. package/.output/server/chunks/routes/api/tasks/describe.post.mjs +6 -6
  187. package/.output/server/chunks/routes/api/tasks/details.get.mjs +6 -6
  188. package/.output/server/chunks/routes/api/tasks/limit.post.mjs +6 -6
  189. package/.output/server/chunks/routes/api/tasks/links.get.mjs +7 -7
  190. package/.output/server/chunks/routes/api/tasks/models.get.mjs +6 -6
  191. package/.output/server/chunks/routes/api/tasks/pick.post.mjs +6 -6
  192. package/.output/server/chunks/routes/api/tasks/project.get.mjs +6 -6
  193. package/.output/server/chunks/routes/api/tasks/project.post.mjs +6 -6
  194. package/.output/server/chunks/routes/api/tasks/question.post.mjs +6 -6
  195. package/.output/server/chunks/routes/api/tasks/scheduled.get.mjs +6 -6
  196. package/.output/server/chunks/routes/api/tasks/settings.get.mjs +6 -6
  197. package/.output/server/chunks/routes/api/tasks/start.get.mjs +6 -6
  198. package/.output/server/chunks/routes/api/tasks/tell.post.mjs +6 -6
  199. package/.output/server/chunks/routes/api/tasks/transcript-image.get.mjs +47 -0
  200. package/.output/server/chunks/routes/api/tasks/transcript.get.mjs +48 -0
  201. package/.output/server/chunks/routes/api/tasks.get.mjs +6 -6
  202. package/.output/server/chunks/routes/api/unqueue.post.mjs +6 -6
  203. package/.output/server/chunks/routes/api/update/status.get.mjs +5 -5
  204. package/.output/server/chunks/routes/api/update.get.mjs +5 -5
  205. package/.output/server/chunks/routes/api/update.post.mjs +5 -5
  206. package/.output/server/chunks/routes/api/upload.post.mjs +6 -6
  207. package/.output/server/chunks/routes/api/worktrees/remove.post.mjs +5 -5
  208. package/.output/server/chunks/routes/api/worktrees.get.mjs +5 -5
  209. package/.output/server/chunks/routes/favicon.ico.get.mjs +5 -5
  210. package/.output/server/chunks/routes/icons/_name_.get.mjs +6 -6
  211. package/.output/server/chunks/routes/renderer.mjs +7 -7
  212. package/.output/server/chunks/routes/uploads/_...name_.get.mjs +6 -6
  213. package/.output/server/chunks/routes/ws/_..._.mjs +6 -6
  214. package/.output/server/chunks/routes/ws/events.mjs +6 -6
  215. package/.output/server/chunks/routes/ws/term.mjs +6 -6
  216. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  217. package/.output/server/index.mjs +6 -6
  218. package/.output/server/package.json +1 -1
  219. package/README.md +1 -1
  220. package/bin/lib/tailnet.d.mts +7 -4
  221. package/bin/lib/tailnet.mjs +76 -18
  222. package/bin/lib/updater.mjs +6 -0
  223. package/docs/README.md +4 -0
  224. package/docs/RELEASING.md +27 -5
  225. package/docs/configuration.md +3 -4
  226. package/docs/conversation.md +11 -3
  227. package/docs/design.md +1 -1
  228. package/docs/devices.md +28 -0
  229. package/docs/home.md +29 -0
  230. package/docs/installation.md +14 -5
  231. package/docs/networks.md +9 -4
  232. package/docs/notifications.md +7 -0
  233. package/docs/plugins.md +1 -1
  234. package/docs/quick-start.md +7 -6
  235. package/docs/quotas.md +10 -1
  236. package/docs/release-notes-2.5.0.md +25 -0
  237. package/docs/release-notes-2.6.0.md +29 -0
  238. package/docs/security.md +61 -11
  239. package/docs/troubleshooting.md +4 -0
  240. package/package.json +1 -1
  241. package/skills/wherdr/SKILL.md +21 -0
  242. package/.output/public/_nuxt/AgentView.B12D6ccj.css +0 -1
  243. package/.output/public/_nuxt/AgentView.B12D6ccj.css.br +0 -0
  244. package/.output/public/_nuxt/AgentView.B12D6ccj.css.gz +0 -0
  245. package/.output/public/_nuxt/B2KlcBQ-.js.br +0 -0
  246. package/.output/public/_nuxt/B2KlcBQ-.js.gz +0 -0
  247. package/.output/public/_nuxt/BDTOz37p.js.br +0 -0
  248. package/.output/public/_nuxt/BDTOz37p.js.gz +0 -0
  249. package/.output/public/_nuxt/BGD5AamG.js +0 -25
  250. package/.output/public/_nuxt/BGD5AamG.js.br +0 -0
  251. package/.output/public/_nuxt/BGD5AamG.js.gz +0 -0
  252. package/.output/public/_nuxt/BdJIc1Xf.js.gz +0 -0
  253. package/.output/public/_nuxt/BxlzUs1i.js +0 -23
  254. package/.output/public/_nuxt/BxlzUs1i.js.br +0 -0
  255. package/.output/public/_nuxt/BxlzUs1i.js.gz +0 -0
  256. package/.output/public/_nuxt/C-BDzEtI.js +0 -3
  257. package/.output/public/_nuxt/C-BDzEtI.js.br +0 -0
  258. package/.output/public/_nuxt/C-BDzEtI.js.gz +0 -0
  259. package/.output/public/_nuxt/CO4sDeSt.js +0 -62
  260. package/.output/public/_nuxt/CO4sDeSt.js.br +0 -0
  261. package/.output/public/_nuxt/CO4sDeSt.js.gz +0 -0
  262. package/.output/public/_nuxt/CPlkxpge.js.br +0 -0
  263. package/.output/public/_nuxt/CPlkxpge.js.gz +0 -0
  264. package/.output/public/_nuxt/CTPNZj4T.js +0 -9
  265. package/.output/public/_nuxt/CTPNZj4T.js.br +0 -0
  266. package/.output/public/_nuxt/CTPNZj4T.js.gz +0 -0
  267. package/.output/public/_nuxt/CgUSTIM1.js.gz +0 -0
  268. package/.output/public/_nuxt/CwurhZnh.js +0 -123
  269. package/.output/public/_nuxt/CwurhZnh.js.br +0 -0
  270. package/.output/public/_nuxt/CwurhZnh.js.gz +0 -0
  271. package/.output/public/_nuxt/D7fCPncR.js +0 -1
  272. package/.output/public/_nuxt/D7fCPncR.js.br +0 -0
  273. package/.output/public/_nuxt/D7fCPncR.js.gz +0 -0
  274. package/.output/public/_nuxt/DFe-cDm2.js +0 -1
  275. package/.output/public/_nuxt/DFe-cDm2.js.br +0 -0
  276. package/.output/public/_nuxt/DFe-cDm2.js.gz +0 -0
  277. package/.output/public/_nuxt/Dpe9TNcA.js +0 -31
  278. package/.output/public/_nuxt/Dpe9TNcA.js.br +0 -0
  279. package/.output/public/_nuxt/Dpe9TNcA.js.gz +0 -0
  280. package/.output/public/_nuxt/board.fZYj3O-m.css.br +0 -0
  281. package/.output/public/_nuxt/board.fZYj3O-m.css.gz +0 -0
  282. package/.output/public/_nuxt/builds/meta/75cc4ea7-3027-463a-aa3e-29e474ed7173.json +0 -1
  283. package/.output/public/_nuxt/entry.KNdllmoL.css +0 -2
  284. package/.output/public/_nuxt/entry.KNdllmoL.css.br +0 -0
  285. package/.output/public/_nuxt/entry.KNdllmoL.css.gz +0 -0
  286. package/.output/public/_nuxt/settings.Cdb21nNw.css.br +0 -0
  287. package/.output/public/_nuxt/settings.Cdb21nNw.css.gz +0 -0
@@ -3,7 +3,7 @@
3
3
  [Documentation](README.md) · [Project](../README.md)
4
4
 
5
5
  - **Themes**: wherdr Titanium (default), herdr.dev and Herdr's built-in themes, or follow your Herdr theme.
6
- **English and French** interface.
6
+ English interface.
7
7
 
8
8
  ## Configuration
9
9
 
@@ -20,7 +20,7 @@ native service (read at each start: `wherdr restart`, or **R** in the panel).
20
20
  | `TAILNET_REFRESH_MS` | `30000` | How often wherdr reads again what `tailscale serve` publishes for its port. |
21
21
  | `HERDR_WEB_ALLOWED_HOSTS` | *(empty)* | Additional hostnames or IP addresses allowed to open the app, comma-separated. |
22
22
  | `PASSKEY_USER` | `wherdr` | User name stored with the passkeys. |
23
- | `DATA_DIR` | `./data` | VAPID keys, push subscriptions, passkeys, recent folders, and `settings.json`: the display settings that follow you across devices (theme, machine bar and groups, home list, Board and file browser preferences). Screen-dependent ones (content width, sidebar width, terminal size and rendering), the language, the last tab or filter and drafts stay in each browser. |
23
+ | `DATA_DIR` | `./data` | VAPID keys, push subscriptions, passkeys, recent folders, and `settings.json`: the display settings that follow you across devices (theme, machine bar and groups, home list, Board and file browser preferences). Screen-dependent ones (content width, sidebar width, terminal size and rendering), the last tab or filter and drafts stay in each browser. |
24
24
  | `HERDR_BIN` | `~/.local/bin/herdr`, else `herdr` in `PATH` | Herdr binary. |
25
25
  | `HERDR_WEB_SESSION` | *(default session)* | Named Herdr session to drive. Not `HERDR_SESSION`, which Herdr reads itself. |
26
26
  | `HERDR_PROJECTS_DATA_DIR` | `~/.herdr-projects/data` | herdr-projects' data folder, where wherdr reads the `PATH` it recorded ([Agent PATH](installation.md#agent-path)). |
@@ -34,8 +34,7 @@ native service (read at each start: `wherdr restart`, or **R** in the panel).
34
34
  | `TZ` | `UTC` | Time zone. |
35
35
 
36
36
  In the app, **Settings** holds display preferences such as theme, typing effect and what the
37
- home screen shows; these follow you across devices through `settings.json`. Language,
38
- notifications, terminal text size and renderer remain device-specific. The passkey lock is
37
+ home screen shows; these follow you across devices through `settings.json`. Notifications, terminal text size and renderer remain device-specific. The passkey lock is
39
38
  managed under Settings › Security.
40
39
 
41
40
  ## Paste diagnostics
@@ -8,6 +8,7 @@
8
8
  A command the agent proposes (shell code block, or inline `! command`) can be copied without
9
9
  its prompt, or run in the agent's shell mode (`!`, Claude Code, Codex and omp) after you
10
10
  confirm the full command.
11
+ - **SVG diagrams**: complete code fences labelled `svg` render as isolated, theme-aware images. **Source / Preview**, beside **Copy** in the card header, switches the same card between the diagram and its original markup; **Copy** always copies that markup. Click or use Enter/Space on the image for the existing full-screen viewer. Static shapes, text and local definitions are supported, up to 150,000 source characters, 2,000 source elements and 10,000 expanded reference instances. Incomplete, unsupported or unsafe diagrams stay readable code. Scripts, foreign HTML, animation and external resources are rejected; raw inline SVG is not inserted into the page. Other fence languages and file-explorer document fences remain code.
11
12
  - **Interactive agent screens**: in a conversation, an uncertain menu or panel opens the
12
13
  pane's real terminal in a modal window, with arrows, Enter, Escape, Tab, digits and y/n.
13
14
  The rest of the app waits behind a scrim (the window fills the screen on a phone); in a
@@ -39,6 +40,14 @@
39
40
  pane and opens a new one for the same agent (the same conversation, or the same herdr-projects
40
41
  coordinator or task, as after `coordinator restart`), the view moves to the new pane and the
41
42
  draft comes along. A pane closed for good keeps it under its banner, with **Copy draft**.
43
+ - **omp context**: beside the model, plain text shows a whole-number percentage and
44
+ estimated tokens in uppercase `K`. Select it for the window, original percentage,
45
+ estimate details and terminal status; no separate status row is added. Estimates use
46
+ omp's rounded percentage, not cumulative token consumption. Unknown readings stay hidden.
47
+ Working panes refresh even when not selected; unviewed resting panes retain their reading.
48
+ Failed or gauge-free reads are marked as last known. On touch screens, context details
49
+ appear with the model picker when the composer expands. Escape closes details without
50
+ stopping the agent.
42
51
  - **Model and effort pickers** for Claude Code, Codex and omp (omp: its
43
52
  "Switch Model" selector, `/switch` or `/model`, and its thinking level, changed with its own
44
53
  ⇧⇥ cycle). Changes apply **to the current session only**; wherdr never changes your default
@@ -100,7 +109,7 @@ same lines as text, a fallback if the tokens misbehave. The message sent is the
100
109
 
101
110
  A **closed question**, one that yes or no answers in full, also gets **Yes** and **No** next to
102
111
  its `↳` (at the end of its row in the list style). A tap refers to the question and writes the
103
- answer under it, in the language of the interface, exactly as Reply and typing would; nothing is sent, so several questions can be
112
+ answer under it (`Yes` or `No`), exactly as Reply and typing would; nothing is sent, so several questions can be
104
113
  answered in a row before sending. On a computer a click hands the field the focus, the caret
105
114
  right after the answer, so Enter sends it at once; the keyboard stays down on a phone, and a
106
115
  button pressed with the keyboard keeps the focus. Tapping the other answer
@@ -115,8 +124,7 @@ them, gets a panel right under it: **OK to all** refers to the whole table and w
115
124
  under it (a second tap takes it back), and each row has **Yes**, **No** and `↳`, which refer to
116
125
  the row by its number and write `Yes` or `No` under it, or nothing for an answer in words; a row
117
126
  that asks an open question has `↳` alone. Every answer names the message and the row, even for
118
- the last message: the agent may write another one before you send. In French: `OK tout`, `Oui`,
119
- `Non`. Here too everything is composed in the field and you send; on a computer the field takes the focus
127
+ the last message: the agent may write another one before you send. Here too everything is composed in the field and you send; on a computer the field takes the focus
120
128
  after a click, so Enter sends. A table qualifies when it has a
121
129
  number column (a header `#`, `N°`, `No`, `Num`, `ID` or `Q` over short numbers, each once) and a
122
130
  question column, known by its header (`Question`, `Decision`, `Choix`…), by cells that all end
package/docs/design.md CHANGED
@@ -97,7 +97,7 @@ implementations.
97
97
  ## Text and layout checks
98
98
 
99
99
  Follow [Contributing: Conventions](../CONTRIBUTING.md#conventions) for English
100
- keys, mandatory French translations and `t()` / `tl()` usage. Do not duplicate
100
+ only interface text and `t()` / `tl()` usage; French translations are optional. Do not duplicate
101
101
  those translation rules here. Examples: the translated Send label in
102
102
  [Composer.vue](../app/components/Composer.vue) and Back in
103
103
  [AgentView.vue](../app/components/AgentView.vue).
package/docs/devices.md CHANGED
@@ -38,6 +38,30 @@
38
38
 
39
39
  ### On a phone
40
40
 
41
+ - **Compact agent header**: a 44 px bar holds Back, one two-line identity, Terminal and Options.
42
+ The identity shows the pane name, then its square state indicator, state label and agent kind.
43
+ Tap it (always available, even for a lone pane) for the machine/space context and everything
44
+ that manages panes and tabs: switching (listed only when there are several; a lone pane shows its
45
+ full name and state instead), then the current
46
+ pane (rename, split, move, swap, copy ID, then **Close this conversation** or **Close this
47
+ terminal**: only that pane, from either view) and the current tab (rename, new tab, tab
48
+ overview). **Close entire space…** stands apart, and only when the space holds other panes: a
49
+ lone pane has a single close, whose confirmation says the space closes too. A tab closes from its
50
+ overview, which says how many panes go. Every close confirmation names what stops, how many tabs
51
+ and panes go and what stays open; if the structure changes while it is open and the close would
52
+ now take more, the updated confirmation is shown again. Long names ellipsize; multiple panes
53
+ show their position, never 1/1.
54
+ Options keeps the conversation, the agent and its tools: Terminal/Conversation, search,
55
+ notifications, changes, files, plugin actions, take control, restart, reconnect and settings.
56
+ - **Terminal function keys** start closed. The square chevron button above Send shows or hides
57
+ one horizontally scrolling row in both Conversation and Terminal. It holds Ctrl, Esc, Tab,
58
+ arrows, Enter, Ctrl+C, Backspace and the remaining modifiers and function keys. The phone-keyboard
59
+ toggle comes first, and Home, End, PgUp, PgDn, Insert and Delete are available in both modes.
60
+ In Conversation the keyboard toggle focuses the message field, and navigation uses the
61
+ named-key API without opening a terminal connection.
62
+ Select Shift then Tab for Shift+Tab. Closing the bar clears any armed modifier.
63
+ The input stays on one row at rest and expands while editing. Desktop and tablet layouts
64
+ keep their existing controls.
41
65
  - **Installable app (PWA)** on iPhone and Android, with safe-area and keyboard handling.
42
66
  - **Web Push notifications** when an agent finishes or needs you (with its question), and for
43
67
  `herdr notification show` (plugins, scripts).
@@ -45,6 +69,10 @@
45
69
  - **Split tabs on a small screen**: a plan of the tab's panes in Herdr's proportions, then each
46
70
  pane full screen with a mini-map and swipe to its neighbours.
47
71
  - **Terminal key bar** (Esc, Enter, arrows, Shift+Tab, Ctrl+C…) and touch scrolling.
72
+ On iPhone and iPad a tap on the terminal does not open the on-screen keyboard: type in the
73
+ message field, or tap the key bar's keyboard button to type in the terminal.
74
+ Terminal links back to this app stay in the current window on a phone; external
75
+ links still open separately.
48
76
  - **Offline reading** of the last known state and of recently opened conversations.
49
77
 
50
78
  ### On an iPad
package/docs/home.md CHANGED
@@ -23,6 +23,9 @@
23
23
  the first 2.0 load; turning it on again is kept. Other agents
24
24
  also use dense rows, with a last-answer preview in normal mode; waiting agents keep their
25
25
  questions and one-tap answers in both modes.
26
+ omp cards show context usage as a thin bottom-border line, without changing row height or
27
+ click targets. Open the conversation for percentage and estimated-token details. Resting
28
+ panes retain their last reading; unknown readings stay hidden.
26
29
  With [herdr-projects](board.md#works-great-with-herdr-projects), the board's spaces follow Herdr's sidebar:
27
30
  the Coordinators on top, then one group per repo, headed by its project coordinator (the repo's
28
31
  name, the wherdr mark, **Coordinator** and its state), a bar that folds the group and sums up its
@@ -59,3 +62,29 @@ cards remain the default. Board tasks and repository headers keep their existing
59
62
  grouping, and **Show terminals** still controls plain terminal rows. The setting
60
63
  follows you across devices. Workspace drag reordering is disabled in this view;
61
64
  switch it off to reorder spaces.
65
+
66
+ ## What the counters count
67
+
68
+ The three counters at the top of the home (**Your turn**, **Working**, **Ready**) count the
69
+ rows of the list under them: **one row, one count**, in the state that row shows. The counters,
70
+ the rows' state (their badge, accent and place in their group), the repo bar and the machine
71
+ bar's numbers all come from the same functions (`taskStat`, `memberStat` and `countedPanes` in
72
+ `shared/boardLinks.ts`, `homeCounts` in `shared/homeCounts.ts`).
73
+
74
+ - **A Board task** counts once, by its state on the Board, whatever helpers (reviewer, planner,
75
+ attempts) it has open and in however many spaces. **Needs you** and **Ready to approve** are
76
+ **Your turn**: you must act. A task ready to approve keeps its *Ready to approve* label, but its
77
+ badge and row accent use the Your turn colour, and it sits with the tasks waiting for you. A
78
+ task that waits for you still counts once its agent has exited or its space was closed.
79
+ **Working** is the Board's working group (reviews and checks included); **To do** and
80
+ **Done** are Ready.
81
+ - **A coordinator** (the Board's, a machine's or a project's) is a row with its own state square:
82
+ it counts by that state, like any agent row. The repo bar's summary (`3 TASKS · 1 YOUR TURN`)
83
+ is the one exception: it counts the repo's tasks only, since its coordinator's row and state
84
+ sit right above it.
85
+ - **A row standing for several terminals** (a space with several agents, its card showing their
86
+ number) counts once, in the state the row shows: its most urgent terminal. With Settings ›
87
+ Appearance › Home screen › **Individual agents** on, each agent is a row, and a count. A
88
+ space shared by several tasks, or by a coordinator and other agents, is split into one row per
89
+ task or agent, and counts the same way.
90
+ - Each counter's number uses its own state colour, that of its legend square.
@@ -58,7 +58,7 @@ The script ([read it first](https://wherdr.dev/install), source in
58
58
  Mac with Homebrew, otherwise the official Node.js 22 build from nodejs.org, its checksum
59
59
  checked, unpacked in `~/wherdr/node` (used by wherdr only: your `PATH` is not changed). With
60
60
  musl libc (Alpine) it gives the install links instead;
61
- 4. runs `herdr plugin install afloury/wherdr --yes`: the [Herdr plugin](plugins.md#install-as-a-herdr-plugin)
61
+ 4. runs `herdr plugin install afloury/wherdr --ref 2.x --yes`: the [Herdr plugin](plugins.md#install-as-a-herdr-plugin)
62
62
  downloads the GitHub release package of its version (checksum checked, no wait for npm),
63
63
  installs it in `~/wherdr/app`, starts it on `http://localhost:7683`, opens the setup guide in
64
64
  your browser (first install) and binds **prefix+i** to its panel;
@@ -87,7 +87,7 @@ Settings, as environment variables (`curl -fsSL https://wherdr.dev/install | WHE
87
87
  | `WHERDR_SERVICE` | — | `off`: no login service; wherdr then starts with Herdr only (the plugin's startup hook). |
88
88
  | `WHERDR_YES` | — | `1` answers yes to every question (non-interactive use), installing Node.js included. |
89
89
  | `WHERDR_NO_BROWSER` | — | `1` never opens a browser. |
90
- | `WHERDR_REF` | main | Git tag or branch of the plugin. |
90
+ | `WHERDR_REF` | `2.x` | Git tag or branch of the plugin (`2.x` is the wherdr 2 release branch). |
91
91
  | `WHERDR_PLUGIN` | `afloury/wherdr` | A local checkout to link and build instead (development, tests). |
92
92
 
93
93
  Update: run the command again (it offers the update), **U** in the wherdr panel, or **Update** in
@@ -134,6 +134,13 @@ home screen and in Settings › About, with the release notes and the exact comm
134
134
  Hide it until the next version with ×. Set `WHERDR_UPDATE_CHECK=off` to disable the check. The
135
135
  installed version is shown in Settings › About.
136
136
 
137
+ Updates stay within the running major version: wherdr 2 only offers 2.x releases, and the
138
+ one-tap update refuses to install a new major. When wherdr 3 is published, the home screen shows
139
+ a **“wherdr 3 is out”** card instead of the banner, with its release notes, its migration guide and a
140
+ Copy button for the command that installs wherdr 3 **beside** wherdr 2 (it only prints its plan
141
+ until you add `--yes`). wherdr 2 keeps running with its data, and 2.x fixes are still offered in
142
+ Settings › About.
143
+
137
144
  GitHub release packages (including the bundled agent skill) and Homebrew
138
145
  updates are published without waiting for npm moderation. Native plugin installs and updates
139
146
  use the GitHub `.tgz` and verify its SHA-256 checksum before extraction. A failed one-tap
@@ -142,7 +149,9 @@ GitHub first, then the prebuilt npm package of the exact same version (with its
142
149
  This also keeps older 1.3.x releases without GitHub archives installable with Node.js or Bun.
143
150
  Packages predating the bundled skill retain the plugin checkout's commands and skill, so
144
151
  skill refresh, installation and uninstallation remain available.
145
- The installer fetches `main` explicitly unless you set `WHERDR_REF`.
152
+ The installer fetches the `2.x` branch unless you set `WHERDR_REF`. The plugin update command
153
+ adds `--ref 2.x`, and a git checkout follows `2.x` (`git checkout 2.x`) before pulling: `main`
154
+ holds wherdr 3.
146
155
 
147
156
  The SHA-256 checksum detects damaged downloads. It is served beside the archive, so someone
148
157
  who can replace both files can make them match; it does not prove the publisher’s identity
@@ -175,10 +184,10 @@ and the app says so. Every step is logged in `~/wherdr/update.log`.
175
184
  | Setup | Update command (in the wherdr folder) |
176
185
  | --- | --- |
177
186
  | The one command | Run `curl -fsSL https://wherdr.dev/install \| sh` again: it offers the update (GitHub release package) |
178
- | Herdr plugin, Node.js | **U** in the **wherdr** panel, or `herdr plugin install afloury/wherdr --yes` (wherdr restarts on the new version) |
187
+ | Herdr plugin, Node.js | **U** in the **wherdr** panel, or `herdr plugin install afloury/wherdr --ref 2.x --yes` (wherdr restarts on the new version) |
179
188
  | `wherdr` command (npm) | `npm install -g wherdr@latest && wherdr restart` (npx: stop it, then `npx wherdr@latest`) |
180
189
  | `wherdr` command (Homebrew) | `brew upgrade wherdr`, then `brew services restart wherdr` |
181
- | From source | `git pull && npm ci && npm run build`, then restart wherdr |
190
+ | From source | `git checkout 2.x && git pull && npm ci && npm run build`, then restart wherdr |
182
191
 
183
192
  Update notices also show the number of newer stable releases and up to four
184
193
  additional version numbers besides the latest. This measures published releases,
package/docs/networks.md CHANGED
@@ -26,7 +26,10 @@ tailscale serve --bg --https=7683 http://127.0.0.1:7683
26
26
  ```
27
27
 
28
28
  Tailscale Serve listens on HTTPS port 7683 of your tailnet name, with a valid certificate, and
29
- forwards to wherdr's local HTTP port. Only devices of your tailnet can reach it.
29
+ forwards to wherdr's local HTTP port. Only devices of your tailnet can reach it, and wherdr
30
+ answers only **your own** devices among them: another user's device in a team tailnet, a node
31
+ shared in from another tailnet or a tagged node is refused until you set up a passkey (see
32
+ [Your own devices only](security.md#own-devices)).
30
33
 
31
34
  **wherdr enables that address by itself**, on a server without a screen too: at startup, every
32
35
  30 seconds, and when an address it does not know yet is opened, it reads what `tailscale serve`
@@ -59,9 +62,11 @@ desktop layout can replace the Herdr terminal client for daily work.
59
62
  **5. Passkey lock** (optional): Settings → Security → **Enable passkey lock**. The
60
63
  first passkey asks for a bootstrap token, printed in the server log (`~/wherdr/wherdr.log`,
61
64
  **L** in the wherdr panel); it changes on each restart and is only needed for that first passkey. Add a passkey on each of
62
- your devices after that (see [New device](security.md#new-device)). Without one, every device of your
63
- tailnet can control your agents: enough when the tailnet is yours alone, a passkey adds a lock
64
- on each device when someone else shares the tailnet or your devices.
65
+ your devices after that (see [New device](security.md#new-device)). Without one, wherdr
66
+ answers this computer and your own tailnet devices; devices of other people on your tailnet are
67
+ always refused (with a passkey, they reach the lock screen). A passkey adds an automatic lock on
68
+ each device if you want one, for example on a computer other people log in to (see
69
+ [Shared computers](security.md#shared-computers)).
65
70
 
66
71
  **6. More machines** (optional): register your other computers in Herdr over SSH
67
72
  (`herdr machine add`, see [Several machines (SSH)](machines.md#several-machines-ssh)). Their agents show
@@ -52,6 +52,13 @@ The screen reads top to bottom:
52
52
  others). Each says how it differs from the default; removing one goes back to it.
53
53
  4. **This device's notifications** only asks this browser or phone for permission to
54
54
  show push notifications, and shows whether it is allowed, blocked or not asked yet.
55
+ Below it, every registered device is listed with the result of the last push sent to it:
56
+ delivered, expired (404/410: the browser dropped the subscription), rejected (401/403),
57
+ push service unreachable, or failed with its HTTP status. Each device keeps its own result,
58
+ so one device's success never hides another's failure. An expired device is no longer
59
+ tried; **Remove** forgets it (otherwise it goes away on its own after 30 days). Only the
60
+ time, the class and the status code are stored in `data/push.json`, never the push
61
+ service's response.
55
62
 
56
63
  A coordinator's exception is kept by its identity (machine, Herdr session, level and
57
64
  repo), not its pane: it still applies after `coordinator new` or a restart.
package/docs/plugins.md CHANGED
@@ -7,7 +7,7 @@
7
7
  This is what [the one command](installation.md#the-one-command) installs; you can also run it yourself, on Linux
8
8
  and macOS, with Herdr ≥ 0.9.1 (no checks for Herdr or Node.js first). In three lines:
9
9
 
10
- 1. Run `herdr plugin install afloury/wherdr`: it installs wherdr, starts it
10
+ 1. Run `herdr plugin install afloury/wherdr --ref 2.x`: it installs wherdr, starts it
11
11
  (`✓ wherdr is running → http://localhost:7683`) and, at the first install on a computer with a
12
12
  screen, opens the setup guide in your browser (`open` on macOS, `xdg-open` on Linux with a
13
13
  display; a headless server, or `WHERDR_NO_BROWSER=1` in the environment of the install, just
@@ -33,20 +33,21 @@ address and the phone steps instead. See [The one command](installation.md#the-o
33
33
 
34
34
  **wherdr 2.0 runs only natively**, as your user, so it can act on the
35
35
  [herdr-projects Board](board.md#works-great-with-herdr-projects) (Coordinator, reply, start, stop,
36
- approve). Other ways, after it: `herdr plugin install afloury/wherdr` (the same plugin, without
36
+ approve). Other ways, after it: `herdr plugin install afloury/wherdr --ref 2.x` (the same plugin, without
37
37
  the checks), and [the `wherdr` command](command.md#the-wherdr-command) from npm or Homebrew (npm can hold a
38
38
  new release for up to two days; the installer gets it from GitHub at once).
39
39
 
40
40
  At first launch, wherdr opens a **setup guide**: the Herdr agents it found, your phone
41
- (Tailscale), then the passkey lock and notifications. Skip it any time; finishing or skipping is
41
+ (Tailscale), then notifications. Skip it any time; finishing or skipping is
42
42
  saved on the server (`data/onboarding.json`), so your other devices do not show it again. Reopen
43
43
  it from Settings › About › **Setup guide**. On a phone that already opens wherdr through its
44
44
  tailnet address, the phone step becomes Add to Home Screen.
45
45
 
46
- **Passkeys are tied to the address they are created on.** Once wherdr's tailnet address is
47
- published and answers, the guide opened on `localhost` shows **Continue on
48
- <machine>.<tailnet>.ts.net**, which reopens it at the same step on that address, and its Security
49
- step (like Settings › Security) sends the passkey lock there. Nothing redirects on its own:
46
+ Once wherdr's tailnet address is published and answers, the guide opened on `localhost` shows
47
+ **Continue on <machine>.<tailnet>.ts.net**, which reopens it at the same step on that address
48
+ (notifications and the installed app need it). The guide does not propose a passkey: the
49
+ optional passkey lock is in Settings › Security, which also sends it to that address, since
50
+ **passkeys are tied to the address they are created on**. Nothing redirects on its own:
50
51
  `localhost` stays valid on the computer. The installer, the Herdr plugin and `wherdr open` open
51
52
  that address too when it is already published.
52
53
 
package/docs/quotas.md CHANGED
@@ -30,7 +30,16 @@ week, with reset times), for every online machine.
30
30
 
31
31
  - **Codex**: nothing to do, Codex writes its limits in its session files. Machines on different
32
32
  Codex accounts get one block each (told apart by a short hash of the account ID found in the
33
- session files; `~/.codex/auth.json` is never read).
33
+ session files; `~/.codex/auth.json` is never read). Codex only writes that account ID in
34
+ conversations created with a ChatGPT login by a recent Codex version. A reading without it
35
+ joins another machine's reading only when both show the same 5-hour window and the same
36
+ weekly window (resets within a minute, week already started), a sign of one account: the
37
+ machines then share one Codex row at the top, like Claude. Otherwise it stays under its own
38
+ machine and is never merged with another conversation or machine. A new conversation identifies an account switch even before it
39
+ logs limits; until a reading for that account is available, no Codex gauge is shown. Failed reads do not
40
+ reuse an older machine snapshot, and remembered weekly limits cannot cross account
41
+ identities. Without a known fingerprint, remembered weekly corrections are not reused,
42
+ even on the same machine. Session files are read from `~/.codex/sessions`.
34
43
  - **Claude Code** only gives them to its status line. On each machine, install wherdr's
35
44
  invisible status line (it displays nothing, and chains to your existing status line if you
36
45
  have one):
@@ -0,0 +1,25 @@
1
+ ## New
2
+
3
+ You can choose the order of the phone key bar in Settings › Terminal, with a preview and a reset to the default. The order is saved on the device and shared by Conversation and Terminal. Thanks to [@followcat](https://github.com/followcat) for the idea.
4
+
5
+ Notification settings show each registered device's last push delivery result and let you remove expired registrations.
6
+
7
+ ## Improved
8
+
9
+ The interface is always in English, including on browsers previously set to French. Messages written by you and your agents keep their original language. The phone header chevron has a full-size button box, and desktop view switching uses a single action button.
10
+
11
+ The agent skill explains how to share verified links that users can open. Focused unit and component coverage replaces repeated browser cases, CI shards phone tests, and full develop coverage runs nightly. Develop prereleases are built only from commits that passed CI.
12
+
13
+ ## Fixed
14
+
15
+ After a server restart, machines recover independently and existing panes remain visible until their machine has been read or confirmed offline. Codex quota readings stay with the correct account, and Home WORKING counters follow Board groups without counting remote tasks twice.
16
+
17
+ On iPhone and iPad, tapping the terminal keeps the keyboard closed; the message field and keyboard button still open it. Phone navigation returns from tasks to the Board, wherdr links reuse the existing window, and closing a terminal no longer falsely reports that its process stopped.
18
+
19
+ Conversation images can load from other machines and ports on the same tailnet, with an explanation and an open link when loading fails. Codex photo placeholders no longer appear as message text, older explicitly resumed conversations can be found, and current Codex sessions no longer show a false restart-to-update notice. omp provider errors now appear in the conversation, and coordinators can be found by conversation after their pane closes.
20
+
21
+ ## Update
22
+
23
+ No migration is required from 2.4.0. Node.js 22 or newer and Herdr 0.9.1 or newer remain required. The Board continues to support herdr-projects 1.2.x, not Shepherdr 2.x.
24
+
25
+ [Full changelog](https://github.com/afloury/wherdr/blob/v2.5.0/CHANGELOG.md)
@@ -0,0 +1,29 @@
1
+ ## New
2
+
3
+ 2.6 is the last new version of wherdr 2. It gets you ready for wherdr 3, which installs **beside** wherdr 2 rather than replacing it.
4
+
5
+ - **Updates stay on wherdr 2.** The update check only offers 2.x versions. When wherdr 3 is published, the Update button never installs it, and wherdr 2 fixes (2.6.1, 2.6.2…) are still offered.
6
+ - **A "wherdr 3 is out" card.** Once wherdr 3 is released, Home shows a card instead of the update banner, with links to its release notes and its migration guide, and a Copy button for the command that installs wherdr 3 beside this version. That command shows its plan first and changes nothing until you add `--yes`. wherdr 2 keeps running with its data, so you can compare both and go back at any time.
7
+ - **Installs follow the 2.x branch.** The Herdr plugin now updates from the `2.x` branch (**U** in the panel, and the update command, add `--ref 2.x`), and the curl installer installs `2.x` by default (`WHERDR_REF` still picks another branch or tag). If you run wherdr from a git checkout, switch it to the release branch once with `git checkout 2.x` before pulling; the update banner shows that command.
8
+ - **SVG diagrams in conversations.** A static `svg` code block shows as a theme-aware preview you can enlarge or copy; unsafe or incomplete content stays plain code. Thanks to [@guileen](https://github.com/guileen).
9
+ - **omp context at a glance.** Agent rows and the composer show how much of omp's context is used.
10
+
11
+ ## Improved
12
+
13
+ - **Tighter security on the tailnet.** Without a passkey, only your own Tailscale devices can open wherdr; another user's device in a team tailnet, or a shared-in or tagged node, is refused until a passkey is set. A locked device learns nothing before unlocking (no passkey names, machine name or server clock). **Lock all devices** now invalidates every session, push subscription and open connection at once.
14
+ - **Lighter conversation reads.** While an agent writes, wherdr reads only what was added to its transcript.
15
+
16
+ ## Fixed
17
+
18
+ - A finished task's Agent tab shows the agent's conversation instead of the shell left behind.
19
+ - Conversations reopen at their reading position, are not redrawn when nothing changed, and keep a sent message visible while reads fail for a while.
20
+ - A repeated Enter (from a dictation app, for example) no longer sends the same message twice, and a single stray Escape no longer stops the agent.
21
+ - Home counters and badges agree on task states, the Board grouping appears right after a reload, and two machines on one Codex account share one quota row.
22
+ - Reconnecting after a suspend or a redeploy no longer hangs on a stalled read.
23
+ - On macOS, when wherdr runs as a service, Settings › Phone no longer says *not connected* while Tailscale runs, and Web Push works.
24
+
25
+ ## Update
26
+
27
+ No migration is required from 2.5.0. Node.js 22 or newer and Herdr 0.9.1 or newer remain required. Git checkouts: run `git checkout 2.x` once before your next `git pull`.
28
+
29
+ [Full changelog](https://github.com/afloury/wherdr/blob/v2.6.0/CHANGELOG.md)
package/docs/security.md CHANGED
@@ -10,7 +10,7 @@ wherdr drives Herdr, so **it can run anything as your user**.
10
10
  network** (Tailscale or [equivalent](networks.md#other-private-networks)). Never add a public route
11
11
  (port forwarding, public reverse proxy, ngrok…); the only exception is a Cloudflare Tunnel
12
12
  behind Cloudflare Access, with its risks (it needs `WHERDR_TRUSTED_NETWORKS=any`).
13
- - It answers only this computer and the tailnet: see [Network access](security.md#network-access).
13
+ - It answers only this computer and your own tailnet devices: see [Network access](security.md#network-access).
14
14
  - Requests are accepted only for `localhost`, loopback addresses, the runtime's hostname, the
15
15
  hostname in `APP_URL`, the tailnet name `tailscale serve` publishes for wherdr's own port
16
16
  (read from Tailscale on the machine, never from a request, and only while it is published),
@@ -29,21 +29,48 @@ wherdr drives Herdr, so **it can run anything as your user**.
29
29
 
30
30
  A network error or a server restart never locks the app. **Lock all devices** (Settings →
31
31
  Security, for a lost or stolen device) ends every session, this one included, and keeps the
32
- passkeys: each device unlocks again with its own. Turning the lock off removes the passkeys and
33
- invalidates every session. Lost passkey: delete `data/auth.json` on the server; the app is open
34
- again to whoever can reach it.
32
+ passkeys: each device unlocks again with its own. It draws a new signing secret and a new random
33
+ session generation, so even a cookie forged by someone who once read `data/auth.json` stops
34
+ working; open WebSockets close at once, and every push subscription is removed (a device
35
+ subscribes again by itself once unlocked; its own quiet setting is reset). Turning the lock off
36
+ does the same and removes the passkeys; every open app then posts its push subscription again
37
+ at once, and an app that was closed or asleep does so when it opens or reconnects. The first start after upgrading to this version ends the open
38
+ sessions once (the old numeric generation is replaced); the passkeys stay. Lost passkey: delete
39
+ `data/auth.json` on the server; the app is open again to whoever can reach it.
40
+ - **Before the unlock**, a locked device only learns that the app is locked: no passkey names or
41
+ dates, no machine name, no server clock. Unlocking lists no key: the device offers its own
42
+ passkey for the site (passkeys are registered as discoverable credentials). Unlock attempts
43
+ are limited to 10 per minute per client, and at most 100 unlock challenges wait at once.
44
+ - **Protected files**: the file browser and the file paths shown in conversations never preview,
45
+ download or stream wherdr's data folder (`DATA_DIR`, with `auth.json`, the push keys and
46
+ subscriptions), `~/.ssh` or `~/.config/herdr`, on any machine, even through a symlink or inside
47
+ a browsed project. Agents themselves can still read them: the lock protects the app, not the
48
+ machine.
35
49
  - <a id="new-device"></a>**New device**: it can only get in with an existing passkey. Unlock it
36
50
  with a passkey synced by your password manager (iCloud Keychain, Google Password Manager,
37
51
  1Password…), or with the passkey of another device through the browser's "use a phone or
38
52
  tablet" QR code, then Settings → Security → **Add this device** to give it its own passkey.
39
- - Without a passkey, **anyone who can reach the address can control your agents**: every device
40
- on your tailnet, and every user of your tailnet if you share it.
53
+ - By default, with no passkey, wherdr answers **this computer and your own tailnet devices**:
54
+ devices of other people on your tailnet are always filtered out (no setting turns this off).
55
+ Whoever uses this computer or one of your devices can control your agents, other OS users of
56
+ this computer included (see [Shared computers](security.md#shared-computers)). The passkey is
57
+ optional: it adds an automatic lock on each device if you want one, and then lets other
58
+ people's tailnet devices reach that lock.
41
59
  - Writes require `Content-Type: application/json` (or `image/*` for photos, `application/octet-stream` for attached files) and an `Origin`
42
- matching the host, WebSockets included. API reads sent from another site (links, images,
60
+ matching the host, WebSockets included. Request bodies are read with a size limit (256 KB for
61
+ JSON, 20 MB for photos, 30 MB for attached files), chunked bodies included: past it the server
62
+ stops reading, answers `too_large` and closes the connection. API reads sent from another site (links, images,
43
63
  `no-cors` fetches, reported by the browser's `Sec-Fetch-Site`) are refused too.
44
64
  - A strict Content Security Policy limits scripts to the app's own origin and hashed inline
45
65
  scripts, and connections to that origin and its WebSockets. It also blocks embedded objects
46
66
  and framing.
67
+ - Images in agent replies load from the app's own origin (`data:` and `blob:` included) and,
68
+ over HTTPS only, from any machine and port of the tailnet wherdr is reached on
69
+ (`https://*.<tailnet>.ts.net:*`). That tailnet is read from Tailscale on the machine, from
70
+ `APP_URL`, from `HERDR_WEB_ALLOWED_HOSTS` and from what `tailscale serve` publishes, never from
71
+ a request; a name outside `*.ts.net` adds nothing. Any other image (public sites, another
72
+ tailnet, plain HTTP, Tailscale IP addresses) is refused by the browser and shown as a note with
73
+ a link to open it in a new tab.
47
74
  - wherdr never changes your agents' global settings: model and effort changes are for the
48
75
  current session only.
49
76
 
@@ -51,17 +78,19 @@ See [SECURITY.md](../SECURITY.md) to report a vulnerability.
51
78
 
52
79
  ### Network access
53
80
 
54
- wherdr answers **this computer and your tailnet only**. A device on any other network (a coffee shop
81
+ wherdr answers **this computer and your own tailnet devices only**. A device on any other network (a coffee shop
55
82
  Wi-Fi, an office LAN, the Internet) gets HTTP 403 (`network`), with a page that says why, even
56
- when it knows the address and the port. The passkey lock is never required, on any network: it
57
- is an extra lock on top, and it needs HTTPS (or `localhost`).
83
+ when it knows the address and the port. The passkey lock is never required for this computer
84
+ and your own devices: it is an extra lock on top, and it needs HTTPS (or `localhost`). Other
85
+ people's tailnet devices get in only once a passkey is set up (see below).
58
86
 
59
87
  **Good practices**
60
88
 
61
89
  - Prefer **Tailscale** (`tailscale serve`): a private HTTPS address, only for your devices.
62
90
  - A network you add **without HTTPS** gets no passkey, no installed app and no notifications, and
63
91
  any device on that network can control your agents.
64
- - **Create a passkey** as soon as wherdr is reached over HTTPS.
92
+ - **Passkey** (optional, over HTTPS): an automatic lock on each device, if you want one. Your
93
+ own devices get in without it.
65
94
 
66
95
  How a request is judged, before anything else (WebSockets included):
67
96
 
@@ -74,6 +103,20 @@ How a request is judged, before anything else (WebSockets included):
74
103
  reads that from Tailscale every 10 seconds (the CLI or Tailscale's socket); an
75
104
  interface address in that range proves nothing, since a LAN may use it too. When Tailscale is
76
105
  stopped, missing or unreadable for 30 seconds, no direct connection counts as tailnet.
106
+ - <a id="own-devices"></a>*Your own devices only*: a peer counts when Tailscale lists it under
107
+ the same user as this machine (`UserID`), is not tagged, and is not a node of someone you
108
+ shared this machine with (`ShareeNode`). Every other peer is refused with HTTP 403 (`network`, `why: foreign`) while
109
+ no passkey is set up: another user's device in a team tailnet, a machine someone
110
+ [shared](https://tailscale.com/kb/1084/sharing) with you (it belongs to another user), a
111
+ device of someone you shared this machine with, a tagged node (tagged devices
112
+ belong to no user, your own tagged servers included). When the machine running wherdr is
113
+ itself tagged, its owner is unknown: no peer counts as yours, so only this computer gets in
114
+ until a passkey is set up. The same rule applies to the client `tailscale serve` names in
115
+ `X-Forwarded-For`: without a passkey, it must be one of your devices in the latest reading
116
+ (a device that joined in the last seconds, or any device while Tailscale cannot be read, is
117
+ refused). The other peers are remembered from the last valid reading, so a failed reading
118
+ does not let them in. Once a passkey is set up, they are let through to the lock screen and
119
+ need that passkey like any device.
77
120
  - **Proxy headers**, only when the peer is this computer (a proxy on the machine, such as
78
121
  `tailscale serve`): every address in `X-Forwarded-For`, `Forwarded`, `X-Real-IP`,
79
122
  `CF-Connecting-IP` must be loopback, the tailnet or a trusted range too. They can only refuse
@@ -99,6 +142,13 @@ How a request is judged, before anything else (WebSockets included):
99
142
  loopback or this machine's tailnet address as Tailscale reports it, with Tailscale running
100
143
  (`HOST=0.0.0.0`, a LAN address), and says how to fix it.
101
144
 
145
+ > [!IMPORTANT]
146
+ > <a id="shared-computers"></a>**Every user of this computer counts as this computer.** Loopback
147
+ > connections are not tied to an OS user: on a machine where other people have an account (a
148
+ > shared server, a lab machine), any of them can open `http://127.0.0.1:7683/` and control
149
+ > your agents. wherdr keeps loopback open so you can always reach it on your own machine. On a
150
+ > machine others log in to, the optional passkey locks loopback too.
151
+
102
152
  > [!IMPORTANT]
103
153
  > **A relay on this computer counts as this computer.** A reverse proxy or TCP forward you run
104
154
  > on the machine (nginx without forwarded headers, `socat`, an SSH tunnel, a tunnel client) opens
@@ -20,6 +20,10 @@
20
20
  - **HTTP 403 "wherdr only answers this computer and your tailnet"**: the device is on another
21
21
  network (Wi-Fi, LAN). Open wherdr through Tailscale, or name that private network in
22
22
  `WHERDR_TRUSTED_NETWORKS` ([Network access](security.md#network-access)).
23
+ - **HTTP 403 "This device is not one of the owner's"**: Tailscale lists the device under another
24
+ user (a team tailnet), as shared in from another tailnet, or as a tagged node. Without a passkey,
25
+ wherdr only answers your own devices: log that device in to Tailscale with your own account, or
26
+ set up a passkey ([Your own devices only](security.md#own-devices)).
23
27
  - **HTTP 403 "Tailscale Funnel opens wherdr to the Internet"**: turn Funnel off for wherdr’s port (`tailscale funnel status`).
24
28
  - **wherdr stops at startup with "Refusing to listen on …"**: `HOST` is not loopback nor this
25
29
  machine's tailnet address. Set `HOST=127.0.0.1` and use `tailscale serve`, or see
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wherdr",
3
- "version": "2.4.0",
3
+ "version": "2.6.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Unofficial mobile web client (PWA) to drive the coding agents running in Herdr",
@@ -30,6 +30,27 @@ So a message from the Board is the user's own words: answer and act on it as on
30
30
  from them. Report your progress with `herdr-projects report` as usual: the Board shows the
31
31
  state, the message and the progress you report, the pull request and the review.
32
32
 
33
+ ## Links
34
+
35
+ - **Files:** verify that the file exists and is readable on the agent's machine, then
36
+ write its absolute path in inline code (backticks), optionally with `:line` or
37
+ `:line:column` (these suffixes are stripped, not navigated to). Chat recognizes
38
+ paths; Markdown file links do not use its file viewer. `~/` means that machine's
39
+ home; relative paths use the pane's working folder, falling back to home when
40
+ unknown or outside home. Never use a path relative to another folder (such as a
41
+ vault). The file and its symlink target must stay inside that machine's home.
42
+ Terminal links open only HTTP(S) URLs, not file paths.
43
+ - **Web:** give an absolute HTTPS URL reachable from the user's phone and other
44
+ machines (for example through their tailnet). Never offer localhost, loopback
45
+ addresses (including `127.0.0.1`) or relative URLs as portable links. Follow
46
+ redirects and verify a successful HTTP status and the final destination; a local
47
+ response alone does not prove access from another device.
48
+ - **Images:** prefer displaying them directly in chat (web images must come from
49
+ wherdr's own origin or an HTTPS tailnet address). For an image URL, verify
50
+ that the final response serves the image itself with an `image/*` Content-Type,
51
+ rather than an HTML page.
52
+ - If no verified link is available, say so clearly instead of offering a broken one.
53
+
33
54
  ## What the Board does not do
34
55
 
35
56
  No task is added, delegated, stopped, archived or cleared from wherdr: the user asks the
@@ -1 +0,0 @@
1
- .header-menu[data-v-562a5bfa]{display:contents}.grid-backdrop[data-v-a290ab6b]{z-index:-1;pointer-events:none;contain:strict;overflow:hidden}.gbd-dots[data-v-a290ab6b],.grid-backdrop[data-v-a290ab6b]{position:absolute;inset:0}.gbd-dots[data-v-a290ab6b]{background-image:radial-gradient(circle at 32px 32px,color-mix(in srgb,var(--muted) 60%,transparent) 1.4px,transparent 2.2px),linear-gradient(color-mix(in srgb,var(--line) 30%,transparent) 1px,transparent 1px),linear-gradient(90deg,color-mix(in srgb,var(--line) 30%,transparent) 1px,transparent 1px),radial-gradient(ellipse 50% 40% at 50% 25%,color-mix(in srgb,var(--accent) 6%,transparent),transparent 75%);background-position:calc(50% - 32px) -32px,top,top,0 0;background-size:64px 64px,64px 64px,64px 64px,100% 100%;-webkit-mask:radial-gradient(85% 75% at 50% 35%,#000 25%,#00000073 60%,#0000 88%);mask:radial-gradient(85% 75% at 50% 35%,#000 25%,#00000073 60%,#0000 88%)}.gbd-packets[data-v-a290ab6b]{position:absolute;inset:0;-webkit-mask:radial-gradient(70% 60% at 50% 30%,#000 30%,#0000 75%);mask:radial-gradient(70% 60% at 50% 30%,#000 30%,#0000 75%)}.gbd-packets[data-v-a290ab6b] .pkt{will-change:transform,opacity;--c:var(--accent);display:block;position:absolute}.gbd-packets[data-v-a290ab6b] .pkt.alt{--c:var(--lav)}.gbd-packets[data-v-a290ab6b] .pkt.h{background:linear-gradient(90deg,transparent,var(--c));width:56px;height:1px;margin-left:-56px}.gbd-packets[data-v-a290ab6b] .pkt.h.rev{background:linear-gradient(270deg,transparent,var(--c));margin-left:0}.gbd-packets[data-v-a290ab6b] .pkt.h:after{content:"";background:var(--c);width:6px;height:5px;box-shadow:0 0 10px var(--c);position:absolute;top:-2px;right:-3px}.gbd-packets[data-v-a290ab6b] .pkt.h.rev:after{left:-3px;right:auto}.gbd-packets[data-v-a290ab6b] .pkt.v{background:linear-gradient(180deg,transparent,var(--c));width:1px;height:56px;margin-top:-56px}.gbd-packets[data-v-a290ab6b] .pkt.v.rev{background:linear-gradient(0deg,transparent,var(--c));margin-top:0}.gbd-packets[data-v-a290ab6b] .pkt.v:after{content:"";background:var(--c);width:5px;height:6px;box-shadow:0 0 10px var(--c);position:absolute;bottom:-3px;left:-2px}.gbd-packets[data-v-a290ab6b] .pkt.v.rev:after{top:-3px;bottom:auto}.gbd-vignette[data-v-a290ab6b]{background:linear-gradient(to right,var(--bg),transparent 14%,transparent 86%,var(--bg)),linear-gradient(to bottom,var(--bg),transparent 18%,transparent 70%,var(--bg));position:absolute;inset:0}.xterm{cursor:text;-webkit-user-select:none;user-select:none;position:relative}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;inset:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{z-index:10;color:#0000;pointer-events:none;position:absolute;inset:0}.xterm .xterm-accessibility-tree:not(.debug) ::-moz-selection{color:#0000}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:#0000}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:underline double;text-decoration:underline double}.xterm-underline-3{-webkit-text-decoration:underline wavy;text-decoration:underline wavy}.xterm-underline-4{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.xterm-underline-5{-webkit-text-decoration:underline dashed;text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}.key-modifier[aria-pressed=true][data-v-33673e53]{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}.usage-limit-card[data-v-8c0c9595]{border-top:1px solid var(--line);white-space:normal;text-align:left;color:var(--ochre);flex-wrap:wrap;justify-content:flex-start;padding-top:8px}.restart-text small[data-v-8c0c9595]{color:var(--muted);display:block}.dismissible-transcript-note[data-v-745f7953]{flex:none;align-items:center;gap:8px;padding:0 4px 0 12px;display:flex}.dismissible-transcript-note>span[data-v-745f7953]{flex:1;padding:7px 0}.dismissible-transcript-note>button[data-v-745f7953]{min-width:40px;min-height:40px;color:var(--muted);font:22px/1 var(--sans);touch-action:manipulation;border-radius:6px;flex:none;align-self:stretch}.dismissible-transcript-note>button[data-v-745f7953]:focus-visible,.dismissible-transcript-note>button[data-v-745f7953]:hover{color:var(--text);background:var(--line-soft)}
Binary file
Binary file
Binary file
Binary file