wherdr 1.3.4 → 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/{CJKsMzIp.js → BYZVrreW.js} +1 -1
- package/.output/public/_nuxt/BhgehRdJ.js +1 -0
- package/.output/public/_nuxt/BtH82ZuZ.js +9 -0
- package/.output/public/_nuxt/C3Bkn4ac.js +62 -0
- package/.output/public/_nuxt/CuUQ4mEf.js +1 -0
- package/.output/public/_nuxt/DFGEl0v2.js +23 -0
- package/.output/public/_nuxt/{DMLmW6JM.js → DFmgQVme.js} +41 -42
- 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/{w7Rm7unj.js → R0Y4VoeX.js} +1 -1
- 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/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 +44 -17
- package/.output/server/chunks/_/machineOrder.mjs +1 -1
- package/.output/server/chunks/_/onboarding.mjs +1 -1
- package/.output/server/chunks/_/package.mjs +9 -3
- package/.output/server/chunks/_/plugins.mjs +92 -125
- package/.output/server/chunks/_/restart.mjs +1 -1
- package/.output/server/chunks/_/selfupdate.mjs +1 -1
- 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 +14033 -10618
- 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 +20 -7
- package/.output/server/chunks/routes/api/choose.post.mjs +31 -17
- 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 +7 -6
- 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 -31
- 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 +7 -6
- 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 +7 -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 +7 -6
- 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 -10
- package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +6 -10
- package/.output/server/chunks/routes/api/plugins/projects.get.mjs +10 -9
- package/.output/server/chunks/routes/api/plugins/projects.post.mjs +11 -8
- 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 +7 -6
- package/.output/server/chunks/routes/api/screen.get.mjs +7 -6
- 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 +7 -6
- 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 -16
- 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 -14
- package/.output/server/chunks/routes/api/{index.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/{gitroot.get.mjs → tasks/board-settings.get.mjs} +12 -14
- 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 -12
- 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 -1445
- 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 +9 -3
- package/skills/wherdr/SKILL.md +24 -173
- package/.output/public/_nuxt/BEJHMUrj.js +0 -1
- package/.output/public/_nuxt/BOtcEDnA.js +0 -1
- package/.output/public/_nuxt/BUaYhQ2r.js +0 -6
- package/.output/public/_nuxt/CJNhn9sA.js +0 -36
- package/.output/public/_nuxt/COCvtdHN.js +0 -89
- package/.output/public/_nuxt/Cl45J6_Q.js +0 -1
- package/.output/public/_nuxt/CndPDbMI.js +0 -2
- package/.output/public/_nuxt/Ctbx58Um.js +0 -23
- package/.output/public/_nuxt/Dl1PHLOB.js +0 -1
- package/.output/public/_nuxt/Nhex3_iH.js +0 -1
- package/.output/public/_nuxt/OpWX45i7.js +0 -89
- package/.output/public/_nuxt/builds/meta/4994f3a4-4126-4f57-a9e5-c7ebb25850bc.json +0 -1
- package/.output/public/_nuxt/dTj21DMs.js +0 -1
- package/.output/public/_nuxt/entry.BohTWE-n.css +0 -2
- package/.output/public/_nuxt/kwSoIiM7.js +0 -1
- package/.output/public/_nuxt/mkEwYka4.js +0 -1
- package/.output/server/chunks/_/pluginPolicy.mjs +0 -93
- package/.output/server/chunks/_/projectBoard.mjs +0 -363
- 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 -30
- package/.output/server/chunks/_/projectsPlugin2.mjs +0 -57
- 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 -148
- package/.output/server/chunks/routes/api/plugins/thread-limits.post.mjs +0 -45
- package/.output/server/chunks/routes/api/project/inbox.get.mjs +0 -44
- package/.output/server/chunks/routes/api/project/inbox.post.mjs +0 -44
- package/.output/server/chunks/routes/api/project/memory.get.mjs +0 -43
- package/.output/server/chunks/routes/api/project/routines.get.mjs +0 -44
- package/.output/server/chunks/routes/api/project/thread.get.mjs +0 -45
- package/.output/server/chunks/routes/api/project/thread.post.mjs +0 -47
- package/bin/install-docker-skill.sh +0 -20
package/docs/machines.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Machines and SSH
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
- **Several machines**: agents of the SSH machines registered in Herdr (`herdr machine add`)
|
|
6
|
+
appear in their own section; everything works the same on a remote agent.
|
|
7
|
+
## Several machines (SSH)
|
|
8
|
+
|
|
9
|
+
wherdr also shows the agents of the **SSH machines registered in Herdr** — the ones the Herdr
|
|
10
|
+
terminal client shows in its sidebar. It opens one SSH master connection per machine, forwards
|
|
11
|
+
the remote Herdr socket through it, and reads transcripts and files over the same connection.
|
|
12
|
+
|
|
13
|
+
Requirements on the remote machine: the **same Herdr version** (`herdr --version`), its server
|
|
14
|
+
running, and key-based SSH access from the server. Example, on the server:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
ssh-keygen -t ed25519 -f ~/.ssh/wherdr_ed25519 -N ''
|
|
18
|
+
cat >> ~/.ssh/config <<'CFG'
|
|
19
|
+
Host laptop
|
|
20
|
+
HostName laptop
|
|
21
|
+
User alice
|
|
22
|
+
IdentityFile ~/.ssh/wherdr_ed25519
|
|
23
|
+
IdentitiesOnly yes
|
|
24
|
+
CFG
|
|
25
|
+
ssh-copy-id -i ~/.ssh/wherdr_ed25519 laptop
|
|
26
|
+
ssh laptop true # accept the host key once
|
|
27
|
+
herdr machine add laptop --label Laptop # see `herdr machine --help`
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
wherdr picks it up within 30 seconds; `herdr machine rename|disable|remove` work the same way.
|
|
31
|
+
Machines can also be renamed from the app (machine menu → Options).
|
|
32
|
+
With Herdr 0.9.2 or later, **Options → Connection status** checks the saved profile
|
|
33
|
+
with `herdr machine status --json`: reachable, authentication required, error or disabled.
|
|
34
|
+
It also gives a `herdr machine reconnect <profile-id>` command to run interactively
|
|
35
|
+
on the wherdr host. **Retry connection** restarts wherdr's own SSH connection;
|
|
36
|
+
Herdr's approved SSH connection and wherdr's connection are separate, so key-based
|
|
37
|
+
SSH access is still required for wherdr. Older Herdr keeps automatic retries and
|
|
38
|
+
manual **Retry connection**, without the native diagnostic.
|
|
39
|
+
|
|
40
|
+
The terminal view uses `terminal.mouse` on Herdr 0.9.2+ (both CLI and server): clicks,
|
|
41
|
+
touch taps, drags and movement are encoded by Herdr for the application's mouse mode.
|
|
42
|
+
Use **Shift + drag** to select text, and Ctrl/⌘+click to open links. Older or unknown
|
|
43
|
+
versions keep the existing terminal keyboard, selection and scrolling behavior.
|
|
44
|
+
|
|
45
|
+
### Windows remote machines
|
|
46
|
+
|
|
47
|
+
The wherdr host must support Unix sockets and OpenSSH multiplexing (for example,
|
|
48
|
+
Linux/WSL or macOS). On the **remote Windows machine**, install Git Bash and set
|
|
49
|
+
it as the OpenSSH server's default shell. In an elevated PowerShell:
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
New-ItemProperty -Path 'HKLM:\SOFTWARE\OpenSSH' -Name DefaultShell -Value 'C:\Program Files\Git\bin\bash.exe' -PropertyType String -Force
|
|
53
|
+
Restart-Service sshd
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Reconnect SSH after changing the shell. Adjust the path if Git is installed
|
|
57
|
+
elsewhere. wherdr's bootstrap and file commands require a POSIX shell; with
|
|
58
|
+
cmd.exe or PowerShell as the default, they fail before Herdr is discovered.
|
|
59
|
+
Localized Windows errors (including GBK output) can appear as unreadable text.
|
|
60
|
+
If the bootstrap does not produce its shell markers, wherdr shows a shell
|
|
61
|
+
configuration hint; this also covers other failures before the bootstrap runs.
|
|
62
|
+
|
|
63
|
+
Herdr must be available in this non-interactive shell (`ssh laptop 'command -v herdr'`) and its server must already be running. Windows remotes require Herdr
|
|
64
|
+
0.9.3 or later with `remote-api-bridge` support. wherdr selects a stdio relay for
|
|
65
|
+
socket paths containing a drive letter or backslashes, rather than putting the
|
|
66
|
+
Windows path into `ssh -L`. It uses Herdr's native `remote-api-bridge` for API
|
|
67
|
+
requests and `remote-client-bridge` for notifications, over the same SSH master.
|
|
68
|
+
No additional relay program or TCP listening port is needed. Linux and macOS
|
|
69
|
+
POSIX socket paths continue to use `ssh -L`.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Migrate a wherdr 1.x Docker install to wherdr 2.0
|
|
2
|
+
|
|
3
|
+
wherdr 2.0.0 has no Docker image: it runs natively, as your user, so the herdr-projects Board can act.
|
|
4
|
+
This guide moves a wherdr 1.x container to the native service. It keeps the port, the data
|
|
5
|
+
folder (passkeys, push subscriptions, phone address, settings) and therefore the address your
|
|
6
|
+
devices open.
|
|
7
|
+
|
|
8
|
+
**For an AI agent.** The user can say "read this file and migrate me". Run the steps in order,
|
|
9
|
+
on the machine where the `wherdr` container runs, as the user who runs Herdr (never with `sudo`
|
|
10
|
+
unless a step says so). Every step ends with a **Check**. When a check fails, stop, do not run
|
|
11
|
+
the next step, and report the output to the user. Run [Roll back](#roll-back) if the user asks,
|
|
12
|
+
or if wherdr no longer answers after step 4. Ask the user before deleting anything; this guide never deletes.
|
|
13
|
+
|
|
14
|
+
The 1.x in-app banner shows `docker compose pull && docker compose up -d` for 2.0. That
|
|
15
|
+
command cannot reach 2.0: the `wherdr:latest` image on GHCR stays on the last 1.x release.
|
|
16
|
+
Running it changes nothing. This guide is the way to 2.0.
|
|
17
|
+
|
|
18
|
+
## 0. Collect the facts
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
docker inspect wherdr --format '{{.State.Status}} {{.Config.Image}}'
|
|
22
|
+
OLD="$(docker inspect wherdr --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}')"
|
|
23
|
+
DATA_SRC="$(docker inspect wherdr --format '{{ range .Mounts }}{{ if eq .Destination "/data" }}{{ .Source }}{{ end }}{{ end }}')"
|
|
24
|
+
PORT="$(docker inspect wherdr --format '{{ range .Config.Env }}{{ println . }}{{ end }}' | sed -n 's/^PORT=//p')"
|
|
25
|
+
echo "OLD=$OLD DATA_SRC=$DATA_SRC PORT=${PORT:-7683}"
|
|
26
|
+
curl -fsS "http://127.0.0.1:${PORT:-7683}/api/health"
|
|
27
|
+
herdr --version
|
|
28
|
+
grep -s WHERDR_MODE ~/wherdr/plugin.env
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Check:** the container exists (`running`), `OLD` and `DATA_SRC` are absolute paths, `DATA_SRC`
|
|
32
|
+
holds files (`ls -la "$DATA_SRC"`), and the health answer says `"name":"wherdr"` with a `1.x`
|
|
33
|
+
version. Herdr must be 0.9.1 or newer. Linux or macOS only.
|
|
34
|
+
|
|
35
|
+
If the container has another name, use that name in every command. Keep `OLD`, `DATA_SRC` and
|
|
36
|
+
`PORT` for the next steps; in a new shell, set them again with the same commands.
|
|
37
|
+
If `~/wherdr/plugin.env` says `WHERDR_MODE=docker`, the Herdr plugin started this container: that is
|
|
38
|
+
expected, the plugin switches to native at step 4.
|
|
39
|
+
|
|
40
|
+
If the user also runs herdr-projects 0.2 (the Project panel, `TASKS.md`, threads), stop here
|
|
41
|
+
and tell them. wherdr 2.0's Board needs herdr-projects 1.2.1 or later; they must follow
|
|
42
|
+
[the herdr-projects migration guide](herdr-projects-1x.md) as well, or stay on wherdr 1.3.4.
|
|
43
|
+
|
|
44
|
+
## 1. Back up
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
BACKUP=~/wherdr-docker-backup-$(date +%F-%H%M).tgz
|
|
48
|
+
tar czf "$BACKUP" -C "$DATA_SRC" . && tar czf "$BACKUP.compose.tgz" -C "$OLD" $(cd "$OLD" && ls -A | grep -E '^(\.env|docker-compose.*\.ya?ml)$')
|
|
49
|
+
docker inspect wherdr --format '{{.Image}}' > "$BACKUP.image"
|
|
50
|
+
tar tzf "$BACKUP" | head; tar tzf "$BACKUP.compose.tgz"; cat "$BACKUP.image"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Check:** the first archive lists the data files (for example `./auth.json`, `./push.json`), the
|
|
54
|
+
second one lists `docker-compose.yml` (and `.env` when there is one), and the image file holds a
|
|
55
|
+
`sha256:` id. That image stays on the machine for the way back: do not prune it.
|
|
56
|
+
|
|
57
|
+
## 2. Keep the settings
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
cat "$OLD/.env" 2>/dev/null
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Keep the lines the user set, which the native service reads too: `APP_URL`, `HOST_LABEL`,
|
|
64
|
+
`HERDR_WEB_SESSION`, `HERDR_WEB_MACHINES`, `HERDR_WEB_SELF_HOSTS`, `HERDR_WEB_ALLOWED_HOSTS`,
|
|
65
|
+
`WHERDR_TRUSTED_NETWORKS`, `WHERDR_UPDATE_CHECK`, `PASSKEY_USER`,
|
|
66
|
+
`AGENT_KINDS`, `TZ`. Drop the container's own: `PORT` (it becomes `WHERDR_PORT` at step 4),
|
|
67
|
+
`HOST_HOME`, `HOME`, `PUID`, `PGID`, `DATA_DIR`, `HERDR_BIN`, `WHERDR_IMAGE`,
|
|
68
|
+
`WHERDR_DOCKER_LOOPBACK_ONLY`, `WHERDR_TASKS_FROM_FILES`, `HERDR_WEB_HOST_OPEN_*`.
|
|
69
|
+
Show the user the kept lines before writing them at step 3.
|
|
70
|
+
|
|
71
|
+
**Check:** the user agrees with the list (it may be empty).
|
|
72
|
+
|
|
73
|
+
## 3. Stop the container and put the data in place
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
(cd "$OLD" && docker compose down)
|
|
77
|
+
docker ps -a --filter name='^wherdr$' --format '{{.Names}}'
|
|
78
|
+
curl -fsS --max-time 3 "http://127.0.0.1:${PORT:-7683}/api/health" || echo "port free"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Check:** no container is listed and the last line says `port free`. `docker compose down`
|
|
82
|
+
removes the container only; its folder, data and image stay.
|
|
83
|
+
|
|
84
|
+
The native service reads `~/wherdr/data`. When `DATA_SRC` is already `~/wherdr/data` (installs
|
|
85
|
+
made by the 1.x installer or plugin), leave it. Otherwise copy it; the command refuses when the
|
|
86
|
+
target exists (then show the user what `~/wherdr/data` holds and ask):
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
mkdir -p ~/wherdr
|
|
90
|
+
[ "$(cd "$DATA_SRC" && pwd -P)" = "$(cd ~/wherdr 2>/dev/null && pwd -P)/data" ] || { [ ! -e ~/wherdr/data ] && cp -a "$DATA_SRC" ~/wherdr/data; }
|
|
91
|
+
find ~/wherdr/data ! -user "$(id -u)" | head
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Check:** `ls ~/wherdr/data` shows the same files as `DATA_SRC`, and `find` prints nothing. If it
|
|
95
|
+
lists files owned by someone else (the container ran with another `PUID`), ask the user to run
|
|
96
|
+
`sudo chown -R "$(id -un)" ~/wherdr/data`.
|
|
97
|
+
|
|
98
|
+
Write the settings kept at step 2, one `KEY=VALUE` per line, in `~/wherdr/wherdr.env` (add them
|
|
99
|
+
to the file if it exists; never overwrite other lines).
|
|
100
|
+
|
|
101
|
+
## 4. Install the native service
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
curl -fsSL https://wherdr.dev/install | WHERDR_PORT="${PORT:-7683}" sh
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The installer asks before installing Node.js 22 when it is missing; let the user answer
|
|
108
|
+
(`WHERDR_YES=1` answers yes to everything). It installs the Herdr plugin into `~/wherdr`, starts
|
|
109
|
+
wherdr on that port and installs its login service: `systemd --user` (`wherdr.service`) on Linux,
|
|
110
|
+
a LaunchAgent (`dev.wherdr.plist`) on macOS. A plugin left in Docker mode says
|
|
111
|
+
`switching this plugin to the native service`.
|
|
112
|
+
|
|
113
|
+
**Check:** the installer ends without an error and prints the wherdr address.
|
|
114
|
+
|
|
115
|
+
## 5. Verify
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
curl -fsS "http://127.0.0.1:${PORT:-7683}/api/health"
|
|
119
|
+
grep -s WHERDR_MODE ~/wherdr/plugin.env || echo "no Docker mode"
|
|
120
|
+
systemctl --user is-active wherdr 2>/dev/null || launchctl list 2>/dev/null | grep dev.wherdr
|
|
121
|
+
docker ps -a --filter name='^wherdr$' --format '{{.Names}}'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Check:** health says `"name":"wherdr"` with a `2.x` version; `no Docker mode`; the service is
|
|
125
|
+
`active` (Linux) or listed (macOS); no container came back. If wherdr was reached over Tailscale,
|
|
126
|
+
`tailscale serve status` still points to the same port.
|
|
127
|
+
|
|
128
|
+
Then ask the user to open wherdr at the address they used before, on the phone and on each
|
|
129
|
+
browser. It must unlock with the same passkey, and notifications must still arrive. Each browser
|
|
130
|
+
copies its display settings to the server once, at its first visit (they are then shared).
|
|
131
|
+
If something fails here, go to [Roll back](#roll-back).
|
|
132
|
+
|
|
133
|
+
Leave the backup, the old compose folder and the image in place for a few days. Remove them later
|
|
134
|
+
only when the user asks (`docker image rm <id from $BACKUP.image>`).
|
|
135
|
+
|
|
136
|
+
## Roll back
|
|
137
|
+
|
|
138
|
+
Back to the 1.x container, with the data as it is now:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
herdr plugin disable afloury.wherdr
|
|
142
|
+
systemctl --user disable --now wherdr 2>/dev/null || launchctl bootout "gui/$(id -u)/dev.wherdr" 2>/dev/null
|
|
143
|
+
curl -fsS --max-time 3 "http://127.0.0.1:${PORT:-7683}/api/health" || echo "port free"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
When the data was copied at step 3, copy back what changed since (skip this when `DATA_SRC` is
|
|
147
|
+
`~/wherdr/data`): `cp -a ~/wherdr/data/. "$DATA_SRC/"`. To return to the data as it was before
|
|
148
|
+
the migration instead, restore the backup: `tar xzf "$BACKUP" -C "$DATA_SRC"`.
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
(cd "$OLD" && docker compose up -d)
|
|
152
|
+
curl -fsS "http://127.0.0.1:${PORT:-7683}/api/health"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Check:** `port free` before starting, then health answers with the `1.x` version. If
|
|
156
|
+
`docker compose up` pulls a different image, pin the saved one: `WHERDR_IMAGE=<id from $BACKUP.image>`
|
|
157
|
+
in `"$OLD/.env"`, then run it again.
|
|
158
|
+
|
|
159
|
+
To stop the 1.x banner from offering 2.0, set `WHERDR_UPDATE_CHECK=off` in `"$OLD/.env"` and run
|
|
160
|
+
`docker compose up -d` again.
|
package/docs/networks.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Phone access and private networks
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
### Recommended: always-on server + Tailscale
|
|
6
|
+
|
|
7
|
+
Your agents keep running when your laptop is closed, and you follow them from your phone.
|
|
8
|
+
wherdr stays on your private network: it is **never published on the Internet**.
|
|
9
|
+
|
|
10
|
+
**1. Herdr on the server.** Install [Herdr](https://herdr.dev) (≥ 0.9.1) and start it
|
|
11
|
+
(`herdr` or `herdr server`), with the integrations of the agents you use
|
|
12
|
+
(`herdr integration install claude|codex`).
|
|
13
|
+
|
|
14
|
+
**2. wherdr.** Run [the one command](installation.md#the-one-command) on the server, as your user (over SSH, it
|
|
15
|
+
prints the address and the phone steps instead of opening a browser). wherdr runs as a service
|
|
16
|
+
that starts with the machine and keeps running when you log out.
|
|
17
|
+
|
|
18
|
+
**3. Private HTTPS with Tailscale.** Install [Tailscale](https://tailscale.com) on the server,
|
|
19
|
+
your phone and your computers, on the same tailnet, with
|
|
20
|
+
[MagicDNS and HTTPS certificates](https://tailscale.com/kb/1153/enabling-https) enabled. Then,
|
|
21
|
+
on the server:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
tailscale serve --bg --https=7683 http://127.0.0.1:7683
|
|
25
|
+
# → https://<machine>.<tailnet>.ts.net:7683/ (e.g. https://server.example.ts.net:7683/)
|
|
26
|
+
```
|
|
27
|
+
|
|
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.
|
|
30
|
+
|
|
31
|
+
**wherdr enables that address by itself**, on a server without a screen too: at startup, every
|
|
32
|
+
30 seconds, and when an address it does not know yet is opened, it reads what `tailscale serve`
|
|
33
|
+
publishes on this machine (the `tailscale` command) and adopts the HTTPS address that points to
|
|
34
|
+
its own port (allowed host and `APP_URL`, saved in `data/app-url.json`; an HTTPS `APP_URL` in the
|
|
35
|
+
environment or `~/wherdr/wherdr.env` wins). An address removed from `tailscale serve` is refused
|
|
36
|
+
again. Nothing a request says can enable an address: only Tailscale's own state on the machine
|
|
37
|
+
does, and an address that `tailscale funnel` opens to the Internet is never adopted.
|
|
38
|
+
|
|
39
|
+
**Settings › Phone** on the server (`http://localhost:7683/#/settings?section=phone`) shows the
|
|
40
|
+
state and the QR code and publishes wherdr itself: one button, asked first. From a terminal,
|
|
41
|
+
**P** in the wherdr panel, `wherdr phone` (npm, Homebrew) or the [one command](installation.md#the-one-command)
|
|
42
|
+
run again checks the same thing, and `curl -fsS http://127.0.0.1:7683/api/phone` prints the state
|
|
43
|
+
as JSON.
|
|
44
|
+
|
|
45
|
+
If the phone shows **"This address isn't enabled yet"** (403), the address it opened is not one
|
|
46
|
+
`tailscale serve` publishes for wherdr's port on that machine: the page gives the command to run
|
|
47
|
+
there.
|
|
48
|
+
|
|
49
|
+
**Why HTTPS matters.** Browsers only allow three things on `https://` (or on `localhost` itself):
|
|
50
|
+
|
|
51
|
+
- **installing the app** on the home screen (PWA) on iPhone and Android;
|
|
52
|
+
- **push notifications** (on iOS, only in the installed app);
|
|
53
|
+
- **passkeys** for the lock screen (Face ID, Touch ID, Windows Hello, Android).
|
|
54
|
+
|
|
55
|
+
**4. On the phone:** open the address, [install the app](phone.md#install-it-as-an-app), then
|
|
56
|
+
Settings → **Enable notifications**. **On the computer:** use the same address in a browser; the
|
|
57
|
+
desktop layout can replace the Herdr terminal client for daily work.
|
|
58
|
+
|
|
59
|
+
**5. Passkey lock** (optional): Settings → Security → **Enable passkey lock**. The
|
|
60
|
+
first passkey asks for a bootstrap token, printed in the server log (`~/wherdr/wherdr.log`,
|
|
61
|
+
**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
|
+
|
|
66
|
+
**6. More machines** (optional): register your other computers in Herdr over SSH
|
|
67
|
+
(`herdr machine add`, see [Several machines (SSH)](machines.md#several-machines-ssh)). Their agents show
|
|
68
|
+
in wherdr in their own section, and everything works the same on them. The machine menu also has
|
|
69
|
+
**Keep awake**, which stops a Mac (`caffeinate`) or a Linux machine (`systemd-inhibit`) from
|
|
70
|
+
sleeping for an hour, four hours, the evening or until turned off.
|
|
71
|
+
|
|
72
|
+
> [!CAUTION]
|
|
73
|
+
> Never publish wherdr on the Internet: no port forwarding on your router, no public reverse
|
|
74
|
+
> proxy, no ngrok, no `tailscale funnel`, no Cloudflare Tunnel without Cloudflare Access. Use
|
|
75
|
+
> `tailscale serve`, which stays inside your tailnet. See [Security](security.md#security).
|
|
76
|
+
|
|
77
|
+
### Other private networks
|
|
78
|
+
|
|
79
|
+
Tailscale is the built-in path (one click in **Settings › Phone**), not a requirement. wherdr
|
|
80
|
+
works with any private network that gives it an **HTTPS address with a valid certificate**:
|
|
81
|
+
|
|
82
|
+
- **HTTPS with a certificate your phone trusts.** The installed app (PWA), push notifications
|
|
83
|
+
and passkeys only work on `https://`. A self-signed certificate or plain `http://` on your
|
|
84
|
+
LAN is not enough, and is never a supported way to reach wherdr.
|
|
85
|
+
- **`APP_URL` set to that address** (the environment of `wherdr` / `npm start`), then restart wherdr. Its hostname is allowed automatically and it enables Web
|
|
86
|
+
Push. Add `HERDR_WEB_ALLOWED_HOSTS` only if you also open wherdr under another name. The
|
|
87
|
+
automatic adoption and the address check in **Settings › Phone** only know the `*.ts.net`
|
|
88
|
+
addresses of `tailscale serve`; an HTTPS `APP_URL` in the environment always wins.
|
|
89
|
+
- **Never exposed without authentication.** wherdr keeps listening on `127.0.0.1`; only the
|
|
90
|
+
proxy listens on the private network. Turn on the passkey lock as soon as another device can
|
|
91
|
+
reach it.
|
|
92
|
+
|
|
93
|
+
**Headscale, NetBird, ZeroTier, WireGuard: the same recipe.** Join the server and the phone to
|
|
94
|
+
the network, give the server a DNS name that points to its **private** address, and put a reverse
|
|
95
|
+
proxy in front of wherdr that listens on that private address only. Let's Encrypt cannot reach a
|
|
96
|
+
private address, so the certificate comes from the
|
|
97
|
+
[DNS challenge](https://caddyserver.com/docs/automatic-https#dns-challenge). With
|
|
98
|
+
[Caddy](https://caddyserver.com) built with your DNS provider's
|
|
99
|
+
[module](https://github.com/caddy-dns) (here [Cloudflare DNS](https://github.com/caddy-dns/cloudflare)),
|
|
100
|
+
a `Caddyfile` like this ([`bind`](https://caddyserver.com/docs/caddyfile/directives/bind),
|
|
101
|
+
[`tls`](https://caddyserver.com/docs/caddyfile/directives/tls)):
|
|
102
|
+
|
|
103
|
+
```caddy
|
|
104
|
+
wherdr.example.com {
|
|
105
|
+
bind 100.64.0.10 # the server's address on the private network only
|
|
106
|
+
tls {
|
|
107
|
+
dns cloudflare {env.CF_API_TOKEN}
|
|
108
|
+
}
|
|
109
|
+
reverse_proxy 127.0.0.1:7683
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Point `wherdr.example.com` (an `A` record in your DNS zone) to that private address: from
|
|
114
|
+
anywhere else it leads nowhere. Then set `APP_URL=https://wherdr.example.com/` and restart
|
|
115
|
+
wherdr. Caddy is a relay on this computer ([Network access](security.md#network-access)): keep it bound to
|
|
116
|
+
the private address and turn the passkey lock on. Caddy passes each client's address on: with
|
|
117
|
+
Headscale or NetBird (`100.64.0.0/10`) wherdr accepts it as is; for another range (ZeroTier,
|
|
118
|
+
WireGuard), also set `WHERDR_TRUSTED_NETWORKS` to it, such as `10.147.17.0/24`. To join each
|
|
119
|
+
device:
|
|
120
|
+
|
|
121
|
+
| Network | Join a device (official docs) | Server's private address |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| [Headscale](https://headscale.net/stable/usage/getting-started/) | Tailscale clients: `tailscale up --login-server <YOUR_HEADSCALE_URL>` | `tailscale ip -4` |
|
|
124
|
+
| [NetBird](https://docs.netbird.io/get-started/cli) | `netbird up` (`--management-url` when self-hosted) | `netbird status` |
|
|
125
|
+
| [ZeroTier](https://docs.zerotier.com/start) | `zerotier-cli join <network ID>`, then authorize the device in the network's members | The members list of your network |
|
|
126
|
+
| [WireGuard](https://www.wireguard.com/quickstart/) | `wg-quick up wg0` with a peer per device | The `Address` of the server's interface |
|
|
127
|
+
|
|
128
|
+
Headscale does not support `tailscale serve` with HTTPS yet
|
|
129
|
+
([features](https://headscale.net/stable/about/features/),
|
|
130
|
+
[#1921](https://github.com/juanfont/headscale/issues/1921)): `tailscale cert` and Serve need
|
|
131
|
+
certificates that only Tailscale's own control server provisions. Use the reverse proxy above
|
|
132
|
+
instead of step 3.
|
|
133
|
+
|
|
134
|
+
**Cloudflare Tunnel + Cloudflare Access: only with Access.**
|
|
135
|
+
|
|
136
|
+
> [!WARNING]
|
|
137
|
+
> A Cloudflare Tunnel puts wherdr **on the Internet**: anyone can reach its address, and
|
|
138
|
+
> Cloudflare terminates TLS, so it sees all traffic. **Cloudflare Access is mandatory**, created
|
|
139
|
+
> *before* the route: without it, whoever finds the address controls your agents. Keep the
|
|
140
|
+
> passkey lock on as well. Prefer a private network when you can.
|
|
141
|
+
|
|
142
|
+
1. In Cloudflare One, create the Access application first:
|
|
143
|
+
[Self-hosted and private › Add public hostname](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/)
|
|
144
|
+
`wherdr.example.com`, with an **Allow** policy for your own e-mail only (Access applications
|
|
145
|
+
deny everyone else by default).
|
|
146
|
+
2. [Create a tunnel](https://developers.cloudflare.com/tunnel/setup/) and run `cloudflared` on the
|
|
147
|
+
server, then add a **published application** route: hostname `wherdr.example.com`, service
|
|
148
|
+
`http://localhost:7683`.
|
|
149
|
+
3. Set `APP_URL=https://wherdr.example.com/` and `WHERDR_TRUSTED_NETWORKS=any` (the tunnel brings
|
|
150
|
+
clients from the Internet, which wherdr refuses otherwise; it then shows a warning), and
|
|
151
|
+
restart wherdr.
|
|
152
|
+
|
|
153
|
+
Installing the app and notifications behind Access are not tested by the project: the phone must
|
|
154
|
+
first sign in to Access in the browser.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Notifications
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
## Notifications
|
|
6
|
+
|
|
7
|
+
With notifications enabled on a device, wherdr sends a Web Push when an agent goes from working
|
|
8
|
+
to **blocked** (with its question and options) or to **done** (with the start of its answer).
|
|
9
|
+
There is no push for the agent currently on screen.
|
|
10
|
+
|
|
11
|
+
**Do not disturb** (top of Settings → Notifications) stops every push, for this device or for all
|
|
12
|
+
devices, for 1 hour, until 8 am or until turned back on. The server applies it before sending and
|
|
13
|
+
keeps it across restarts (`data/quiet.json` for all devices). While it is on, the agent list
|
|
14
|
+
shows one line, “Notifications silenced until 08:00”, that opens these settings (a crossed-out bell
|
|
15
|
+
under the counters when the list is collapsed); the header keeps its buttons as they are.
|
|
16
|
+
|
|
17
|
+
wherdr also relays custom notifications sent with `herdr notification show` (plugins, scripts).
|
|
18
|
+
To receive them it connects to Herdr's client socket as a **passive** client: it never becomes
|
|
19
|
+
the foreground client and never resizes anything. Side effects: with wherdr connected,
|
|
20
|
+
`notification show` reports `shown: true` even with no terminal attached, and an empty session
|
|
21
|
+
gets a default workspace, as with any attached client.
|
|
22
|
+
|
|
23
|
+
## A default and exceptions
|
|
24
|
+
|
|
25
|
+
Settings → Notifications stores one policy, synchronized across devices
|
|
26
|
+
(`notificationRules` in `settings.json`): **a default for everything, and
|
|
27
|
+
exceptions to that default**. Every setting uses the same scale:
|
|
28
|
+
|
|
29
|
+
| Level | Notifies for |
|
|
30
|
+
|---|---|
|
|
31
|
+
| **Muted** | nothing |
|
|
32
|
+
| **Needs you** | a task or a coordinator waiting for you (questions, permission requests, board approvals) |
|
|
33
|
+
| **Needs you + finished** | also a finished task, or a coordinator that finished answering you |
|
|
34
|
+
| **All activity** | also every intermediate message and scheduled tasks |
|
|
35
|
+
|
|
36
|
+
Under each level, **Customize** keeps the individual events (needs you, finished,
|
|
37
|
+
new message, scheduled task) for edge cases.
|
|
38
|
+
|
|
39
|
+
The screen reads top to bottom:
|
|
40
|
+
|
|
41
|
+
1. **Do not disturb** turns off every notification, no exceptions: the default and
|
|
42
|
+
the exceptions are ignored while it is on.
|
|
43
|
+
2. **Default (applies to everything)**: a level (needs you + finished at first)
|
|
44
|
+
and a **Sub-agents** switch. Off, only coordinators, standalone agents and board
|
|
45
|
+
events notify; on, task agents (workers, reviewers, planners, attempts) notify
|
|
46
|
+
too, at the same level. Sub-agents are all on or all off, never task by task.
|
|
47
|
+
3. **Exceptions**: **Add an exception** picks a coordinator (main, machine or project
|
|
48
|
+
coordinator, grouped by machine) or a standalone agent conversation. Each one has
|
|
49
|
+
its own level, Customize and, for a coordinator, a Sub-agents switch: on, the task
|
|
50
|
+
agents of its board follow the exception instead of the default (a project
|
|
51
|
+
coordinator covers its project's tasks; the main and machine coordinators cover the
|
|
52
|
+
others). Each says how it differs from the default; removing one goes back to it.
|
|
53
|
+
4. **This device's notifications** only asks this browser or phone for permission to
|
|
54
|
+
show push notifications, and shows whether it is allowed, blocked or not asked yet.
|
|
55
|
+
|
|
56
|
+
A coordinator's exception is kept by its identity (machine, Herdr session, level and
|
|
57
|
+
repo), not its pane: it still applies after `coordinator new` or a restart.
|
|
58
|
+
|
|
59
|
+
Right-click → **Notifications** on a coordinator or a standalone agent sets that
|
|
60
|
+
conversation's exception: the four levels (with a check mark on the current one, and
|
|
61
|
+
whether it is an exception or the default), **Mute for 1 hour / 8 hours** (then back
|
|
62
|
+
to what it was), and **Follow the default** to remove the exception. On a task agent,
|
|
63
|
+
the menu points to the Sub-agents switch instead.
|
|
64
|
+
|
|
65
|
+
Board events (`herdr-projects` approvals and failures) follow the default even with
|
|
66
|
+
Sub-agents off, or their project coordinator's exception in the task's own Herdr
|
|
67
|
+
session (the default when that session is not known: a task with no pane or space linked). Custom `herdr notification
|
|
68
|
+
show` notices with a pane follow that conversation; others follow the default. Scripts
|
|
69
|
+
can mark a scheduled notice with a `[scheduled]` title prefix. New assistant messages
|
|
70
|
+
are identified by their transcript position, so streaming updates do not ring
|
|
71
|
+
repeatedly.
|
|
72
|
+
|
|
73
|
+
Settings saved by the earlier machine → project → conversation rules are converted
|
|
74
|
+
once: a permanent machine rule becomes the default (so "Only main coordinators"
|
|
75
|
+
becomes default Muted plus one exception), project rules are dropped, and
|
|
76
|
+
conversation rules become exceptions (moved to the coordinator's identity when the
|
|
77
|
+
conversation is a coordinator).
|
|
78
|
+
|
|
79
|
+
An open conversation suppresses push to **every** device while the page is visible,
|
|
80
|
+
focused and recently used (within two minutes). A WebSocket heartbeat renews a
|
|
81
|
+
30-second lease every 10 seconds. Hiding, closing, losing the connection or leaving
|
|
82
|
+
an idle tab stops renewing it; push resumes when the last lease expires. The global
|
|
83
|
+
Do not disturb setting and notification deduplication still apply.
|
package/docs/overview.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Overview and architecture
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="../server/assets/branding/icons/icon-512.png" alt="wherdr logo" width="120">
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<h1 align="center">wherdr</h1>
|
|
10
|
+
|
|
11
|
+
<p align="center"><b>Start your <a href="https://herdr.dev">Herdr</a> agents on your computer.
|
|
12
|
+
Follow their work and answer from your phone.</b></p>
|
|
13
|
+
|
|
14
|
+
<p align="center">Add it to your home screen like an app — no App Store needed.</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="../assets/wherdr-launch.mp4"><img src="../assets/wherdr-launch.webp" width="720" alt="Animated wherdr launch film with real app captures: launch a Claude agent on desktop, continue the same conversation on a phone, approve its test request, then open project tasks and the Board."></a>
|
|
18
|
+
</p>
|
|
19
|
+
<p align="center"><sub>Start on PC. Stay in control from your phone. Real app, demo data. <a href="../assets/wherdr-launch.mp4">Watch the 15-second video</a>.</sub></p>
|
|
20
|
+
|
|
21
|
+
<p align="center"><a href="https://wherdr.dev"><b>wherdr.dev</b></a> · <a href="quick-start.md#ask-your-ai-agent">Ask your AI agent</a></p>
|
|
22
|
+
|
|
23
|
+
<p align="center"><a href="https://wherdr.dev/demo/"><b>Try it in your browser — no install</b></a>: the real app on scripted agents, nothing runs.</p>
|
|
24
|
+
|
|
25
|
+
<p align="center"><a href="https://github.com/afloury/wherdr/actions/workflows/ci.yml"><img src="https://github.com/afloury/wherdr/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a></p>
|
|
26
|
+
|
|
27
|
+
## Why wherdr
|
|
28
|
+
|
|
29
|
+
- **Herdr's splits, live.** A tab with several panes is drawn in Herdr's real layout, every pane
|
|
30
|
+
live; drag a pane to move it or a divider to resize, and the change goes to Herdr itself.
|
|
31
|
+
- **Private by default, passkey lock optional.** wherdr answers only its own computer and your
|
|
32
|
+
tailnet. Add a passkey if you like: Face ID, Touch ID, Windows Hello or Android unlock the app,
|
|
33
|
+
and nothing about your agents is served to a locked session.
|
|
34
|
+
- **The agent's diff.** Git status and diff of each agent's working folder, one click away from
|
|
35
|
+
its conversation.
|
|
36
|
+
- **The herdr-projects board.** Every machine's herdr-projects tasks in the board's own groups,
|
|
37
|
+
with replies to their agents, approval of finished work and a box for the Coordinator.
|
|
38
|
+
- **Keyboard and global search.** One shortcut searches every agent and conversation; next
|
|
39
|
+
agent, new tab, pane swaps and `1`–`9` answers never need the mouse.
|
|
40
|
+
- **Real time, and private.** The real terminal over a WebSocket, a push when an agent needs
|
|
41
|
+
you, and your phone through Tailscale: nothing is on the Internet.
|
|
42
|
+
|
|
43
|
+
Details in [Features](home.md#features) and [Security](security.md#security).
|
|
44
|
+
|
|
45
|
+
## How it works
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
phone / laptop ──private network──> HTTPS (tailscale serve, private proxy)
|
|
49
|
+
│
|
|
50
|
+
▼
|
|
51
|
+
wherdr (Nitro server, 127.0.0.1:7683)
|
|
52
|
+
├─ Herdr API socket ~/.config/herdr/herdr.sock
|
|
53
|
+
├─ Herdr client socket (notifications, passive client)
|
|
54
|
+
├─ herdr terminal session control <pane> (terminal view)
|
|
55
|
+
├─ transcripts ~/.claude, ~/.codex, ~/.omp (read only)
|
|
56
|
+
└─ ssh -M <machine> (other machines, optional)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
wherdr runs on the same machine as the Herdr server (the "server" in these guides). It uses Herdr's local
|
|
60
|
+
socket API and CLI, reads the agents' transcript files, and serves a client-only Nuxt app. Data
|
|
61
|
+
it keeps (VAPID keys, push subscriptions, passkeys, recent folders) lives in `data/`. So do the
|
|
62
|
+
long texts pasted into the message field, which the server keeps to show them as cards on every
|
|
63
|
+
device: `data/pastes.json`, in clear, readable by the server's user only (mode 600), at most the
|
|
64
|
+
last 100 texts and 2 MB, each one deleted 30 days after it was last sent.
|
|
65
|
+
|
|
66
|
+
Built with Nuxt 4, Nuxt UI 4, Tailwind 4 and TypeScript; the Herdr gateway (API, WebSockets,
|
|
67
|
+
Web Push, lock) runs in Nitro.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
[MIT](../LICENSE). The embedded font subsets keep their own licenses (`app/assets/fonts/`).
|
package/docs/phone.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Install as an app
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
## Install it as an app
|
|
6
|
+
|
|
7
|
+
For phone access, first configure [private HTTPS](networks.md). For UI development, see the
|
|
8
|
+
[iOS conventions](../CONTRIBUTING.md#conventions), including the iOS 26 status-bar pitfall.
|
|
9
|
+
|
|
10
|
+
The app must be served over HTTPS (see [Requirements](installation.md#requirements)). Installed, wherdr opens
|
|
11
|
+
in its own window, without the browser's bars.
|
|
12
|
+
|
|
13
|
+
- **iPhone / iPad** (iOS 16.4 or later): open the address in **Safari** → Share →
|
|
14
|
+
**Add to Home Screen**. Open wherdr from the home screen, then Settings → **Enable
|
|
15
|
+
notifications**. On iOS, push notifications only work in the installed app.
|
|
16
|
+
- **Android**: open the address in **Chrome** → menu → **Install app** (or Add to Home
|
|
17
|
+
screen), then Settings → **Enable notifications**.
|
|
18
|
+
- **Mac, Safari** (macOS 14 Sonoma or later): File → **Add to Dock**.
|
|
19
|
+
- **Chrome, Edge, Brave** (macOS, Windows, Linux): the install icon at the right of the address
|
|
20
|
+
bar, or menu → **Cast, save and share** → **Install page as app** (Edge: Apps → **Install
|
|
21
|
+
this site as an app**).
|
|
22
|
+
- **Arc** cannot install web apps
|
|
23
|
+
([Arc Help Center](https://resources.arc.net/hc/en-us/articles/25678978728983-Does-Arc-for-Desktop-Support-Progressive-Web-Apps)):
|
|
24
|
+
open the address in Safari or Chrome for that, or keep wherdr as an Arc favorite.
|
|
25
|
+
|
|
26
|
+
Then Settings → Security → **Enable passkey lock** on each device.
|