wherdr 1.3.3 → 2.0.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.
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/BDNMzG2s.js +1 -0
- package/.output/public/_nuxt/BETID3_E.js +1 -0
- package/.output/public/_nuxt/BHenDMeH.js +1 -0
- package/.output/public/_nuxt/BYZVrreW.js +1 -0
- package/.output/public/_nuxt/BhgehRdJ.js +1 -0
- package/.output/public/_nuxt/{yzABg5xl.js → Bpoxvzh6.js} +1 -1
- package/.output/public/_nuxt/BtH82ZuZ.js +9 -0
- package/.output/public/_nuxt/C25ROUa9.js +3 -0
- package/.output/public/_nuxt/C3Bkn4ac.js +62 -0
- package/.output/public/_nuxt/CDWjMk9C.js +2 -0
- package/.output/public/_nuxt/{Ci8N4mWR.js → CXJnLmWv.js} +1 -1
- package/.output/public/_nuxt/CuUQ4mEf.js +1 -0
- package/.output/public/_nuxt/DFGEl0v2.js +23 -0
- package/.output/public/_nuxt/{CKhgznq4.js → DFmgQVme.js} +42 -39
- package/.output/public/_nuxt/DZGKxROq.js +6 -0
- package/.output/public/_nuxt/DmkWvmnE.js +2 -0
- package/.output/public/_nuxt/DrKKeFFE.js +31 -0
- package/.output/public/_nuxt/DyfFwwGq.js +1 -0
- package/.output/public/_nuxt/FaNto0ga.js +105 -0
- package/.output/public/_nuxt/NIF0VwHk.js +25 -0
- package/.output/public/_nuxt/R0Y4VoeX.js +1 -0
- package/.output/public/_nuxt/board.CEEcFc6M.css +1 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/0b28b3e9-664e-4347-a35b-afb0a711d5c5.json +1 -0
- package/.output/public/_nuxt/entry.CKoXcwRa.css +2 -0
- package/.output/public/_nuxt/pdf.min.t82Z80JU.mjs +28 -0
- package/.output/public/_nuxt/pdf.worker.min.wgc6bjNh.mjs +28 -0
- package/.output/public/_nuxt/settings.Cdb21nNw.css +1 -0
- package/.output/public/sw.js +1 -1
- package/.output/server/chunks/_/branding.mjs +1 -1
- package/.output/server/chunks/_/changes.mjs +4 -4
- package/.output/server/chunks/_/fileDownload.mjs +278 -0
- package/.output/server/chunks/_/files.mjs +60 -20
- package/.output/server/chunks/_/machineOrder.mjs +1 -1
- package/.output/server/chunks/_/onboarding.mjs +1 -1
- package/.output/server/chunks/_/package.mjs +11 -3
- package/.output/server/chunks/_/plugins.mjs +92 -122
- package/.output/server/chunks/_/restart.mjs +1 -1
- package/.output/server/chunks/_/selfupdate.mjs +1 -1
- package/.output/server/chunks/_/settingstabs.mjs +36 -0
- package/.output/server/chunks/_/updates.mjs +1 -1
- package/.output/server/chunks/_/worktrees.mjs +1 -1
- package/.output/server/chunks/_/ws.mjs +43 -6
- package/.output/server/chunks/nitro/nitro.mjs +14126 -10238
- package/.output/server/chunks/raw/check-doc-links.mjs +5 -0
- package/.output/server/chunks/raw/check-leaks.mjs +1 -1
- package/.output/server/chunks/raw/check-release-notes.mjs +5 -0
- package/.output/server/chunks/raw/herdr-plugin.mjs +1 -1
- package/.output/server/chunks/raw/install-claude-statusline.mjs +1 -1
- package/.output/server/chunks/raw/prerelease-summary.mjs +5 -0
- package/.output/server/chunks/raw/prerelease.mjs +7 -0
- package/.output/server/chunks/raw/reveal.mjs +1 -1
- package/.output/server/chunks/raw/test-affected.mjs +5 -0
- package/.output/server/chunks/routes/api/_..._.mjs +6 -5
- package/.output/server/chunks/routes/api/agents.post.mjs +6 -5
- package/.output/server/chunks/routes/api/ask-tab.post.mjs +6 -5
- package/.output/server/chunks/routes/api/attach.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/disable.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/lock-all.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/lock.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/login/options.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/login/verify.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/max-session.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/register/options.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/register/verify.post.mjs +6 -5
- package/.output/server/chunks/routes/api/auth/status.get.mjs +6 -5
- package/.output/server/chunks/routes/api/changes.get.mjs +7 -6
- package/.output/server/chunks/routes/api/chat/image.get.mjs +7 -6
- package/.output/server/chunks/routes/api/chat.get.mjs +44 -11
- package/.output/server/chunks/routes/api/choose.post.mjs +44 -30
- package/.output/server/chunks/routes/api/claude-setup.post.mjs +42 -0
- package/.output/server/chunks/routes/api/claude-statusline.post.mjs +7 -6
- package/.output/server/chunks/routes/api/clear-queued-input.post.mjs +42 -0
- package/.output/server/chunks/routes/api/close/status.get.mjs +7 -6
- package/.output/server/chunks/routes/api/close.post.mjs +6 -5
- package/.output/server/chunks/routes/api/codex-update.post.mjs +7 -6
- package/.output/server/chunks/routes/api/commands.get.mjs +9 -7
- package/.output/server/chunks/routes/api/config.get.mjs +10 -13
- package/.output/server/chunks/routes/api/dirs.get.mjs +7 -6
- package/.output/server/chunks/routes/api/dismiss-stopped.post.mjs +7 -6
- package/.output/server/chunks/routes/api/dismiss.post.mjs +7 -6
- package/.output/server/chunks/routes/api/effort.post.mjs +7 -6
- package/.output/server/chunks/routes/api/efforts.get.mjs +7 -6
- package/.output/server/chunks/routes/api/file.post.mjs +7 -6
- package/.output/server/chunks/routes/api/files/download.get.mjs +12 -24
- package/.output/server/chunks/routes/api/files/download.head.mjs +41 -0
- package/.output/server/chunks/routes/api/files/read.get.mjs +8 -7
- package/.output/server/chunks/routes/api/files.get.mjs +7 -6
- package/.output/server/chunks/routes/api/health.get.mjs +6 -5
- package/.output/server/chunks/routes/api/input.post.mjs +33 -15
- package/.output/server/chunks/routes/api/interrupt.post.mjs +24 -21
- package/.output/server/chunks/routes/api/isgit.get.mjs +8 -8
- package/.output/server/chunks/routes/api/machine/awake.get.mjs +7 -6
- package/.output/server/chunks/routes/api/machine/awake.post.mjs +7 -6
- package/.output/server/chunks/routes/api/machine/order.get.mjs +6 -5
- package/.output/server/chunks/routes/api/machine/order.post.mjs +6 -5
- package/.output/server/chunks/routes/api/machine/reconnect.post.mjs +40 -0
- package/.output/server/chunks/routes/api/machine/rename.post.mjs +7 -6
- package/.output/server/chunks/routes/api/machine/status.post.mjs +39 -0
- package/.output/server/chunks/routes/api/menu-left.post.mjs +40 -0
- package/.output/server/chunks/routes/api/menu.post.mjs +10 -6
- package/.output/server/chunks/routes/api/model.post.mjs +7 -6
- package/.output/server/chunks/routes/api/models.get.mjs +7 -6
- package/.output/server/chunks/routes/api/nav.post.mjs +40 -26
- package/.output/server/chunks/routes/api/onboarding.get.mjs +6 -5
- package/.output/server/chunks/routes/api/onboarding.post.mjs +6 -5
- package/.output/server/chunks/routes/api/phone.get.mjs +8 -10
- package/.output/server/chunks/routes/api/phone.post.mjs +8 -10
- package/.output/server/chunks/routes/api/plugins/actions.get.mjs +6 -8
- package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +6 -8
- package/.output/server/chunks/routes/api/plugins/projects.get.mjs +10 -8
- package/.output/server/chunks/routes/api/plugins/projects.post.mjs +11 -7
- package/.output/server/chunks/routes/api/prompt.post.mjs +22 -13
- package/.output/server/chunks/routes/api/push/quiet.post.mjs +7 -6
- package/.output/server/chunks/routes/api/push/subscribe.post.mjs +7 -7
- package/.output/server/chunks/routes/api/push/test.post.mjs +7 -6
- package/.output/server/chunks/routes/api/push/unsubscribe.post.mjs +7 -6
- package/.output/server/chunks/routes/api/quotas.get.mjs +7 -6
- package/.output/server/chunks/routes/api/rename.post.mjs +6 -5
- package/.output/server/chunks/routes/api/requeue.post.mjs +7 -6
- package/.output/server/chunks/routes/api/restart.get.mjs +6 -5
- package/.output/server/chunks/routes/api/restart.post.mjs +6 -5
- package/.output/server/chunks/routes/api/reveal.post.mjs +91 -8
- package/.output/server/chunks/routes/api/screen-key.post.mjs +19 -15
- package/.output/server/chunks/routes/api/screen.get.mjs +8 -40
- package/.output/server/chunks/routes/api/search.get.mjs +7 -6
- package/.output/server/chunks/routes/api/seen.post.mjs +7 -6
- package/.output/server/chunks/routes/api/sessions.get.mjs +7 -6
- package/.output/server/chunks/routes/api/sessions.post.mjs +7 -6
- package/.output/server/chunks/routes/api/settings-tab.post.mjs +90 -0
- package/.output/server/chunks/routes/api/{plugins/thread-limits.get.mjs → settings.get.mjs} +10 -9
- package/.output/server/chunks/routes/api/settings.post.mjs +40 -0
- package/.output/server/chunks/routes/api/space.post.mjs +34 -18
- package/.output/server/chunks/routes/api/state.get.mjs +7 -6
- package/.output/server/chunks/routes/api/tasks/action.post.mjs +39 -0
- package/.output/server/chunks/routes/api/{plugins/profiles.get.mjs → tasks/add.post.mjs} +12 -14
- package/.output/server/chunks/routes/api/tasks/after.post.mjs +39 -0
- package/.output/server/chunks/routes/api/tasks/approve.post.mjs +39 -0
- package/.output/server/chunks/routes/api/{project/report.get.mjs → tasks/archived.get.mjs} +12 -13
- package/.output/server/chunks/routes/api/{gitroot.get.mjs → tasks/autopilot.get.mjs} +13 -14
- package/.output/server/chunks/routes/api/tasks/autopilot.post.mjs +39 -0
- package/.output/server/chunks/routes/api/{index.get.mjs → tasks/board-settings.get.mjs} +12 -13
- package/.output/server/chunks/routes/api/tasks/board-settings.post.mjs +39 -0
- package/.output/server/chunks/routes/api/tasks/coordinator.get.mjs +40 -0
- package/.output/server/chunks/routes/api/tasks/coordinator.post.mjs +39 -0
- package/.output/server/chunks/routes/api/tasks/details.get.mjs +40 -0
- package/.output/server/chunks/routes/api/tasks/limit.post.mjs +39 -0
- package/.output/server/chunks/routes/api/tasks/links.get.mjs +39 -0
- package/.output/server/chunks/routes/api/tasks/models.get.mjs +40 -0
- package/.output/server/chunks/routes/api/tasks/pick.post.mjs +39 -0
- package/.output/server/chunks/routes/api/tasks/question.post.mjs +39 -0
- package/.output/server/chunks/routes/api/tasks/scheduled.get.mjs +40 -0
- package/.output/server/chunks/routes/api/{project → tasks}/settings.get.mjs +10 -11
- package/.output/server/chunks/routes/api/tasks/start.get.mjs +40 -0
- package/.output/server/chunks/routes/api/tasks/tell.post.mjs +39 -0
- package/.output/server/chunks/routes/api/tasks.get.mjs +39 -0
- package/.output/server/chunks/routes/api/unqueue.post.mjs +7 -6
- package/.output/server/chunks/routes/api/update/status.get.mjs +6 -5
- package/.output/server/chunks/routes/api/update.get.mjs +8 -10
- package/.output/server/chunks/routes/api/update.post.mjs +8 -10
- package/.output/server/chunks/routes/api/upload.post.mjs +7 -6
- package/.output/server/chunks/routes/api/worktrees/remove.post.mjs +6 -5
- package/.output/server/chunks/routes/api/worktrees.get.mjs +6 -5
- package/.output/server/chunks/routes/favicon.ico.get.mjs +6 -5
- package/.output/server/chunks/routes/icons/_name_.get.mjs +7 -6
- package/.output/server/chunks/routes/renderer.mjs +8 -7
- package/.output/server/chunks/routes/uploads/_...name_.get.mjs +7 -6
- package/.output/server/chunks/routes/ws/_..._.mjs +7 -6
- package/.output/server/chunks/routes/ws/events.mjs +9 -7
- package/.output/server/chunks/routes/ws/term.mjs +7 -6
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/index.mjs +7 -6
- package/.output/server/package.json +1 -1
- package/README.md +44 -1424
- package/bin/lib/commands.mjs +44 -9
- package/bin/lib/panel.mjs +3 -11
- package/bin/lib/service.mjs +41 -2
- package/bin/lib/skill.mjs +1 -1
- package/bin/lib/tailnet.d.mts +6 -3
- package/bin/lib/tailnet.mjs +70 -32
- package/bin/lib/userpath.d.mts +14 -0
- package/bin/lib/userpath.mjs +142 -0
- package/docs/README.md +58 -0
- package/docs/RELEASING.md +174 -0
- package/docs/board.md +164 -0
- package/docs/command.md +58 -0
- package/docs/configuration.md +39 -0
- package/docs/conversation.md +132 -0
- package/docs/development.md +38 -0
- package/docs/devices.md +58 -0
- package/docs/files.md +29 -0
- package/docs/herdr-projects-1x.md +278 -0
- package/docs/home.md +52 -0
- package/docs/installation.md +174 -0
- package/docs/machines.md +69 -0
- package/docs/migrate-from-1.x-docker.md +160 -0
- package/docs/networks.md +154 -0
- package/docs/notifications.md +83 -0
- package/docs/overview.md +71 -0
- package/docs/phone.md +26 -0
- package/docs/plugins.md +109 -0
- package/docs/quick-start.md +89 -0
- package/docs/quotas.md +65 -0
- package/docs/release-notes-2.0.0.md +84 -0
- package/docs/security.md +117 -0
- package/docs/terminal.md +15 -0
- package/docs/troubleshooting.md +45 -0
- package/package.json +11 -3
- package/skills/wherdr/SKILL.md +24 -167
- package/.output/public/_nuxt/B4Yk2tB1.js +0 -23
- package/.output/public/_nuxt/BBw_0a4M.js +0 -6
- package/.output/public/_nuxt/BSy6OCIY.js +0 -1
- package/.output/public/_nuxt/BX5PM2JQ.js +0 -1
- package/.output/public/_nuxt/BgPDnf2S.js +0 -1
- package/.output/public/_nuxt/BkOxQzbS.js +0 -2
- package/.output/public/_nuxt/CP8o925U.js +0 -1
- package/.output/public/_nuxt/CWwyK2XK.js +0 -2
- package/.output/public/_nuxt/CkbmecwS.js +0 -1
- package/.output/public/_nuxt/Cr3SrGky.js +0 -1
- package/.output/public/_nuxt/DAg9fiVh.js +0 -89
- package/.output/public/_nuxt/DT8qZ0Wv.js +0 -1
- package/.output/public/_nuxt/DaaJIRB3.js +0 -1
- package/.output/public/_nuxt/DcJi16XA.js +0 -1
- package/.output/public/_nuxt/SiL1FJ5s.js +0 -3
- package/.output/public/_nuxt/VpjCgSXu.js +0 -36
- package/.output/public/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json +0 -1
- package/.output/public/_nuxt/dPxB2L94.js +0 -89
- package/.output/public/_nuxt/entry.CGWKmYU3.css +0 -2
- package/.output/public/_nuxt/xYX85SPI.js +0 -1
- package/.output/server/chunks/_/pluginPolicy.mjs +0 -93
- package/.output/server/chunks/_/projectBoard.mjs +0 -366
- package/.output/server/chunks/_/projectDocs.mjs +0 -106
- package/.output/server/chunks/_/projectInbox.mjs +0 -135
- package/.output/server/chunks/_/projectThreads.mjs +0 -46
- package/.output/server/chunks/_/projectThreads2.mjs +0 -61
- package/.output/server/chunks/_/projectsActions.mjs +0 -143
- package/.output/server/chunks/_/projectsPlugin.mjs +0 -56
- package/.output/server/chunks/_/reveal.mjs +0 -115
- package/.output/server/chunks/raw/dev.wherdr.open.mjs +0 -7
- package/.output/server/chunks/raw/docker-entrypoint.mjs +0 -7
- package/.output/server/chunks/raw/install-host-open.mjs +0 -7
- package/.output/server/chunks/raw/wherdr-open.mjs +0 -7
- package/.output/server/chunks/routes/api/board.get.mjs +0 -147
- package/.output/server/chunks/routes/api/plugins/thread-limits.post.mjs +0 -45
- package/.output/server/chunks/routes/api/project/inbox.get.mjs +0 -43
- package/.output/server/chunks/routes/api/project/inbox.post.mjs +0 -43
- package/.output/server/chunks/routes/api/project/memory.get.mjs +0 -42
- package/.output/server/chunks/routes/api/project/routines.get.mjs +0 -43
- package/.output/server/chunks/routes/api/project/thread.get.mjs +0 -44
- package/.output/server/chunks/routes/api/project/thread.post.mjs +0 -45
- package/bin/install-docker-skill.sh +0 -20
package/docs/plugins.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Herdr plugins
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
### Install as a Herdr plugin
|
|
6
|
+
|
|
7
|
+
This is what [the one command](installation.md#the-one-command) installs; you can also run it yourself, on Linux
|
|
8
|
+
and macOS, with Herdr ≥ 0.9.1 (no checks for Herdr or Node.js first). In three lines:
|
|
9
|
+
|
|
10
|
+
1. Run `herdr plugin install afloury/wherdr`: it installs wherdr, starts it
|
|
11
|
+
(`✓ wherdr is running → http://localhost:7683`) and, at the first install on a computer with a
|
|
12
|
+
screen, opens the setup guide in your browser (`open` on macOS, `xdg-open` on Linux with a
|
|
13
|
+
display; a headless server, or `WHERDR_NO_BROWSER=1` in the environment of the install, just
|
|
14
|
+
prints `http://localhost:7683/#/setup`). Updates open nothing.
|
|
15
|
+
2. In Herdr, press **prefix+i** (`ctrl+b` then `i` with the default prefix): the **wherdr** panel,
|
|
16
|
+
with the state, **O** Open wherdr and **P** Set up my phone. The install prints the key it
|
|
17
|
+
chose (see [Panel key](plugins.md#panel-key)).
|
|
18
|
+
3. **P** opens Settings › Phone in the browser: **Make wherdr reachable from my phone** publishes
|
|
19
|
+
it on your tailnet, then the QR code shows once the address answers. Scan it with the iPhone,
|
|
20
|
+
then Share → Add to Home Screen.
|
|
21
|
+
|
|
22
|
+
Herdr shows the manifest and the commands it will run, then the plugin installs wherdr in
|
|
23
|
+
`~/wherdr` and starts it, unless something already answers on the port. If wherdr cannot start,
|
|
24
|
+
the install still succeeds and says why; press **S** in the panel once it is fixed. It runs
|
|
25
|
+
wherdr natively:
|
|
26
|
+
|
|
27
|
+
- **Node.js 22**, on Linux and macOS: the plugin downloads the prebuilt package of its version
|
|
28
|
+
from GitHub Releases and verifies its SHA-256 checksum, copies it to `~/wherdr/app` and runs it
|
|
29
|
+
from there (detached, or by the login service), with its pid and log (`wherdr.log`) in
|
|
30
|
+
`~/wherdr`. Herdr builds plugins in a temporary folder and then moves them, so the server never
|
|
31
|
+
runs from the plugin folder. Bun works too when Node.js 22 is missing. Without either, the
|
|
32
|
+
install stops and says so ([the one command](installation.md#the-one-command) offers to install Node.js).
|
|
33
|
+
The **wherdr** panel (a Herdr popup) shows ● running / ○ stopped, the local address, the phone
|
|
34
|
+
address (with its QR code once it answers), and two main keys: **O** Open wherdr, **P** Set up my
|
|
35
|
+
phone (Settings › Phone in the browser). The other keys stay as a fallback: **S** start, **X**
|
|
36
|
+
stop, **R** restart, **L** log (Ctrl+C to go back), **U** update (reinstalls the plugin), **A** auto-start at login on/off, **Q** quit.
|
|
37
|
+
|
|
38
|
+
#### Panel key
|
|
39
|
+
|
|
40
|
+
Herdr plugins have no menu of their own: an action runs from a key or from the command line. The
|
|
41
|
+
install therefore appends one marked block to Herdr's `config.toml` (`HERDR_CONFIG_PATH`, or
|
|
42
|
+
`~/.config/herdr/config.toml`):
|
|
43
|
+
|
|
44
|
+
```toml
|
|
45
|
+
# wherdr: key that opens the wherdr panel (remove this block to unbind it)
|
|
46
|
+
[[keys.command]]
|
|
47
|
+
key = "prefix+i"
|
|
48
|
+
type = "plugin_action"
|
|
49
|
+
command = "afloury.wherdr.panel"
|
|
50
|
+
description = "wherdr"
|
|
51
|
+
# end wherdr
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
It takes the first key of `prefix+i`, `prefix+u`, `prefix+y`, `prefix+m`, `prefix+alt+w` that
|
|
55
|
+
neither Herdr's default keymap nor your config already binds, and prints
|
|
56
|
+
`Press prefix+i in Herdr to open the wherdr panel.` Your own lines are never changed. Nothing is
|
|
57
|
+
added when a key already runs the panel, when every candidate is taken, or when
|
|
58
|
+
`herdr config check` would reject the result. The install then runs `herdr server reload-config`:
|
|
59
|
+
keys apply without restarting Herdr. Pick another key by editing the block, or bind the panel
|
|
60
|
+
yourself (`command = "afloury.wherdr.panel"`) before installing.
|
|
61
|
+
|
|
62
|
+
Without a key: `herdr plugin action invoke panel --plugin afloury.wherdr`. The plugin's actions:
|
|
63
|
+
|
|
64
|
+
| Action | What it does |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `panel` | The **wherdr** panel (a popup) |
|
|
67
|
+
| `open` | Opens wherdr in the browser (its tailnet address when published and answering) |
|
|
68
|
+
|
|
69
|
+
Up to 1.2.0 the panel action was called `wherdr`: change a key bound to `afloury.wherdr.wherdr` to
|
|
70
|
+
`afloury.wherdr.panel` (Herdr has no action aliases; the install warns about such a key and
|
|
71
|
+
leaves it alone). Remove the block with `sh scripts/herdr-plugin.sh key uninstall` in the plugin
|
|
72
|
+
folder (`~/.config/herdr/plugins/github/wherdr-…`), or delete the lines from `# wherdr:` to `# end wherdr`.
|
|
73
|
+
|
|
74
|
+
Auto-start: the plugin's startup hook starts wherdr each time the Herdr server starts, when
|
|
75
|
+
nothing answers on the port yet. [The one command](installation.md#the-one-command) also installs a login
|
|
76
|
+
service (a LaunchAgent on macOS, a `systemd --user` unit with lingering on Linux, pointing at
|
|
77
|
+
`~/wherdr/app` with the absolute path of the runtime and your [PATH](installation.md#agent-path)): wherdr then
|
|
78
|
+
runs from boot and is restarted if it crashes. Key **A** in the panel turns it on or off;
|
|
79
|
+
`herdr plugin install` alone does not add it, since it changes your login session.
|
|
80
|
+
|
|
81
|
+
Herdr runs the plugin's commands with the environment of its server, whose `PATH` often lacks the
|
|
82
|
+
folders of version managers (n, nvm, fnm, Volta, asdf). The install therefore saves the absolute
|
|
83
|
+
path of the runtime in `~/wherdr/plugin.env` (`WHERDR_RUNTIME=/path/to/node`); if that binary
|
|
84
|
+
disappears, **Start** looks in the usual places (`~/.n/bin`, `~/.nvm`, `~/.volta/bin`, fnm,
|
|
85
|
+
`~/.asdf/shims`, Homebrew, `/usr/local/bin`, `~/.bun/bin`) and saves what it finds. Set
|
|
86
|
+
`WHERDR_RUNTIME` there by hand for anything else. **Start** only reports success once the
|
|
87
|
+
process is alive and the port answers; otherwise it prints the last lines of `wherdr.log`.
|
|
88
|
+
|
|
89
|
+
When something already answers on the port (a wherdr started with systemd or by hand),
|
|
90
|
+
the plugin starts nothing and never stops it: **Stop** only stops what the plugin started. The
|
|
91
|
+
output of the install and of the startup hook is in the plugin logs:
|
|
92
|
+
`herdr plugin log list --plugin afloury.wherdr`.
|
|
93
|
+
|
|
94
|
+
**Uninstall.** Herdr has no uninstall hook, so remove wherdr in this order: in the panel, **A**
|
|
95
|
+
(if the login service is on) and **X**; in the plugin folder, `sh scripts/herdr-plugin.sh key
|
|
96
|
+
uninstall` (the panel key) and `sh scripts/herdr-plugin.sh skill uninstall`; then
|
|
97
|
+
`herdr plugin uninstall afloury.wherdr && rm -rf ~/wherdr` (`~/wherdr/data` holds your passkeys
|
|
98
|
+
and push keys).
|
|
99
|
+
|
|
100
|
+
## Herdr plugins
|
|
101
|
+
|
|
102
|
+
Actions declared by the plugins installed on a machine (`herdr plugin install|link`) appear in
|
|
103
|
+
the menus: workspace / tab / pane actions in the agent menu, global actions in the machine menu.
|
|
104
|
+
wherdr always passes the agent's pane explicitly. It asks for confirmation before running an
|
|
105
|
+
action, except for actions that only show or check something (list, show, status, doctor…).
|
|
106
|
+
Actions that open a plugin pane or popup open it in the attached Herdr terminal client, not on the
|
|
107
|
+
phone: the menu marks them **Opens in Herdr**. wherdr's own plugin actions (its panel, open in the
|
|
108
|
+
browser) are not listed. With a single machine, the machine's actions are in the **…** menu of
|
|
109
|
+
its session row, under the agent list header.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# First launch
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
## Quick start
|
|
6
|
+
|
|
7
|
+
### Ask your AI agent
|
|
8
|
+
|
|
9
|
+
**Recommended.** Paste this into Claude Code, Codex or any coding agent: it installs and sets up wherdr for you.
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
Read https://wherdr.dev/agent.md and help me set up wherdr. Check what is already installed first, and ask me before installing anything.
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
It follows [our setup guide for agents](https://wherdr.dev/agent.md): checks what you already have,
|
|
16
|
+
asks before installing anything, sets up your phone over Tailscale, never exposes wherdr to the Internet.
|
|
17
|
+
|
|
18
|
+
### Or run the installer yourself
|
|
19
|
+
|
|
20
|
+
On the computer that runs [Herdr](https://herdr.dev) (≥ 0.9.1), macOS or Linux:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
curl -fsSL https://wherdr.dev/install | sh
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
It installs the wherdr [Herdr plugin](plugins.md#install-as-a-herdr-plugin) with the verified package of the
|
|
27
|
+
GitHub release, runs wherdr natively as your user on `http://localhost:7683`, starts it at every
|
|
28
|
+
boot (a `systemd --user` service with lingering on Linux, a LaunchAgent on macOS) and opens its
|
|
29
|
+
setup guide in your browser; then **prefix+i** in Herdr opens the wherdr panel. Without Herdr, it
|
|
30
|
+
says where to get it; without Node.js 22, it offers to install it (Homebrew, or the official
|
|
31
|
+
build in `~/wherdr/node`, no sudo). On a server without a screen (or over SSH), it prints the
|
|
32
|
+
address and the phone steps instead. See [The one command](installation.md#the-one-command).
|
|
33
|
+
|
|
34
|
+
**wherdr 2.0 runs only natively**, as your user, so it can act on the
|
|
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
|
|
37
|
+
the checks), and [the `wherdr` command](command.md#the-wherdr-command) from npm or Homebrew (npm can hold a
|
|
38
|
+
new release for up to two days; the installer gets it from GitHub at once).
|
|
39
|
+
|
|
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
|
|
42
|
+
saved on the server (`data/onboarding.json`), so your other devices do not show it again. Reopen
|
|
43
|
+
it from Settings › About › **Setup guide**. On a phone that already opens wherdr through its
|
|
44
|
+
tailnet address, the phone step becomes Add to Home Screen.
|
|
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:
|
|
50
|
+
`localhost` stays valid on the computer. The installer, the Herdr plugin and `wherdr open` open
|
|
51
|
+
that address too when it is already published.
|
|
52
|
+
|
|
53
|
+
wherdr is a small self-hosted web app that talks to the Herdr server on your machine. **On a
|
|
54
|
+
computer**, it can replace the Herdr terminal client day to day: your spaces, tabs and split panes
|
|
55
|
+
side by side, live conversations and real terminals, drag-and-drop layout, keyboard shortcuts
|
|
56
|
+
and a task board for herdr-projects. **On a phone**, installed as a PWA, it is the companion
|
|
57
|
+
that follows your agents when you are away: push notifications when an agent finishes or needs
|
|
58
|
+
you, one-tap answers, quick replies and offline reading.
|
|
59
|
+
|
|
60
|
+
It lists your agents live — Claude Code, Codex and the other agents Herdr recognizes — and works
|
|
61
|
+
on the same Herdr session `herdr` attaches to: nothing is copied, nothing runs in the cloud. Use
|
|
62
|
+
it on `localhost`, or from your other devices over Tailscale.
|
|
63
|
+
|
|
64
|
+
> [!WARNING]
|
|
65
|
+
> **wherdr is a remote shell on your machine.** Anyone who can reach it can start agents and
|
|
66
|
+
> run commands as your user. **Never expose it on the public Internet** (no port forwarding, no
|
|
67
|
+
> public reverse proxy, no tunnel). Use it on `localhost`, or from your other devices through a
|
|
68
|
+
> private network such as [Tailscale](https://tailscale.com). wherdr refuses any other network
|
|
69
|
+
> by itself; the passkey lock is an optional extra. See [Security](security.md#security).
|
|
70
|
+
|
|
71
|
+
<p align="center">
|
|
72
|
+
<img src="screenshots/desktop.png" alt="wherdr on a computer: compact agent list with a project and its tasks, and a task's conversation waiting for an approval" width="900">
|
|
73
|
+
</p>
|
|
74
|
+
<p align="center"><sub>On a computer</sub></p>
|
|
75
|
+
<br>
|
|
76
|
+
<p align="center">
|
|
77
|
+
<img src="screenshots/board.png" alt="The Board: tasks of each project in Needs you, Ready to approve, Working and To do columns" width="900">
|
|
78
|
+
</p>
|
|
79
|
+
<p align="center"><sub>The Board: every task of every project, with <a href="board.md#works-great-with-herdr-projects">herdr-projects</a></sub></p>
|
|
80
|
+
<br>
|
|
81
|
+
<p align="center">
|
|
82
|
+
<img src="screenshots/home-phone.png" alt="Compact agent list on a phone: a project coordinator and its tasks" width="340">
|
|
83
|
+
|
|
84
|
+
<img src="screenshots/chat-phone.png" alt="A task's conversation on a phone, waiting for an approval" width="340">
|
|
85
|
+
</p>
|
|
86
|
+
<p align="center"><sub>On a phone: the same agents, the same session.</sub></p>
|
|
87
|
+
|
|
88
|
+
*Unofficial project. Not affiliated with or endorsed by Herdr, Anthropic or OpenAI. Claude,
|
|
89
|
+
Claude Code, Codex and other product names are trademarks of their respective owners.*
|
package/docs/quotas.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Claude and Codex quotas
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
- **Codex updates and limits**: in a Codex conversation, a new Codex version shows above the
|
|
6
|
+
field with an **Update** button (runs Codex's official update command on that machine, after
|
|
7
|
+
confirmation), then **Restart to update** on the same conversation. When wherdr cannot run it
|
|
8
|
+
(read-only home), Update exits Codex, runs the command in the agent's own terminal and
|
|
9
|
+
resumes the same conversation (only while Codex is idle). Codex's own weekly-limit warning
|
|
10
|
+
(its footer gauge, or its startup heads-up) is repeated above the field, with its values, while
|
|
11
|
+
Codex shows it.
|
|
12
|
+
- **After Codex updates itself and exits**: a **Your turn** card offers **Restart Codex**
|
|
13
|
+
for a new conversation, with the original launch arguments. For an existing conversation,
|
|
14
|
+
choose **Resume conversation** (its exact session, with the original options) or **Start
|
|
15
|
+
fresh**. If wherdr cannot identify the session, Resume opens Codex's picker rather than
|
|
16
|
+
choosing another pane's latest conversation. The usual attention notification applies.
|
|
17
|
+
Nothing restarts automatically: the pane must still show the same empty shell prompt,
|
|
18
|
+
with the shell in the foreground. Unrecognized shell prompts require a manual restart
|
|
19
|
+
in the terminal. wherdr must have observed Codex before it exited.
|
|
20
|
+
**Dismiss** puts the card away for good: the pane is a plain shell again, to use as you like.
|
|
21
|
+
Messages sent meanwhile wait in the queue until the agent returns; if it does not (Dismiss, or
|
|
22
|
+
a Codex that simply left), their text goes back into the pane's message field. Continuation
|
|
23
|
+
prompts (unfinished quotes or heredocs) refuse a restart too. If the final
|
|
24
|
+
transcript read fails, both conversation choices remain available; a session
|
|
25
|
+
contradicted by a guessed match is resumed through Codex's picker.
|
|
26
|
+
## Claude and Codex quotas
|
|
27
|
+
|
|
28
|
+
The home screen shows what is left of the Claude and Codex usage limits (5-hour window and
|
|
29
|
+
week, with reset times), for every online machine.
|
|
30
|
+
|
|
31
|
+
- **Codex**: nothing to do, Codex writes its limits in its session files. Machines on different
|
|
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).
|
|
34
|
+
- **Claude Code** only gives them to its status line. On each machine, install wherdr's
|
|
35
|
+
invisible status line (it displays nothing, and chains to your existing status line if you
|
|
36
|
+
have one):
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
sh scripts/install-claude-statusline.sh
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The app offers the same thing in **Settings › Agents › Claude quotas**: each machine with
|
|
43
|
+
Claude, its state (set up, installed, update available, not set up), **Install** or **Update**
|
|
44
|
+
(runs it over SSH on a remote machine) and **Copy command**. The setup guide has it as an
|
|
45
|
+
optional **Quotas** step, and the home list only adds **Set up Claude quotas** and **Don’t
|
|
46
|
+
suggest again** to the machine's **…** menu, never a banner. Skipped or declined, it is no
|
|
47
|
+
longer suggested on any device (`data/claude-quotas.json`); Settings still offers it. A machine
|
|
48
|
+
whose quotas show from an older status line reads **Update available**, never "not set up".
|
|
49
|
+
Quotas show after the next exchange with a Claude agent. The status line stores the limits in `~/.cache/herdr-web/claude-status.json`
|
|
50
|
+
and a short hash of the account ID (never a token or email) to tell accounts apart. The hash
|
|
51
|
+
and observation time are stored together with the limits in one atomic snapshot, so a
|
|
52
|
+
new login takes effect on the next status-line update. Machines sharing a known account
|
|
53
|
+
use its newest snapshot; an unknown identity never overrides a known account. A failed
|
|
54
|
+
Claude read is retried once;
|
|
55
|
+
if both attempts fail, its previous identity is not reused. This prevents an old
|
|
56
|
+
successful reading from masking a newer file indefinitely during SSH failures.
|
|
57
|
+
Failed attempts are logged with the machine key and error code, without account data.
|
|
58
|
+
After upgrading from the older status
|
|
59
|
+
line, rerun the install command on each machine (or use **Install**) and exchange a message
|
|
60
|
+
with Claude to produce the new snapshot. The status line also requires `python3` in Claude’s
|
|
61
|
+
`PATH` at runtime.
|
|
62
|
+
|
|
63
|
+
**New machine**: run `sh scripts/install-claude-statusline.sh` on it once (or **Install** in
|
|
64
|
+
Settings › Agents › Claude quotas), then send a message to any Claude agent there; its quotas
|
|
65
|
+
appear on the home screen.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# wherdr 2.0.0
|
|
2
|
+
|
|
3
|
+
A major upgrade from **1.3.4**, with the new work board and a native service.
|
|
4
|
+
|
|
5
|
+
> **Upgrading from 1.x? Back up first and read the Update section below.**
|
|
6
|
+
> wherdr 2.0 has no Docker image: on a 1.x Docker install, the suggested `docker compose pull`
|
|
7
|
+
> keeps 1.3.4. Follow the [Docker migration guide](https://github.com/afloury/wherdr/blob/v2.0.0/docs/migrate-from-1.x-docker.md) instead.
|
|
8
|
+
|
|
9
|
+
## New
|
|
10
|
+
|
|
11
|
+
Notifications use one default level and coordinator or agent exceptions, with a separate
|
|
12
|
+
sub-agent switch (#157). Task panels include read-only effective settings and their
|
|
13
|
+
provenance (#163). Start offers a model and effort for this run (#165); task keys can
|
|
14
|
+
be copied with a click or tap (#168). Tasks stopped at a limit offer Continue or a
|
|
15
|
+
higher limit (#167). Remote coordinator links select their machine (#169), and
|
|
16
|
+
restarting an agent keeps its terminal closed (#164).
|
|
17
|
+
|
|
18
|
+
The new Board works with herdr-projects 1.2.1 or later across your machines: create,
|
|
19
|
+
start, queue, stop, retry, approve and archive tasks, set dependencies, and talk to
|
|
20
|
+
project coordinators. Task panels bring together the conversation, terminal, review,
|
|
21
|
+
progress and actionable failures. Done is hidden by default and can be shown again.
|
|
22
|
+
|
|
23
|
+
Display settings follow you across devices through the server's `settings.json`,
|
|
24
|
+
including themes, machine groups, home list options, Board grouping and file browser
|
|
25
|
+
preferences. Changes reach connected devices immediately; offline changes are retried.
|
|
26
|
+
|
|
27
|
+
## Improved
|
|
28
|
+
|
|
29
|
+
The home screen starts with the normal, two-line task list. Machine groups and grouping
|
|
30
|
+
by state help you follow work across machines. The one-line Needs you banner offers
|
|
31
|
+
answers or permission actions and takes you to the question in the conversation.
|
|
32
|
+
The iPad layout stays stable with the keyboard open and has larger touch controls.
|
|
33
|
+
Files support formatted Markdown and video previews, and terminal mouse input uses
|
|
34
|
+
Herdr's structured events on supported versions. Slash commands include plugin skills
|
|
35
|
+
and MCP prompts. New can open or create a project coordinator.
|
|
36
|
+
|
|
37
|
+
Task logos distinguish workers, reviewers and fallbacks. Machine and group tabs restore
|
|
38
|
+
their last conversation, and task banners can send failed reviews back to the agent.
|
|
39
|
+
The bundled herdr-projects installer targets 1.2.21, while the Board still reads 1.2.1 or later.
|
|
40
|
+
|
|
41
|
+
The shorter README links to focused documentation pages. Startup instructions now
|
|
42
|
+
state the actual behavior: at login on macOS, at boot on Linux with lingering enabled,
|
|
43
|
+
otherwise at login. The installer reports which applies.
|
|
44
|
+
|
|
45
|
+
## Fixed
|
|
46
|
+
|
|
47
|
+
Messages removed from Claude's queue are reported as not sent with Retry. Messages
|
|
48
|
+
waiting behind a terminal menu explain why and offer Close menu or Send anyway.
|
|
49
|
+
Drafts follow an agent when its pane is replaced, rewritten transcripts reload correctly,
|
|
50
|
+
and Claude quota snapshots keep the account and observation together. Remote SSH
|
|
51
|
+
connections recover when an old connection leaves its control socket unusable.
|
|
52
|
+
Agent terminal copies (OSC 52) reach the browser clipboard when Herdr forwards them,
|
|
53
|
+
with a tap-to-copy fallback when a browser requires a gesture.
|
|
54
|
+
|
|
55
|
+
## Update
|
|
56
|
+
|
|
57
|
+
**This is a major upgrade from 1.x. Back up before updating.**
|
|
58
|
+
|
|
59
|
+
1. Back up wherdr's data directory and any herdr-projects 0.2 projects. Follow the
|
|
60
|
+
[herdr-projects migration guide](https://github.com/afloury/wherdr/blob/v2.0.0/docs/herdr-projects-1x.md) (#152) before running upstream 1.x:
|
|
61
|
+
it is a different program, and its first run moves the old directory aside.
|
|
62
|
+
wherdr 2.0.0 requires herdr-projects **1.2.1 or later** for the Board. The old Project
|
|
63
|
+
panel, TASKS.md, threads, inbox and routines integration are removed. Stay on
|
|
64
|
+
wherdr **1.3.4** if you still need herdr-projects 0.2.
|
|
65
|
+
2. **Docker is removed.** Move to the native service with the
|
|
66
|
+
[Docker migration guide](https://github.com/afloury/wherdr/blob/v2.0.0/docs/migrate-from-1.x-docker.md), keeping your port and data. You can hand that
|
|
67
|
+
guide to an AI agent: it lists checkable steps and the way back. The plugin switches its old Docker mode to native on
|
|
68
|
+
update and points to that guide. Existing 1.x images remain on GHCR for 1.x users.
|
|
69
|
+
3. Once 2.0.0 is published, update the native Herdr plugin with **U** in its panel or
|
|
70
|
+
rerun the installer. For npm, run `npm install -g wherdr@2.0.0 && wherdr restart`;
|
|
71
|
+
for Homebrew, run `brew upgrade wherdr && brew services restart wherdr`.
|
|
72
|
+
Requirements remain Linux or macOS, Node.js 22 or newer, and Herdr 0.9.1 or newer
|
|
73
|
+
(the herdr-projects migration guide uses Herdr 0.9.3 or newer).
|
|
74
|
+
4. Open each browser once. Its display settings fill only missing server keys; existing
|
|
75
|
+
server values win. After that, shared settings belong to the server. Keep
|
|
76
|
+
`DATA_DIR/settings.json` in your backups. Language, screen and terminal sizing,
|
|
77
|
+
drafts, notifications and authentication remain device-specific.
|
|
78
|
+
5. Compact list from 1.x resets to Normal once. If you prefer Compact, turn it on again
|
|
79
|
+
in Settings; that choice follows you to other devices. Check Settings and the Board
|
|
80
|
+
after updating. The migration guide also documents rollback.
|
|
81
|
+
|
|
82
|
+
PRs #153, #154, #155 and #71 are deferred to 2.0.1.
|
|
83
|
+
|
|
84
|
+
[Full changelog](https://github.com/afloury/wherdr/blob/v2.0.0/CHANGELOG.md)
|
package/docs/security.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
## Security
|
|
6
|
+
|
|
7
|
+
wherdr drives Herdr, so **it can run anything as your user**.
|
|
8
|
+
|
|
9
|
+
- It listens on `127.0.0.1` only. Reach it from other devices **only through a private
|
|
10
|
+
network** (Tailscale or [equivalent](networks.md#other-private-networks)). Never add a public route
|
|
11
|
+
(port forwarding, public reverse proxy, ngrok…); the only exception is a Cloudflare Tunnel
|
|
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).
|
|
14
|
+
- Requests are accepted only for `localhost`, loopback addresses, the runtime's hostname, the
|
|
15
|
+
hostname in `APP_URL`, the tailnet name `tailscale serve` publishes for wherdr's own port
|
|
16
|
+
(read from Tailscale on the machine, never from a request, and only while it is published),
|
|
17
|
+
and the runtime's network interface addresses. Add other private names or IP addresses to `HERDR_WEB_ALLOWED_HOSTS`
|
|
18
|
+
(comma-separated) before using them. An unlisted name returns HTTP 403 (`host` / "Host not
|
|
19
|
+
allowed"); add it to `HERDR_WEB_ALLOWED_HOSTS`, then restart wherdr. This host check also
|
|
20
|
+
applies to WebSockets.
|
|
21
|
+
- **Passkey lock** (Settings → Security, optional): once a passkey is registered, the API,
|
|
22
|
+
images, photos and WebSockets require an unlocked session (signed HttpOnly cookie). One rule
|
|
23
|
+
applies: the duration set in Settings → Security for every device, counted from the last
|
|
24
|
+
passkey unlock, used or not in between: 1 day, 7 days (default), 30 days, 90 days or 1 year.
|
|
25
|
+
There is no idle lock. Past it, the device asks for the passkey again at the next opening of
|
|
26
|
+
the app (launch, reload, or back in the foreground), never in the middle of use; the app warns
|
|
27
|
+
in the last hours. A client that never reopens is cut 12 hours after the deadline. A changed
|
|
28
|
+
duration applies to the sessions already open.
|
|
29
|
+
|
|
30
|
+
A network error or a server restart never locks the app. **Lock all devices** (Settings →
|
|
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.
|
|
35
|
+
- <a id="new-device"></a>**New device**: it can only get in with an existing passkey. Unlock it
|
|
36
|
+
with a passkey synced by your password manager (iCloud Keychain, Google Password Manager,
|
|
37
|
+
1Password…), or with the passkey of another device through the browser's "use a phone or
|
|
38
|
+
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.
|
|
41
|
+
- 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,
|
|
43
|
+
`no-cors` fetches, reported by the browser's `Sec-Fetch-Site`) are refused too.
|
|
44
|
+
- A strict Content Security Policy limits scripts to the app's own origin and hashed inline
|
|
45
|
+
scripts, and connections to that origin and its WebSockets. It also blocks embedded objects
|
|
46
|
+
and framing.
|
|
47
|
+
- wherdr never changes your agents' global settings: model and effort changes are for the
|
|
48
|
+
current session only.
|
|
49
|
+
|
|
50
|
+
See [SECURITY.md](../SECURITY.md) to report a vulnerability.
|
|
51
|
+
|
|
52
|
+
### Network access
|
|
53
|
+
|
|
54
|
+
wherdr answers **this computer and your tailnet only**. A device on any other network (a coffee shop
|
|
55
|
+
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`).
|
|
58
|
+
|
|
59
|
+
**Good practices**
|
|
60
|
+
|
|
61
|
+
- Prefer **Tailscale** (`tailscale serve`): a private HTTPS address, only for your devices.
|
|
62
|
+
- A network you add **without HTTPS** gets no passkey, no installed app and no notifications, and
|
|
63
|
+
any device on that network can control your agents.
|
|
64
|
+
- **Create a passkey** as soon as wherdr is reached over HTTPS.
|
|
65
|
+
|
|
66
|
+
How a request is judged, before anything else (WebSockets included):
|
|
67
|
+
|
|
68
|
+
- **Its TCP peer** (the address the connection comes from, not a header). A client cannot pick
|
|
69
|
+
it: a connection only opens once the client has received wherdr's reply at that address.
|
|
70
|
+
- *Loopback* (`127.0.0.0/8`, `::1`): this computer. The kernel drops packets that arrive from a
|
|
71
|
+
network with a loopback source.
|
|
72
|
+
- *Tailnet*: an address of `100.64.0.0/10` or `fd7a:115c:a1e0::/48` counts only when
|
|
73
|
+
Tailscale, running on this machine, lists it as one of its peers (or its own address). wherdr
|
|
74
|
+
reads that from Tailscale every 10 seconds (the CLI or Tailscale's socket); an
|
|
75
|
+
interface address in that range proves nothing, since a LAN may use it too. When Tailscale is
|
|
76
|
+
stopped, missing or unreadable for 30 seconds, no direct connection counts as tailnet.
|
|
77
|
+
- **Proxy headers**, only when the peer is this computer (a proxy on the machine, such as
|
|
78
|
+
`tailscale serve`): every address in `X-Forwarded-For`, `Forwarded`, `X-Real-IP`,
|
|
79
|
+
`CF-Connecting-IP` must be loopback, the tailnet or a trusted range too. They can only refuse
|
|
80
|
+
more: a client that writes its own headers directly only gets itself refused. A proxy that
|
|
81
|
+
adds the client's address (Caddy, nginx with `proxy_add_x_forwarded_for`, Cloudflare Tunnel)
|
|
82
|
+
therefore has its outside clients refused.
|
|
83
|
+
- **Tailscale Funnel**: wherdr reads Tailscale's serve configuration every 10 seconds, at most
|
|
84
|
+
2 seconds before an HTTP request, and again for every WebSocket it opens. Connections from this
|
|
85
|
+
computer stay local only while the latest reading **proves** that Funnel does not expose
|
|
86
|
+
wherdr's port, in any form (HTTPS, TCP, or TCP with TLS termination, which carry no client
|
|
87
|
+
address). A Funnel route counts unless its target clearly leads to another port: the host is
|
|
88
|
+
never compared (`0.0.0.0:7683`, `127.1:7683` or a LAN address reach wherdr too), and a target
|
|
89
|
+
wherdr cannot parse counts. Funnel routes to other ports do not count (Funnel for another
|
|
90
|
+
service is fine). While Funnel exposes wherdr, **every connection from this computer is
|
|
91
|
+
refused**, with a page that says how to turn it off: Funnel puts wherdr on the Internet. Once
|
|
92
|
+
seen, the exposure stays until a valid reading shows it gone: a failed, garbled or late reading
|
|
93
|
+
does not reopen wherdr. Open WebSockets are judged again after every reading and closed when no
|
|
94
|
+
longer allowed; between two readings (10 seconds at most), a connection opened before Funnel was
|
|
95
|
+
turned on can last that long. When Funnel was never seen, a failed or garbled reading proves
|
|
96
|
+
nothing: connections are served and the server log says that Funnel cannot be checked. A Funnel request with Tailscale's
|
|
97
|
+
`Tailscale-Funnel-Request` header is always refused.
|
|
98
|
+
- **The listening address**: wherdr refuses to start on anything else than
|
|
99
|
+
loopback or this machine's tailnet address as Tailscale reports it, with Tailscale running
|
|
100
|
+
(`HOST=0.0.0.0`, a LAN address), and says how to fix it.
|
|
101
|
+
|
|
102
|
+
> [!IMPORTANT]
|
|
103
|
+
> **A relay on this computer counts as this computer.** A reverse proxy or TCP forward you run
|
|
104
|
+
> on the machine (nginx without forwarded headers, `socat`, an SSH tunnel, a tunnel client) opens
|
|
105
|
+
> its connections from loopback, like your own browser, and may pass on headers its clients
|
|
106
|
+
> wrote. wherdr cannot tell them apart. If you run one, make sure it only listens on a private
|
|
107
|
+
> address, serve it over HTTPS and **create a passkey**.
|
|
108
|
+
|
|
109
|
+
`tailscale serve` (npm, Homebrew, the Herdr plugin) forwards to `127.0.0.1` with the
|
|
110
|
+
tailnet client's address in `X-Forwarded-For`: accepted, nothing to set up.
|
|
111
|
+
|
|
112
|
+
**Another private network** (ZeroTier, WireGuard, NetBird outside `100.64.0.0/10`): name its
|
|
113
|
+
range, `WHERDR_TRUSTED_NETWORKS=10.147.17.0/24` (comma-separated CIDR ranges or addresses), then
|
|
114
|
+
restart wherdr. Such devices get the whole app, Settings › Phone and the one-tap update
|
|
115
|
+
included, like the tailnet. `WHERDR_TRUSTED_NETWORKS=any` accepts every network: wherdr then
|
|
116
|
+
warns at startup and shows a warning that cannot be hidden in the app. Use it only when
|
|
117
|
+
something else protects the network (Cloudflare Access), and set up a passkey there too.
|
package/docs/terminal.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Terminal
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
- **Terminal view**: the real terminal (xterm.js, WebGL or DOM renderer) with take-over. Opening
|
|
6
|
+
it resizes the real pane to your screen (or its cell in a split); closing it gives the pane the size it had before
|
|
7
|
+
(after 30 seconds when the connection just dropped, e.g. a locked phone), unless a Herdr
|
|
8
|
+
client attached meanwhile laid it out its own way. On a
|
|
9
|
+
computer, drag over the text on screen to select it: releasing the button copies it, as do
|
|
10
|
+
`⌘C` / `Ctrl+Shift+C`. The history is browsed with the wheel, not by dragging past the edge.
|
|
11
|
+
Links (OSC 8 hyperlinks and plain `http(s)://` URLs, even wrapped over several rows) open in a
|
|
12
|
+
new tab with `⌘`+click / `Ctrl`+click, or a tap on a phone; only http and https open. A long
|
|
13
|
+
line wrapped by the width is copied in one piece, without the breaks between its rows. An image
|
|
14
|
+
pasted with `⌘V` (Mac), here or in a side-by-side terminal, goes to the agent's machine and its
|
|
15
|
+
path is pasted, which omp and Claude Code attach as an image, as in the conversation.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
## Troubleshooting
|
|
6
|
+
|
|
7
|
+
- **"The Herdr server is not responding"**: start Herdr on the server (`herdr` or
|
|
8
|
+
`herdr server`).
|
|
9
|
+
- **wherdr stops when you log out** (Linux): lingering is off. `wherdr doctor` says so; run
|
|
10
|
+
`sudo loginctl enable-linger $USER` once.
|
|
11
|
+
- **The Board says `claude is not installed here`**: the agent is in a folder your login shell
|
|
12
|
+
does not add to its `PATH`. Add it there, then run `wherdr service install` again
|
|
13
|
+
([Agent PATH](installation.md#agent-path)).
|
|
14
|
+
- **No conversation, only the terminal**: the agent was started before the Herdr integration
|
|
15
|
+
was installed, or it is not Claude Code / Codex / omp. Install the integration
|
|
16
|
+
(`herdr integration install claude|codex|omp`) and start a new agent. When the integration
|
|
17
|
+
is missing on the agent's machine, the conversation says so with the exact command.
|
|
18
|
+
- **Passkeys or notifications unavailable**: the page must be served over HTTPS (or
|
|
19
|
+
`localhost`). On iPhone, notifications need the app installed on the home screen.
|
|
20
|
+
- **HTTP 403 "wherdr only answers this computer and your tailnet"**: the device is on another
|
|
21
|
+
network (Wi-Fi, LAN). Open wherdr through Tailscale, or name that private network in
|
|
22
|
+
`WHERDR_TRUSTED_NETWORKS` ([Network access](security.md#network-access)).
|
|
23
|
+
- **HTTP 403 "Tailscale Funnel opens wherdr to the Internet"**: turn Funnel off for wherdr’s port (`tailscale funnel status`).
|
|
24
|
+
- **wherdr stops at startup with "Refusing to listen on …"**: `HOST` is not loopback nor this
|
|
25
|
+
machine's tailnet address. Set `HOST=127.0.0.1` and use `tailscale serve`, or see
|
|
26
|
+
[Network access](security.md#network-access).
|
|
27
|
+
- **HTTP 403 "This address isn't enabled yet"**: for a tailnet address, run the command the
|
|
28
|
+
page gives on the machine: it tells whether `tailscale serve` publishes wherdr's port there.
|
|
29
|
+
For another private name, add the hostname or IP address to `HERDR_WEB_ALLOWED_HOSTS`, or use
|
|
30
|
+
the hostname in `APP_URL`, then restart wherdr.
|
|
31
|
+
- **Remote machine offline**: `grep machine ~/wherdr/wherdr.log` shows its state. Test SSH:
|
|
32
|
+
`ssh -o BatchMode=yes laptop '~/.local/bin/herdr status server'`.
|
|
33
|
+
- `Could not resolve hostname`: check the full name in `HostName`;
|
|
34
|
+
- `Permission denied (publickey)` / `Host key verification failed`: key or `known_hosts`
|
|
35
|
+
(run `ssh laptop true` once on the server);
|
|
36
|
+
- `Bad owner or permissions`: `~/.ssh/config` must belong to you and not be group-writable.
|
|
37
|
+
- **Claude says "Not logged in" on a Mac** whose Herdr server was started over SSH: the macOS
|
|
38
|
+
keychain is locked in SSH sessions. Start the Herdr server from a terminal on the Mac.
|
|
39
|
+
- **`Session open refused by peer` / `Connection closed by UNKNOWN port 65535`** in the logs: the
|
|
40
|
+
shared SSH connection to a remote machine reached sshd's session limit (`MaxSessions`, 10 by
|
|
41
|
+
default). wherdr stays under it and retries, so this is rare; if it persists (many terminals
|
|
42
|
+
open at once), raise `MaxSessions` in the remote machine's `sshd_config`.
|
|
43
|
+
- **Quotas missing**: see [Claude and Codex quotas](quotas.md#claude-and-codex-quotas).
|
|
44
|
+
- **iPhone layout cut at the bottom** after changing the status bar style: reinstall the home
|
|
45
|
+
screen icon (iOS reads this setting at install time).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wherdr",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.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",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
".output",
|
|
27
27
|
"!.output/**/*.map",
|
|
28
28
|
"bin",
|
|
29
|
-
"skills/wherdr"
|
|
29
|
+
"skills/wherdr",
|
|
30
|
+
"docs/*.md"
|
|
30
31
|
],
|
|
31
32
|
"engines": {
|
|
32
33
|
"node": ">=22"
|
|
@@ -39,10 +40,15 @@
|
|
|
39
40
|
"typecheck": "nuxt typecheck",
|
|
40
41
|
"test": "vitest run",
|
|
41
42
|
"test:e2e": "playwright test",
|
|
43
|
+
"shots:readme": "node tools/readme-shots/capture.mjs",
|
|
44
|
+
"test:e2e:shots": "SHOTS=${SHOTS:-.shots} playwright test tests/e2e/question-reply.spec.ts --grep screenshot:",
|
|
45
|
+
"test:e2e:stability": "playwright test menu-responsiveness codex-self-update quote-mode onboarding-tailnet --grep \"MCP arrow paints|a new Codex restarts|the former per-device values|a quoted question keeps|#/setup[?]step=security opens|a direct Security link\"",
|
|
42
46
|
"prepare": "nuxt prepare",
|
|
43
47
|
"prepack": "nuxt build && node scripts/pack-output.mjs",
|
|
48
|
+
"check:docs": "node scripts/check-doc-links.mjs",
|
|
44
49
|
"check:leaks": "node scripts/check-leaks.mjs",
|
|
45
|
-
"hooks:install": "git config core.hooksPath .githooks"
|
|
50
|
+
"hooks:install": "git config core.hooksPath .githooks",
|
|
51
|
+
"test:affected": "node scripts/test-affected.mjs"
|
|
46
52
|
},
|
|
47
53
|
"dependencies": {
|
|
48
54
|
"qrcode-terminal": "^0.12.0"
|
|
@@ -60,6 +66,7 @@
|
|
|
60
66
|
"@types/node": "^22.0.0",
|
|
61
67
|
"@types/web-push": "^3.6.4",
|
|
62
68
|
"@vite-pwa/nuxt": "^1.1.1",
|
|
69
|
+
"@vue/reactivity": "^3.5.43",
|
|
63
70
|
"@vue/test-utils": "^2.5.1",
|
|
64
71
|
"@xterm/addon-fit": "^0.10.0",
|
|
65
72
|
"@xterm/addon-webgl": "^0.18.0",
|
|
@@ -68,6 +75,7 @@
|
|
|
68
75
|
"happy-dom": "^20.14.5",
|
|
69
76
|
"marked": "^15.0.12",
|
|
70
77
|
"nuxt": "^4.5.2",
|
|
78
|
+
"pdfjs-dist": "5.4.624",
|
|
71
79
|
"tailwindcss": "^4.3.3",
|
|
72
80
|
"typescript": "~5.9.3",
|
|
73
81
|
"vitest": "^4.1.11",
|