@huynle/hub 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +7 -4
  3. package/package.json +1 -1
  4. package/packages/claude-code/package.json +1 -1
  5. package/packages/claude-code/src/wrapper.ts +2 -2
  6. package/packages/cli/dist/hub-cli.js +1126 -479
  7. package/packages/cli/package.json +1 -1
  8. package/packages/cli/src/bootstrap/PHASE3-CONTRACT.md +112 -0
  9. package/packages/cli/src/bootstrap/adapters/node-services/control.ts +164 -21
  10. package/packages/cli/src/bootstrap/adapters/node-services/plan.ts +24 -7
  11. package/packages/cli/src/bootstrap/adapters/node-services/render.ts +116 -9
  12. package/packages/cli/src/bootstrap/adapters/node-services/validate.ts +8 -2
  13. package/packages/cli/src/bootstrap/contracts/actions.ts +8 -6
  14. package/packages/cli/src/bootstrap/contracts/discovery.ts +4 -3
  15. package/packages/cli/src/bootstrap/contracts/exec.ts +4 -2
  16. package/packages/cli/src/bootstrap/contracts/paths.ts +2 -1
  17. package/packages/cli/src/bootstrap/contracts/platform.ts +5 -5
  18. package/packages/cli/src/bootstrap/discovery/discover.ts +7 -2
  19. package/packages/cli/src/bootstrap/discovery/host.ts +2 -0
  20. package/packages/cli/src/bootstrap/preview/plan.ts +5 -1
  21. package/packages/cli/src/bootstrap/profiles/desired.ts +16 -1
  22. package/packages/cli/src/bootstrap/profiles/history.ts +2 -1
  23. package/packages/cli/src/bootstrap/profiles/upgrade.ts +2 -2
  24. package/packages/cli/src/bootstrap/release/assets.ts +4 -1
  25. package/packages/cli/src/node.ts +94 -12
  26. package/packages/cli/src/setup/commands.ts +27 -7
  27. package/packages/cli/src/setup/runtime.ts +31 -4
  28. package/packages/codex/package.json +1 -1
  29. package/packages/codex/src/wrapper.ts +2 -2
  30. package/packages/node/package.json +1 -1
  31. package/packages/node/src/config.ts +49 -17
  32. package/packages/node/src/git.ts +16 -2
  33. package/packages/node/src/harnesses.ts +54 -9
  34. package/packages/node/src/host.ts +61 -15
  35. package/packages/node/src/opencode-service.ts +111 -0
  36. package/packages/node/src/opencode-warm.ts +11 -5
  37. package/packages/node/src/run.ts +50 -5
  38. package/packages/preset/package.json +1 -1
  39. package/packages/protocol/package.json +1 -1
  40. package/packages/protocol/src/index.ts +3 -1
  41. package/packages/protocol/src/launch.ts +39 -4
  42. package/packages/runtime-openshell/package.json +1 -1
  43. package/packages/runtime-sdk/package.json +1 -1
  44. package/packages/sdk/package.json +1 -1
  45. package/packages/spoke-sdk/package.json +1 -1
  46. package/src/central/agent/launch-spec.ts +4 -2
  47. package/src/central/agent/webhooks.ts +5 -2
  48. package/src/central/config.ts +24 -2
  49. package/src/central/http.ts +1 -0
  50. package/src/central/install/CONTRACT.md +4 -4
  51. package/src/central/install/install.ps1 +37 -5
  52. package/src/central/install/render.ts +13 -8
  53. package/src/central/push-target.ts +11 -2
  54. package/web/dist/assets/{ArtifactPage-4GoBqSpU.js → ArtifactPage-BKISN-Qj.js} +1 -1
  55. package/web/dist/assets/{ArtifactPane-Dx_0iWwH.js → ArtifactPane-CKOiAUNH.js} +1 -1
  56. package/web/dist/assets/{ArtifactWorkbench-CuHgARMk.js → ArtifactWorkbench-d7R1o-rs.js} +1 -1
  57. package/web/dist/assets/{Diagram-CZM_R7MG.js → Diagram-azw2B_QH.js} +1 -1
  58. package/web/dist/assets/{Library-TwvrwqSk.js → Library-CcjZXsfF.js} +2 -2
  59. package/web/dist/assets/{ProfilesDestination-rKNDpLjR.js → ProfilesDestination-DdzbzmeK.js} +1 -1
  60. package/web/dist/assets/{TeamsPage-DtJylLrt.js → TeamsPage-CDyjrK-I.js} +1 -1
  61. package/web/dist/assets/{Watchtower-Bmgdyfjb.js → Watchtower-DYwoUWME.js} +2 -2
  62. package/web/dist/assets/{index-D-xG9nrc.js → index-LNMZCqgE.js} +15 -15
  63. package/web/dist/assets/{live-7q72Lyj2.js → live-ycZ_aQZO.js} +1 -1
  64. package/web/dist/assets/{renderer2d-hfZxM6Xl.js → renderer2d-BIwbgRoX.js} +1 -1
  65. package/web/dist/assets/{renderer3d-Km9vipTM.js → renderer3d-C1a7eYGQ.js} +1 -1
  66. package/web/dist/assets/{safe-area-BAbgBiFk.js → safe-area-DHhPy61Q.js} +1 -1
  67. package/web/dist/index.html +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.2
