@huynle/hub 0.8.3 → 0.8.5
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/CHANGELOG.md +80 -0
- package/README.md +40 -3
- package/package.json +1 -1
- package/packages/claude-code/package.json +1 -1
- package/packages/cli/dist/hub-cli.js +5288 -4415
- package/packages/cli/package.json +1 -1
- package/packages/cli/src/bootstrap/PHASE2-CONTRACT.md +87 -0
- package/packages/cli/src/bootstrap/adapters/claude/node.ts +11 -43
- package/packages/cli/src/bootstrap/adapters/codex/verify.ts +19 -12
- package/packages/cli/src/bootstrap/adapters/opencode/README.md +42 -6
- package/packages/cli/src/bootstrap/adapters/opencode/apply.ts +18 -7
- package/packages/cli/src/bootstrap/adapters/opencode/archive.ts +36 -19
- package/packages/cli/src/bootstrap/adapters/opencode/config.ts +19 -3
- package/packages/cli/src/bootstrap/adapters/opencode/index.ts +2 -5
- package/packages/cli/src/bootstrap/adapters/opencode/plan.ts +37 -6
- package/packages/cli/src/bootstrap/adapters/opencode/verify.ts +49 -26
- package/packages/cli/src/bootstrap/adapters/process-tree.ts +263 -0
- package/packages/cli/src/bootstrap/contracts/paths.ts +7 -4
- package/packages/cli/src/bootstrap/contracts/platform.ts +9 -5
- package/packages/cli/src/bootstrap/contracts/validate.ts +20 -4
- package/packages/cli/src/bootstrap/discovery/discover.ts +3 -1
- package/packages/cli/src/bootstrap/discovery/executables.ts +49 -14
- package/packages/cli/src/bootstrap/discovery/host.ts +16 -8
- package/packages/cli/src/bootstrap/discovery/node-probe.ts +41 -5
- package/packages/cli/src/bootstrap/discovery/probe.ts +7 -3
- package/packages/cli/src/bootstrap/discovery/util.ts +42 -5
- package/packages/cli/src/bootstrap/enrollment/store.ts +13 -6
- package/packages/cli/src/bootstrap/preview/plan.ts +3 -0
- package/packages/cli/src/bootstrap/profiles/desired.ts +10 -3
- package/packages/cli/src/bootstrap/release/README.md +63 -9
- package/packages/cli/src/bootstrap/release/cli.ts +94 -16
- package/packages/cli/src/bootstrap/release/harness-pins.pending.json +24 -0
- package/packages/cli/src/bootstrap/release/manifest.ts +3 -0
- package/packages/cli/src/bootstrap/release/pins.ts +60 -9
- package/packages/cli/src/bootstrap/transaction/builtin.ts +18 -6
- package/packages/cli/src/bootstrap/transaction/engine.ts +166 -12
- package/packages/cli/src/bootstrap/transaction/executor.ts +5 -0
- package/packages/cli/src/bootstrap/transaction/fs.ts +142 -3
- package/packages/cli/src/bootstrap/transaction/shim.ts +31 -0
- package/packages/cli/src/bootstrap/transaction/state.ts +11 -6
- package/packages/cli/src/credentials.ts +5 -2
- package/packages/cli/src/main.ts +3 -2
- package/packages/cli/src/setup/commands.ts +96 -18
- package/packages/cli/src/setup/contributors.ts +45 -10
- package/packages/cli/src/setup/runtime.ts +25 -6
- package/packages/cli/src/setup/self-aside.ts +130 -0
- package/packages/codex/package.json +1 -1
- package/packages/node/package.json +1 -1
- package/packages/node/src/opencode-warm.ts +12 -4
- package/packages/preset/package.json +1 -1
- package/packages/protocol/package.json +1 -1
- package/packages/protocol/src/index.ts +7 -2
- package/packages/runtime-openshell/package.json +1 -1
- package/packages/runtime-sdk/package.json +1 -1
- package/packages/sdk/package.json +1 -1
- package/packages/spoke-sdk/package.json +1 -1
- package/src/central/config.ts +12 -0
- package/src/central/http.ts +10 -0
- package/src/central/install/CONTRACT.md +66 -0
- package/src/central/install/install.ps1 +349 -0
- package/src/central/install/install.sh +266 -0
- package/src/central/install/render.ts +146 -0
- package/src/central/install/templates.ts +6 -0
- package/src/central/install/text-assets.d.ts +9 -0
- package/src/central/registry.ts +93 -22
- package/src/config.ts +12 -7
- package/src/index.ts +28 -0
- package/src/spoke/env-inject.ts +17 -2
- package/src/spoke/launchers.ts +145 -9
- package/src/spoke/spoke.ts +150 -10
- package/src/status.ts +70 -14
- package/src/win-acl.ts +154 -0
- package/web/dist/assets/ArtifactPage-Dq7VW-bU.js +1 -0
- package/web/dist/assets/{ArtifactPane-DPHjiWce.js → ArtifactPane-DKwbMTX9.js} +1 -1
- package/web/dist/assets/ArtifactWorkbench-vrgIfncX.js +6 -0
- package/web/dist/assets/Library-D6-Go6Cu.js +2 -0
- package/web/dist/assets/{ProfilesDestination-GriuOpiZ.js → ProfilesDestination-BOML2Dgf.js} +1 -1
- package/web/dist/assets/{TeamsPage-C-5lYeeM.js → TeamsPage-CNBTE8gB.js} +1 -1
- package/web/dist/assets/Watchtower-Dor1Ubjy.js +2 -0
- package/web/dist/assets/Watchtower-RphjcL0_.css +1 -0
- package/web/dist/assets/hub-CrbItiwP.js +13 -0
- package/web/dist/assets/index-82Ce0IAf.css +1 -0
- package/web/dist/assets/index-D9SP3_0z.js +22 -0
- package/web/dist/assets/{live-CDWcyavI.js → live-BNJgy0vg.js} +3 -3
- package/web/dist/assets/{pane-layout-Csk-vPNe.js → pane-layout-EdR1kRrG.js} +1 -1
- package/web/dist/assets/preload-helper-DqrJTq0t.js +1 -0
- package/web/dist/assets/{renderer2d-CNFdTDTr.js → renderer2d-CYag1CnR.js} +1 -1
- package/web/dist/assets/{renderer3d-C45jPvYX.js → renderer3d-BRB6rgDJ.js} +1 -1
- package/web/dist/assets/{safe-area-sA9a8vYJ.js → safe-area-fOy0AOuG.js} +1 -1
- package/web/dist/assets/{source-BFLJQ43_.js → source-Wbq35qGp.js} +1 -1
- package/web/dist/assets/{ui-Qo2Pd9-O.js → ui-j5FFk90B.js} +1 -1
- package/web/dist/index.html +6 -4
- package/web/dist/assets/ArtifactPage-CSotAWob.js +0 -1
- package/web/dist/assets/ArtifactWorkbench-CkmWnP6L.js +0 -6
- package/web/dist/assets/Library-DTLEyekn.js +0 -2
- package/web/dist/assets/Watchtower-CTgqHk5g.css +0 -1
- package/web/dist/assets/Watchtower-DOlPsiIy.js +0 -2
- package/web/dist/assets/index-6mdQ7eUY.js +0 -34
- package/web/dist/assets/index-C5OmwA1y.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,85 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.5
|
|
4
|
+
|
|
5
|
+
Set up Hub on a machine in one line. The console's new **Set up Hub on a machine** (avatar menu, Settings → Machines
|
|
6
|
+
& devices, Watchtower) builds a `curl … | sh` (macOS, Linux) or `irm … | iex` (Windows) command that the hub serves
|
|
7
|
+
with its own URL and version: it installs and checks hubctl, enrolls the machine (you approve it in the console) and
|
|
8
|
+
sets it up. hubctl now runs natively on Windows x64 for the OpenCode-only profile. No migration is needed.
|
|
9
|
+
|
|
10
|
+
### Machine setup
|
|
11
|
+
- **The hub serves one-line install scripts**: public `GET /install.sh` (macOS, Linux: `curl -fsSL '<hub>/install.sh?…' | sh`)
|
|
12
|
+
and `GET /install.ps1` (Windows: `irm '<hub>/install.ps1?…' | iex`), rendered from fixed templates with only the
|
|
13
|
+
hub's public URL, its version and the validated choices (`profile`, `root`, `activate`; anything else answers 400
|
|
14
|
+
with the reason; never a token). They pin this hub's hubctl release; the new `HUB_RELEASE_BASE` points them
|
|
15
|
+
elsewhere (https, or http on loopback or a private-network IP), and a development version without it answers 503.
|
|
16
|
+
A plain-http hub is served only on loopback or a private-network IP (with `--allow-insecure-http`), else 400.
|
|
17
|
+
Leave both paths reachable behind an authenticating proxy, like `/.well-known/hub`.
|
|
18
|
+
- **`install.ps1` installs hubctl on Windows** (x64; arm64 and 32-bit are refused with the reason): it downloads
|
|
19
|
+
`hubctl-windows-x64.exe`, checks its sha256 against `HUBCTL_SHA256SUMS` (`Get-FileHash`), then runs the same
|
|
20
|
+
`machine status` / `machine enroll` / `setup preview` / `setup apply` / `setup verify` sequence as `install.sh`
|
|
21
|
+
(session-only), and adds `%USERPROFILE%\.local\bin` to the user PATH once (`%VARS%` in it are kept). It no longer adds
|
|
22
|
+
the OpenCode plugin or writes the spoke config itself; it still never closes the PowerShell window.
|
|
23
|
+
- Console: **Set up Hub on a machine**, one dialog opened from the avatar's new menu, Settings → Machines & devices
|
|
24
|
+
(**Set up a machine**, also in the settings search) and Watchtower's **Connect a machine**: pick the OS (detected),
|
|
25
|
+
the profile (OpenCode only, + launch node with its workspace folder, All harnesses; Windows: OpenCode only for
|
|
26
|
+
now) and Start services now, then copy the `curl … | sh` or `irm … | iex` one-liner built from the hub's public URL.
|
|
27
|
+
|
|
28
|
+
### hubctl on Windows (x64, OpenCode only)
|
|
29
|
+
- **`hubctl-windows-x64.exe`** is built, signed into the release manifest and smoke-tested on Windows. Windows paths
|
|
30
|
+
(`C:/…`) are first-class in plans and journals; discovery reads `%USERPROFILE%`, PATHEXT, `.cmd` shims and the
|
|
31
|
+
machine's `MachineGuid`. `hub` and `opencode` on PATH are `.cmd` shims, the harness's current version is a junction,
|
|
32
|
+
files in use are replaced rename-aside, and the machine credential gets an owner-only ACL. `+ launch node` and
|
|
33
|
+
`All harnesses` need user services and are refused on Windows for now. This release pins no OpenCode for Windows
|
|
34
|
+
yet: a Windows machine without OpenCode is asked to install it first.
|
|
35
|
+
- **The OpenCode plugin writes `.cmd` launchers on Windows** instead of `#!/bin/sh` scripts and a symlink, and never
|
|
36
|
+
overwrites hubctl's `hub.cmd`.
|
|
37
|
+
- **Rollback**: a rollback that removes the running `hubctl.exe` deletes it once it exits and lists what it undid; a
|
|
38
|
+
path in use undoes everything else, names the path and is finished by `hubctl setup recover`.
|
|
39
|
+
- **Verify probes leave nothing running** (all harnesses, every OS): `opencode models --standalone`, and the probe's
|
|
40
|
+
whole process tree ends with it.
|
|
41
|
+
|
|
42
|
+
### Fixes
|
|
43
|
+
- `HEAD /install.sh` and `/install.ps1` answer like GET without a body.
|
|
44
|
+
|
|
45
|
+
## 0.8.4
|
|
46
|
+
|
|
47
|
+
A fix release. A question or permission a session is waiting on no longer disappears from the console while
|
|
48
|
+
OpenCode still waits (after the session briefly left a machine's list, or after a reconnect): the hub keeps a bounded
|
|
49
|
+
card for it and puts it back, and the machine re-announces what is pending after every reconnect. "Needs you" and the
|
|
50
|
+
hub now agree; a terminal Herdr reports blocked with nothing pending on the hub shows as "waiting in terminal".
|
|
51
|
+
No migration is needed; machines need the 0.8.4 plugin for the reconnect part.
|
|
52
|
+
|
|
53
|
+
### Fixes
|
|
54
|
+
- **The hub drops a question or permission whose ID is longer than any card keeps** (200 characters), so no such
|
|
55
|
+
entry is stored or alerted.
|
|
56
|
+
- **A pending question or permission no longer vanishes from the console while OpenCode still waits**
|
|
57
|
+
(production 2026-11-05: a session's question card disappeared, `show` said "Nothing waiting", the session still ran
|
|
58
|
+
in the question tool). The hub kept blockers only on a session's status board, built from live events: a board
|
|
59
|
+
recreated after the session left a sessions frame for a moment started empty, and a reconnecting spoke (laptop
|
|
60
|
+
sleep, network change, a restarted OpenCode process or hub) lost every pending question with the old connection and
|
|
61
|
+
never announced it again. Now the hub puts pending questions and permissions back on recreated boards (and on
|
|
62
|
+
boards created after the question was asked), the form stays answerable, and the spoke re-announces what is pending
|
|
63
|
+
after each reconnect: what it saw asked, plus what OpenCode lists for its active sessions (pending forms and
|
|
64
|
+
permission requests from the process's own server), closing those OpenCode no longer has. A re-announced question
|
|
65
|
+
or permission is not notified twice. Tests: `test/central-registry-blockers.test.ts`, `test/e2e-lost-question.test.ts`,
|
|
66
|
+
the spoke's reconnect tests and the browser suite `web/e2e/question-survives.ts`.
|
|
67
|
+
- What the hub keeps of a pending question or permission is the bounded card (at most 20 fields, the text limits,
|
|
68
|
+
10 resources; at most 64 kB, over which descriptions and then fields are left out; IDs over 200 characters are
|
|
69
|
+
refused), at most 200 per user for each kind. A permission card's summary is now bounded too (it joined every
|
|
70
|
+
resource). Another user can no longer close a pending question or permission by its ID, nor push it out with a
|
|
71
|
+
flood of its own.
|
|
72
|
+
- The spoke re-sends what it remembers right after reconnecting, then asks OpenCode (at most 50 sessions, the ones
|
|
73
|
+
with something remembered first, 5 s at most). Closes it infers from OpenCode's lists carry `unlisted: true`: the
|
|
74
|
+
hub drops only that process's hold on the question.
|
|
75
|
+
- Mixed versions: the hub announces the new `pendingReplay` feature. To an older hub (no such feature, and no
|
|
76
|
+
dedupe of re-sent permissions) the spoke re-sends only questions, so permissions it told that hub already are
|
|
77
|
+
lost on a reconnect as before rather than notified twice; a question lost from a frame drop is still lost on an
|
|
78
|
+
older hub. An older spoke on a new hub behaves as before (lost on reconnect).
|
|
79
|
+
- **"Needs you" only when there is something to answer.** A session that only Herdr calls blocked (its agent waits
|
|
80
|
+
for input, the hub has no question or permission for it) is no longer under Needs you or counted there: its row
|
|
81
|
+
shows "waiting in terminal" and it counts as working.
|
|
82
|
+
|
|
3
83
|
## 0.8.3
|
|
4
84
|
|
|
5
85
|
A voice and security release. A new developer setting records voice conversations (the Concierge and any voice
|
package/README.md
CHANGED
|
@@ -179,6 +179,41 @@ Optional: put the voice agent on a fast model in `~/.config/hub/config.json`:
|
|
|
179
179
|
`"voiceModel": "anthropic/claude-haiku-4-5"` (any `provider/model` you have). Subagents keep the
|
|
180
180
|
session's model.
|
|
181
181
|
|
|
182
|
+
## Set up a machine (one line)
|
|
183
|
+
|
|
184
|
+
The hub serves an install script for its own version, so a machine needs only one command. In the console use
|
|
185
|
+
**Set up Hub on a machine** (avatar menu, Settings → Machines, or Watchtower's **Connect a machine**) and copy the
|
|
186
|
+
line it shows, or write it yourself:
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
# macOS / Linux (POSIX sh): downloads and checks hubctl, enrolls (approve the code in the console), previews, applies
|
|
190
|
+
curl -fsSL 'https://hub.example.com/install.sh' | sh
|
|
191
|
+
curl -fsSL 'https://hub.example.com/install.sh?profile=launch-node&root=~/projects&activate=1' | sh
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```powershell
|
|
195
|
+
# Windows x64 (PowerShell 5.1 or 7): the same steps with hubctl-windows-x64.exe; OpenCode only for now
|
|
196
|
+
irm 'https://hub.example.com/install.ps1' | iex
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Query options (each optional, at most once; anything else answers 400 with the reason):
|
|
200
|
+
|
|
201
|
+
| Key | Values | Default |
|
|
202
|
+
|---|---|---|
|
|
203
|
+
| `profile` | `session-only` (OpenCode only), `launch-node` (+ a launch node), `full-harness` (all harnesses) | `session-only` |
|
|
204
|
+
| `root` | the launch node's workspace root, only with `launch-node`: up to 200 characters of `A-Z a-z 0-9 . _ / ~ -`, starting with `/` or `~/`, no `..` or `//` | `~/projects` |
|
|
205
|
+
| `activate` | `1` starts the services now (`--confirm-activation`), `0` leaves them stopped | `0` |
|
|
206
|
+
|
|
207
|
+
`install.ps1` takes only `profile=session-only` and `activate=0` for now (hubctl does not install user services on
|
|
208
|
+
Windows yet) and runs on x64 Windows only (no arm64 build yet). It never closes your PowerShell window: a failure is
|
|
209
|
+
one red `hub setup:` line. After a successful apply it adds `%USERPROFILE%\.local\bin` to your user PATH (once; open a
|
|
210
|
+
new terminal for other windows). The scripts carry
|
|
211
|
+
the hub's public URL, version and the validated choices; never a token. They download hubctl from this version's
|
|
212
|
+
release on [hub-releases](https://github.com/huynle/hub-releases/releases) (`v<version>`); `HUB_RELEASE_BASE` on
|
|
213
|
+
the hub points them at another location (a mirror, or a development build, which otherwise answers 503). A
|
|
214
|
+
plain-`http` hub is served only on loopback or a private-network IP address (the script then passes
|
|
215
|
+
`--allow-insecure-http`); on a host name or a public address the routes answer 400: use https.
|
|
216
|
+
|
|
182
217
|
## Quick start (hubctl, before any harness)
|
|
183
218
|
|
|
184
219
|
`hubctl` is a standalone, signed program (Bun and the CLI compiled into one file; no bun, node or harness needed)
|
|
@@ -317,8 +352,9 @@ foreground with `hub.json`; `scripts/io.github.huynle.hub.plist` is a launchd te
|
|
|
317
352
|
**Security:** the hub can drive your OpenCode sessions, which can run tools. Every API call needs a
|
|
318
353
|
paired device or a signed-in user; spokes need machine tokens; nothing trusts the client IP. For a public
|
|
319
354
|
hostname, consider [sign-in with Google](#multi-user-sign-in-with-google) or an authenticating proxy in
|
|
320
|
-
front of the web app, and leave `/spoke/*`, `/auth/*`, `/healthz`, `/.well-known/hub`,
|
|
321
|
-
icons reachable for spokes, sign-in, app discovery
|
|
355
|
+
front of the web app, and leave `/spoke/*`, `/auth/*`, `/healthz`, `/.well-known/hub`, `/install.sh`,
|
|
356
|
+
`/install.ps1`, the manifest and icons reachable for spokes, sign-in, app discovery, [machine
|
|
357
|
+
setup](#set-up-a-machine-one-line) and the PWA (and `/mcp`, bearer-authenticated, if MCP clients
|
|
322
358
|
or gateways use [MCP over HTTP](#over-http-mcp)).
|
|
323
359
|
|
|
324
360
|
**Behind a reverse proxy**, set a request-body limit of about 26 MiB (artifact blobs are up to 25 MiB) and keep
|
|
@@ -328,7 +364,7 @@ including `blobs/` (artifact bytes).
|
|
|
328
364
|
|
|
329
365
|
## Configuration
|
|
330
366
|
|
|
331
|
-
- **Hub**: env vars (`HUB_HOST`, `HUB_PORT`, `HUB_PUBLIC_URL`, `HUB_STATE_DIR`, `OPENROUTER_API_KEY`, …)
|
|
367
|
+
- **Hub**: env vars (`HUB_HOST`, `HUB_PORT`, `HUB_PUBLIC_URL`, `HUB_STATE_DIR`, `HUB_RELEASE_BASE`, `OPENROUTER_API_KEY`, …)
|
|
332
368
|
over `~/.config/hub/hub.json` (`$HUB_CONFIG`); see [Central server](#central-server) and
|
|
333
369
|
[hub.json](#hubjson). Speech providers and keys are easiest to set in the app (Settings → Speech).
|
|
334
370
|
- **Plugin** (OpenCode spoke): `~/.config/hub/config.json` (`$HUB_SPOKE_CONFIG`; server, mode, machine
|
|
@@ -1218,6 +1254,7 @@ holds the startup (`bin/hub.ts` serves `web/` from disk; the compiled binaries e
|
|
|
1218
1254
|
| `HUB_DATABASE_STATEMENT_TIMEOUT_MS` | `30000` | cluster mode: Postgres `statement_timeout` of the hub's pools (`0` turns it off) |
|
|
1219
1255
|
| `HUB_TURN_TIMEOUT_MS` | `600000` | the hub waits this + 15 s so the spoke's own timeout result arrives first |
|
|
1220
1256
|
| `HUB_VAPID_SUBJECT` | the https public URL, else `mailto:hub@localhost` | contact sent to push services |
|
|
1257
|
+
| `HUB_RELEASE_BASE` | `https://github.com/huynle/hub-releases/releases/download/v<version>` | where `/install.sh` and `/install.ps1` download hubctl ([Set up a machine](#set-up-a-machine-one-line)): https, or plain http on loopback or a private-network IP address; the hub refuses to start with any other value. Without it a development version (not a plain `x.y.z`) answers 503 |
|
|
1221
1258
|
| `HUB_CSP` | `enforce` | `report-only` sends the console's Content-Security-Policy as `Content-Security-Policy-Report-Only` (violations are only logged in the browser console); see [Content-Security-Policy](#content-security-policy) |
|
|
1222
1259
|
| `OPENROUTER_API_KEY` | | default key for the `openrouter` speech provider (never sent to other providers) |
|
|
1223
1260
|
| `HUB_STT_MODEL`, `HUB_TTS_MODEL`, `HUB_VOICE`, `HUB_MAX_*` | see `src/central/config.ts` | speech defaults; the phone's speech settings override them |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Hub: a harness-agnostic central hub your AI agent harnesses connect to (OpenCode first). Follow, steer and stop sessions from your phone or browser, with notifications, optional hands-free Voice and a self-hostable server.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Huy Le",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huynle/hub-claude-code",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "The Claude Code hooks spoke: per-user daemon, hook bridge, plugin dir, `hub claude` wrapper and daemon-backed MCP server (internal; shipped inside @huynle/hub, not published on its own).",
|
|
6
6
|
"license": "MIT",
|