4
+
5
+ Webhooks can reach a named receiver on your own network without opening the hub to every private address. No
6
+ migration is needed.
7
+
8
+ ### For operators
9
+ - **`agents.webhooks.allowPrivateHosts`** in `hub.json`: a list of exact hostnames whose `https://` webhook URLs may
10
+ resolve to private addresses (a LAN-hosted app behind split DNS, such as a Brain on the same cluster). Everything
11
+ else keeps the public-host rule: no `http://`, no IPs, no other hosts or subdomains, and the check still runs before
12
+ every send, and webhooks to a listed host must use port 443. Use it instead of `allowPrivate`, which opens private
13
+ targets for every webhook on the hub. In Kubernetes, also allow egress to the receiver's private address.
14
+ [docs/launches.md → Where webhooks may go](docs/launches.md#where-webhooks-may-go)
15
+
16
+ ## 0.9.1
17
+
18
+ **+ launch node on Windows.** `hubctl` installs `hub node` as a per-user logon task (Task Scheduler: no admin, no
19
+ stored password, no window), and on Windows the node starts OpenCode's background service, so a Windows machine stays
20
+ online and takes launches without OpenCode left open. Choose it in the console's Set up dialog or with
21
+ `install.ps1?profile=launch-node`. All harnesses (Claude Code, Codex) stays coming soon on Windows. No migration is
22
+ needed.
23
+
24
+ ### Machine setup
25
+ - **+ launch node on Windows**: `install.ps1` takes `profile=launch-node` with a `~/…` or `C:/…` workspace root and
26
+ `activate=1` (passed to hubctl as `--workspace-root` and `--confirm-activation`), and the console's Set up dialog
27
+ offers **+ launch node** on Windows (workspace folder as `C:\Users\you\projects`, `C:/…` or `~/…`; network
28
+ folders refused; Start services now). **All harnesses** stays coming soon on Windows.
29
+ - **hubctl: the `task-scheduler` service manager** (windows-x64): `hub node` runs as task `hub-node-<user>` at logon
30
+ (`conhost --headless hubctl.exe node run`, InteractiveToken, LeastPrivilege; the Task XML is UTF-16 in
31
+ `~/.local/state/hub/tasks`), restarted within a minute if it exits, controlled through a fixed `schtasks`/`taskkill`
32
+ allowlist with a locale-independent running check; `hub node install/uninstall` writes the XML and prints the
33
+ `schtasks` commands. `launch-node` plans on Windows; `full-harness` stays refused there.
34
+ - **The node on Windows**: canonical `C:/…` paths in the launch protocol, node.json (`C:\…`, `C:/…`, `~/…`) and
35
+ worktrees; it starts OpenCode's background service when none runs, then keeps it warm; a node with no workspace
36
+ roots waits for node.json instead of exiting; its log goes to `~/.local/state/hub/node.log`. git commands that hang
37
+ are killed with their children; every spawn starts hidden.
38
+
3
39
  ## 0.9.0
4
40
 
5
41
  Three things in one release: **media in sessions** (screenshots, images, Mermaid diagrams, safe HTML previews, and
package/README.md CHANGED
@@ -198,8 +198,9 @@ curl -fsSL 'https://hub.example.com/install.sh?profile=launch-node&root=~/projec
198
198
  ```
199
199
 
200
200
  ```powershell
201
- # Windows x64 (PowerShell 5.1 or 7): the same steps with hubctl-windows-x64.exe; OpenCode only for now
201
+ # Windows x64 (PowerShell 5.1 or 7): the same steps with hubctl-windows-x64.exe
202
202
  irm 'https://hub.example.com/install.ps1' | iex
203
+ irm 'https://hub.example.com/install.ps1?profile=launch-node&root=C:/Users/me/projects&activate=1' | iex
203
204
  ```
204
205
 
205
206
  Query options (each optional, at most once; anything else answers 400 with the reason):
@@ -207,11 +208,12 @@ Query options (each optional, at most once; anything else answers 400 with the r
207
208
  | Key | Values | Default |
208
209
  |---|---|---|
209
210
  | `profile` | `session-only` (OpenCode only), `launch-node` (+ a launch node), `full-harness` (all harnesses) | `session-only` |
210
- | `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` |
211
+ | `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 `~/` (`install.ps1`: `~/` or a drive path such as `C:/`, forward slashes), no `..` or `//` | `~/projects` |
211
212
  | `activate` | `1` starts the services now (`--confirm-activation`), `0` leaves them stopped | `0` |
212
213
 
213
- `install.ps1` takes only `profile=session-only` and `activate=0` for now (hubctl does not install user services on
214
- Windows yet) and runs on x64 Windows only (no arm64 build yet). It never closes your PowerShell window: a failure is
214
+ `install.ps1` takes `session-only` and `launch-node` (the node runs as a per-user logon task); `full-harness` answers
215
+ 400 for now (Claude Code and Codex need user services hubctl does not install on Windows yet). It runs on x64
216
+ Windows only (no arm64 build yet). It never closes your PowerShell window: a failure is
215
217
  one red `hub setup:` line. After a successful apply it adds `%USERPROFILE%\.local\bin` to your user PATH (once; open a
216
218
  new terminal for other windows). The scripts carry
217
219
  the hub's public URL, version and the validated choices; never a token. They download hubctl from this version's
@@ -1993,6 +1995,7 @@ in the file).
1993
1995
  | `agents.never` | permission patterns (`action[:glob]`) no agent may ever approve, for every user, checked before any grant; see [Agent actions and grants](#agent-actions-and-grants) |
1994
1996
  | `agents.launches` | per-user launch caps `{maxQueued?, maxActive?, perHour?}` (defaults 50, 4, 60) and `lostAfterMinutes?` (0–1440 or false, default 5: a running launch the same node instance stops reporting, with its session gone, ends lost; 0/false: never); see [docs/launches.md](docs/launches.md#placement-and-caps) |
1995
1997
  | `agents.webhooks.allowPrivate` | `true` lets webhooks target `http://`, loopback and private hosts (self-hosted receivers); see [docs/launches.md](docs/launches.md#where-webhooks-may-go) |
1998
+ | `agents.webhooks.allowPrivateHosts` | exact hostnames (at most 50) whose `https://` webhook URLs may resolve to private addresses, e.g. a LAN-hosted app behind split DNS; nothing else is opened; see [docs/launches.md](docs/launches.md#where-webhooks-may-go) |
1996
1999
 
1997
2000
  Server fields: env vars (`HUB_HOST`, `HUB_PORT`, `HUB_PUBLIC_URL`, `HUB_STATE_DIR`) > hub.json >
1998
2001
  defaults. Speech: phone settings > hub.json > env vars. Values are checked like the phone's speech
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huynle/hub",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
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.9.0",
3
+ "version": "0.9.2",
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",
@@ -57,7 +57,7 @@ function spawnDaemon(io: ClaudeIo): void {
57
57
  ensurePrivateDir(dir)
58
58
  const log = openSync(path.join(dir, "daemon.log"), "a", 0o600)
59
59
  try {
60
- const child = spawn(io.program.execPath, [io.program.entry, "claude-code", "daemon"], { detached: true, stdio: ["ignore", log, log], env: { ...io.env, BUN_BE_BUN: "1" } })
60
+ const child = spawn(io.program.execPath, [io.program.entry, "claude-code", "daemon"], { detached: true, windowsHide: true, stdio: ["ignore", log, log], env: { ...io.env, BUN_BE_BUN: "1" } })
61
61
  child.on("error", () => {})
62
62
  child.unref()
63
63
  } finally {
@@ -100,7 +100,7 @@ async function prepare(io: ClaudeIo, socket: string): Promise<string | undefined
100
100
  /** Runs `bin` with the terminal; resolves with its exit code (128 + signal number when a signal ended it). */
101
101
  function spawnClaude(bin: string, args: readonly string[], env: Env, stderr: (s: string) => void): Promise<number> {
102
102
  return new Promise((resolve) => {
103
- const child = spawn(bin, args, { stdio: "inherit", env: env as NodeJS.ProcessEnv })
103
+ const child = spawn(bin, args, { stdio: "inherit", windowsHide: true, env: env as NodeJS.ProcessEnv })
104
104
  // The terminal sends Ctrl-C to Claude Code too: it decides what that means; this process keeps waiting.
105
105
  const ignore = () => {}
106
106
  const forward = () => child.kill("SIGTERM")