@tt-a1i/hive 1.4.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.en.md +21 -0
  3. package/README.md +16 -0
  4. package/assets/qq-group.jpg +0 -0
  5. package/dist/bin/team.cmd +1 -0
  6. package/dist/src/cli/hive-update.d.ts +45 -17
  7. package/dist/src/cli/hive-update.js +63 -25
  8. package/dist/src/cli/hive.d.ts +25 -0
  9. package/dist/src/cli/hive.js +41 -3
  10. package/dist/src/cli/team.d.ts +1 -0
  11. package/dist/src/cli/team.js +216 -3
  12. package/dist/src/server/agent-command-resolver.js +3 -19
  13. package/dist/src/server/agent-manager-support.d.ts +2 -2
  14. package/dist/src/server/agent-manager-support.js +98 -24
  15. package/dist/src/server/agent-run-starter.d.ts +6 -1
  16. package/dist/src/server/agent-run-starter.js +9 -2
  17. package/dist/src/server/agent-run-store.d.ts +1 -1
  18. package/dist/src/server/agent-runtime-close.d.ts +1 -0
  19. package/dist/src/server/agent-runtime-close.js +25 -1
  20. package/dist/src/server/agent-runtime-contract.d.ts +12 -1
  21. package/dist/src/server/agent-runtime-stop-run.d.ts +1 -1
  22. package/dist/src/server/agent-runtime-stop-run.js +4 -1
  23. package/dist/src/server/agent-runtime.d.ts +2 -1
  24. package/dist/src/server/agent-runtime.js +14 -3
  25. package/dist/src/server/agent-startup-instructions.d.ts +7 -1
  26. package/dist/src/server/agent-startup-instructions.js +17 -9
  27. package/dist/src/server/agent-stdin-dispatcher.d.ts +25 -5
  28. package/dist/src/server/agent-stdin-dispatcher.js +141 -40
  29. package/dist/src/server/cron-util.d.ts +7 -0
  30. package/dist/src/server/cron-util.js +19 -0
  31. package/dist/src/server/dispatch-ledger-store.d.ts +22 -0
  32. package/dist/src/server/dispatch-ledger-store.js +51 -3
  33. package/dist/src/server/env-sync-message.js +9 -9
  34. package/dist/src/server/feature-flags.d.ts +42 -0
  35. package/dist/src/server/feature-flags.js +24 -0
  36. package/dist/src/server/fs-pick-folder.js +4 -0
  37. package/dist/src/server/fs-sandbox.js +36 -7
  38. package/dist/src/server/hive-team-guidance.d.ts +12 -6
  39. package/dist/src/server/hive-team-guidance.js +253 -71
  40. package/dist/src/server/live-run-registry.d.ts +1 -0
  41. package/dist/src/server/live-run-registry.js +1 -1
  42. package/dist/src/server/open-target-commands.js +5 -6
  43. package/dist/src/server/orchestrator-autostart.d.ts +12 -0
  44. package/dist/src/server/orchestrator-autostart.js +15 -13
  45. package/dist/src/server/path-canonicalization.d.ts +3 -0
  46. package/dist/src/server/path-canonicalization.js +29 -0
  47. package/dist/src/server/platform-path.d.ts +3 -0
  48. package/dist/src/server/platform-path.js +13 -0
  49. package/dist/src/server/post-start-input-writer.d.ts +1 -1
  50. package/dist/src/server/post-start-input-writer.js +110 -13
  51. package/dist/src/server/preset-launch-support.d.ts +1 -1
  52. package/dist/src/server/preset-launch-support.js +33 -2
  53. package/dist/src/server/recovery-summary.d.ts +5 -1
  54. package/dist/src/server/recovery-summary.js +18 -17
  55. package/dist/src/server/report-outbox-store.d.ts +36 -0
  56. package/dist/src/server/report-outbox-store.js +33 -0
  57. package/dist/src/server/restart-policy-support.d.ts +5 -1
  58. package/dist/src/server/restart-policy-support.js +9 -1
  59. package/dist/src/server/restart-policy.d.ts +6 -2
  60. package/dist/src/server/restart-policy.js +51 -31
  61. package/dist/src/server/role-template-store.d.ts +1 -0
  62. package/dist/src/server/role-template-store.js +11 -1
  63. package/dist/src/server/route-types.d.ts +43 -0
  64. package/dist/src/server/routes-runtime.js +2 -1
  65. package/dist/src/server/routes-settings.js +76 -0
  66. package/dist/src/server/routes-tasks.js +23 -0
  67. package/dist/src/server/routes-team.js +211 -1
  68. package/dist/src/server/routes-workflow-schedules.d.ts +2 -0
  69. package/dist/src/server/routes-workflow-schedules.js +58 -0
  70. package/dist/src/server/routes-workflows.d.ts +2 -0
  71. package/dist/src/server/routes-workflows.js +83 -0
  72. package/dist/src/server/routes-workspaces.js +5 -0
  73. package/dist/src/server/routes.js +4 -0
  74. package/dist/src/server/runtime-restart-policy.d.ts +3 -1
  75. package/dist/src/server/runtime-restart-policy.js +2 -1
  76. package/dist/src/server/runtime-store-contract.d.ts +125 -0
  77. package/dist/src/server/runtime-store-contract.js +1 -0
  78. package/dist/src/server/runtime-store-helpers.d.ts +11 -0
  79. package/dist/src/server/runtime-store-helpers.js +106 -2
  80. package/dist/src/server/runtime-store-workflows.d.ts +6 -0
  81. package/dist/src/server/runtime-store-workflows.js +108 -0
  82. package/dist/src/server/runtime-store.d.ts +3 -72
  83. package/dist/src/server/runtime-store.js +71 -4
  84. package/dist/src/server/session-capture-codex.d.ts +3 -3
  85. package/dist/src/server/session-capture-codex.js +9 -7
  86. package/dist/src/server/session-capture-gemini.d.ts +1 -1
  87. package/dist/src/server/session-capture-gemini.js +6 -3
  88. package/dist/src/server/settings-store.d.ts +3 -0
  89. package/dist/src/server/settings-store.js +1 -0
  90. package/dist/src/server/sqlite-schema-v19.d.ts +2 -0
  91. package/dist/src/server/sqlite-schema-v19.js +17 -0
  92. package/dist/src/server/sqlite-schema-v20.d.ts +2 -0
  93. package/dist/src/server/sqlite-schema-v20.js +20 -0
  94. package/dist/src/server/sqlite-schema-v21.d.ts +2 -0
  95. package/dist/src/server/sqlite-schema-v21.js +20 -0
  96. package/dist/src/server/sqlite-schema.d.ts +1 -1
  97. package/dist/src/server/sqlite-schema.js +110 -1
  98. package/dist/src/server/system-message.d.ts +7 -0
  99. package/dist/src/server/system-message.js +8 -1
  100. package/dist/src/server/task-deps.d.ts +32 -0
  101. package/dist/src/server/task-deps.js +40 -0
  102. package/dist/src/server/tasks-file-watcher.d.ts +12 -1
  103. package/dist/src/server/tasks-file-watcher.js +128 -23
  104. package/dist/src/server/tasks-file.d.ts +3 -1
  105. package/dist/src/server/tasks-file.js +33 -9
  106. package/dist/src/server/tasks-websocket-server.js +13 -14
  107. package/dist/src/server/team-authz.d.ts +1 -1
  108. package/dist/src/server/team-authz.js +10 -1
  109. package/dist/src/server/team-autostaff.d.ts +16 -0
  110. package/dist/src/server/team-autostaff.js +16 -0
  111. package/dist/src/server/team-list-serializer.d.ts +1 -1
  112. package/dist/src/server/team-list-serializer.js +3 -1
  113. package/dist/src/server/team-operations.d.ts +21 -1
  114. package/dist/src/server/team-operations.js +183 -16
  115. package/dist/src/server/terminal-protocol.js +9 -3
  116. package/dist/src/server/terminal-stream-hub.js +16 -10
  117. package/dist/src/server/terminal-ws-server.js +10 -8
  118. package/dist/src/server/webhook-notifier.d.ts +34 -0
  119. package/dist/src/server/webhook-notifier.js +47 -0
  120. package/dist/src/server/websocket-upgrade-safety.d.ts +10 -0
  121. package/dist/src/server/websocket-upgrade-safety.js +35 -0
  122. package/dist/src/server/windows-command-line.d.ts +3 -0
  123. package/dist/src/server/windows-command-line.js +9 -0
  124. package/dist/src/server/windows-filename.d.ts +2 -0
  125. package/dist/src/server/windows-filename.js +33 -0
  126. package/dist/src/server/workflow-cli-policy.d.ts +60 -0
  127. package/dist/src/server/workflow-cli-policy.js +110 -0
  128. package/dist/src/server/workflow-dispatch-awaiter.d.ts +12 -0
  129. package/dist/src/server/workflow-dispatch-awaiter.js +80 -0
  130. package/dist/src/server/workflow-feature.d.ts +15 -0
  131. package/dist/src/server/workflow-feature.js +15 -0
  132. package/dist/src/server/workflow-http-serializers.d.ts +64 -0
  133. package/dist/src/server/workflow-http-serializers.js +58 -0
  134. package/dist/src/server/workflow-output-schema.d.ts +18 -0
  135. package/dist/src/server/workflow-output-schema.js +41 -0
  136. package/dist/src/server/workflow-run-log-store.d.ts +19 -0
  137. package/dist/src/server/workflow-run-log-store.js +45 -0
  138. package/dist/src/server/workflow-run-store.d.ts +50 -0
  139. package/dist/src/server/workflow-run-store.js +103 -0
  140. package/dist/src/server/workflow-runner.d.ts +147 -0
  141. package/dist/src/server/workflow-runner.js +411 -0
  142. package/dist/src/server/workflow-schedule-create.d.ts +14 -0
  143. package/dist/src/server/workflow-schedule-create.js +41 -0
  144. package/dist/src/server/workflow-schedule-store.d.ts +43 -0
  145. package/dist/src/server/workflow-schedule-store.js +112 -0
  146. package/dist/src/server/workflow-scheduler.d.ts +36 -0
  147. package/dist/src/server/workflow-scheduler.js +97 -0
  148. package/dist/src/server/workflow-script-loader.d.ts +34 -0
  149. package/dist/src/server/workflow-script-loader.js +106 -0
  150. package/dist/src/server/workspace-path-validation.js +16 -4
  151. package/dist/src/server/workspace-shell-runtime.d.ts +5 -0
  152. package/dist/src/server/workspace-shell-runtime.js +24 -2
  153. package/dist/src/server/workspace-store-contract.d.ts +4 -1
  154. package/dist/src/server/workspace-store-hydration.js +23 -7
  155. package/dist/src/server/workspace-store-mutations.js +2 -5
  156. package/dist/src/server/workspace-store-support.d.ts +4 -0
  157. package/dist/src/server/workspace-store-support.js +13 -1
  158. package/dist/src/server/workspace-store.js +38 -4
  159. package/dist/src/shared/types.d.ts +16 -1
  160. package/package.json +4 -2
  161. package/web/dist/assets/{AddWorkerDialog-DeZhTQLi.js → AddWorkerDialog-CGbaxu0T.js} +2 -2
  162. package/web/dist/assets/AddWorkspaceDialog-CNgExu6b.js +1 -0
  163. package/web/dist/assets/{FirstRunWizard-B5wLcat5.js → FirstRunWizard-DxGApUNc.js} +1 -1
  164. package/web/dist/assets/{MarketplaceDrawer-BC0eBOEW.js → MarketplaceDrawer-Bk6cpukn.js} +1 -1
  165. package/web/dist/assets/WhatsNewDialog-CSGzk-2U.js +1 -0
  166. package/web/dist/assets/WorkerModal-i2F3n3nZ.js +1 -0
  167. package/web/dist/assets/WorkspaceTaskDrawer-C_Ta_K13.js +1 -0
  168. package/web/dist/assets/WorkspaceTerminalPanels-VdDxtrQF.js +1 -0
  169. package/web/dist/assets/index-5zh61jMg.css +1 -0
  170. package/web/dist/assets/index-CAgGM6nb.js +75 -0
  171. package/web/dist/assets/path-join-7MR1s7b1.js +1 -0
  172. package/web/dist/index.html +2 -2
  173. package/web/dist/sw.js +1 -1
  174. package/web/dist/assets/AddWorkspaceDialog-DDpXNEKf.js +0 -1
  175. package/web/dist/assets/WorkerModal-BwMHq-Bi.js +0 -1
  176. package/web/dist/assets/WorkspaceTaskDrawer-CxvT4nqs.js +0 -1
  177. package/web/dist/assets/WorkspaceTerminalPanels-CvibsPSd.js +0 -1
  178. package/web/dist/assets/index-BEsTmfrO.css +0 -1
  179. package/web/dist/assets/index-Ddb7bDN5.js +0 -75
  180. package/web/dist/assets/path-join-S7qkXQtP.js +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,53 @@
2
2
 
3
3
  All notable user-facing changes will be documented in this file.
4
4
 
5
+ ## 1.6.0 - 2026-06-02
6
+
7
+ Orchestrator controls, worker visibility, and protocol-trust fixes.
8
+
9
+ - Adds a Stop control to the running Orchestrator pane so a runaway Orchestrator
10
+ can be halted from the UI (the default is auto-approve, so this is the only
11
+ in-UI kill switch).
12
+ - Surfaces per-worker queue depth and a latest-activity line on worker cards,
13
+ with a legend on the working badge clarifying that Hive does not auto-detect
14
+ stalls — the terminal is the source of truth.
15
+ - Redelivers worker reports the Orchestrator missed: a report is no longer
16
+ silently lost when the Orchestrator is down or restarting; it is queued and
17
+ redelivered on the next report or `team list`.
18
+ - Stops injecting the crash-recovery handover after a deliberate Stop and
19
+ Restart, so the Orchestrator is not handed stale open tasks it was meant to
20
+ drop.
21
+ - Adds an outbound completion webhook setting: Hive POSTs a small JSON payload
22
+ to a URL you choose when a worker reports or a workflow finishes — wire it to
23
+ Slack, ntfy, Feishu, and the like.
24
+ - Adds opt-in structured output to workflow `agent()` via `outputSchema`, so
25
+ fan-out/verify scripts receive a parsed object instead of parsing free text.
26
+ - Adds `team next`: tasks in `.hive/tasks.md` can carry an optional
27
+ `[needs: #2]` dependency, and `team next` returns the tasks that are unblocked
28
+ now.
29
+
30
+ ## 1.5.0 - 2026-05-31
31
+
32
+ Workflow runtime, experimental team automation, and Codex reliability.
33
+
34
+ - Adds the experimental Hive workflow runtime: Orchestrators can author
35
+ multi-agent workflow scripts that fan out across real Hive PTY workers, show
36
+ runs in the Workflows drawer, stop runs, inspect run details, schedule
37
+ recurring workflows, and route workflow reports back into the Orchestrator.
38
+ - Adds workflow agent CLI policy settings so users can choose which CLI
39
+ workflow-created agents use by default and which CLIs are allowed.
40
+ - Adds the experimental auto-staff setting, letting the Orchestrator size the
41
+ worker roster to the task and prefer task-scoped ephemeral workers when it
42
+ needs temporary coders, testers, or reviewers.
43
+ - Adds an in-app What's New dialog so future upgrades can surface curated
44
+ release highlights without requiring users to read the changelog manually.
45
+ - Improves Codex reliability by waiting for pasted-content acknowledgements on
46
+ long dispatches while submitting short report/status injections quickly,
47
+ avoiding the several-second delay before reports reach the Orchestrator.
48
+ - Hardens Windows and runtime edge cases, including malformed WebSocket frames,
49
+ stale nvm4w Codex node entrypoints, workflow worker exits, and additional
50
+ workflow/runtime cleanup paths.
51
+
5
52
  ## 1.4.4 - 2026-05-29
6
53
 
7
54
  Windows portability and team protocol hardening.
package/README.en.md CHANGED
@@ -123,6 +123,18 @@ First-run flow:
123
123
  5. Ask the Orchestrator to delegate work. It sends tasks with
124
124
  `team send <worker-name> "<task>"`; workers report back with `team report`.
125
125
 
126
+ If you want the Orchestrator to size the team itself, leave **Auto-staff**
127
+ enabled (it is on by default). It can `team spawn` the right temporary mix of
128
+ coders, testers, and reviewers for the task, then Hive dismisses those
129
+ temporary workers when their work is done.
130
+
131
+ For stronger automation, enable the experimental **Workflows** toggle in
132
+ settings. The Orchestrator can then author and run multi-agent workflows that
133
+ fan out across implementation, review, testing, or other stages. The topbar
134
+ **Workflows** panel shows runs, phase results, logs, schedules, and stop
135
+ controls. The same panel also lets you choose which CLI workflow-created
136
+ agents use by default and which CLIs they are allowed to use.
137
+
126
138
  ## How It Works
127
139
 
128
140
  ```text
@@ -175,8 +187,17 @@ same shell environment you use to start Hive.
175
187
  - Orchestrator and worker terminals backed by real PTYs.
176
188
  - Add Worker flow with role presets for coder, reviewer, tester, and fully
177
189
  custom prompts and commands — wire any CLI agent into the role you need.
190
+ - Auto-staff (experimental, on by default): the Orchestrator can create
191
+ temporary coders, testers, and reviewers based on the task, and Hive cleans
192
+ them up after their dispatch reports back.
193
+ - Workflows (experimental, off by default): the Orchestrator can run
194
+ multi-stage, multi-agent workflows while Hive shows runs, logs, results,
195
+ schedules, and stop controls in the Workflows panel.
196
+ - Workflow CLI policy: choose the default CLI for workflow-created agents and
197
+ restrict which CLIs workflow scripts may launch.
178
198
  - `.hive/tasks.md` editor with external-file conflict handling.
179
199
  - Background PTY preservation and best-effort native session resume.
200
+ - A What's New dialog after upgrades with curated release highlights.
180
201
  - Local SQLite metadata under `~/.config/hive` by default, or `$HIVE_DATA_DIR`
181
202
  when set.
182
203
 
package/README.md CHANGED
@@ -84,6 +84,10 @@ PWA 只是 UI 壳,Hive 后端仍需要在终端里跑着。如果启动 PWA
84
84
  4. 在 Team Members 面板里添加 Worker。
85
85
  5. 跟 Orchestrator 说一声让它派活,它会用 `team send <worker-name> "<task>"` 发任务,Worker 完事后用 `team report` 回报。
86
86
 
87
+ 想让 Orchestrator 自己决定团队规模,可以保留 **自动组队** 开关开启(默认开启):它会按任务需要临时 `team spawn` 合适数量的 coder / tester / reviewer,任务结束后自动收回临时成员。
88
+
89
+ 想试更强的自动化,可以在右上角设置里开启实验性的 **Workflow** 开关。开启后,Orchestrator 可以编写并运行多 agent workflow,把一个目标拆成 fan-out / review / test 等阶段;顶部的 **Workflows** 面板会显示运行记录、阶段结果、定时任务和停止按钮。Workflow 创建的新 agent 默认使用哪种 CLI、允许使用哪些 CLI,也可以在 Workflows 面板里配置。
90
+
87
91
  ## 工作方式
88
92
 
89
93
  ```text
@@ -131,8 +135,12 @@ Hive 不替你安装这些 CLI。请在启动 Hive 的同一个 shell 环境里
131
135
  - Workspace 侧边栏,方便在多个本机项目之间切换。
132
136
  - Orchestrator 和 Worker 终端都是真实 PTY 支撑的。
133
137
  - Add Worker 预置 coder / reviewer / tester 等角色模板,也支持完全自定义 prompt 与命令——把任何 CLI agent 编排成你需要的角色。
138
+ - 自动组队(实验性,默认开启):Orchestrator 可以根据任务动态创建临时 coder / tester / reviewer,完成后自动回收。
139
+ - Workflows(实验性,默认关闭):Orchestrator 可以运行多阶段、多 agent 的 workflow,Hive 在 Workflows 面板里展示运行、日志、结果、定时任务和停止控制。
140
+ - Workflow CLI 策略:为 workflow 创建的 agent 选择默认 CLI,并限制允许使用的 CLI,避免脚本误启未配置的 agent。
134
141
  - `.hive/tasks.md` 编辑器,带外部文件冲突处理。
135
142
  - PTY 后台保留 + 尽力使用各 CLI 原生 session 恢复。
143
+ - 升级后的 What's New 弹窗,用简短 release highlights 告诉你新版改了什么。
136
144
  - 元数据存在本机 SQLite,默认在 `~/.config/hive`,或者通过 `$HIVE_DATA_DIR` 指定。
137
145
 
138
146
  Hive **不**提供 sandbox 隔离、多用户认证、云端托管,也不自带任何 agent 模型。它只负责调度你已经在用的本机 CLI。
@@ -241,6 +249,14 @@ pnpm release:dry
241
249
 
242
250
  Hive 目前处于 alpha 阶段,核心流程已可用。当前重点是继续打磨多 Agent 协作体验、Windows 支持和更清晰的调度可观测性。欢迎试用、提 issue——反馈会直接影响后续节奏。
243
251
 
252
+ ## 交流群
253
+
254
+ 有问题、想反馈,或者就想聊聊 Agent 协作,欢迎进 QQ 群:**Ai Native 交流群**(群号 `1098836554`)。
255
+
256
+ <p align="center">
257
+ <img src="./assets/qq-group.jpg" width="240" alt="Ai Native 交流群 QQ 群二维码,群号 1098836554" />
258
+ </p>
259
+
244
260
  ## 在路上:跨 Agent 的长时记忆
245
261
 
246
262
  <p align="center">
Binary file
package/dist/bin/team.cmd CHANGED
@@ -1,2 +1,3 @@
1
1
  @echo off
2
+ setlocal DisableDelayedExpansion
2
3
  node "%~dp0..\src\cli\team.js" %*
@@ -4,26 +4,36 @@ export interface RunUpdateResult {
4
4
  spawnError?: Error;
5
5
  }
6
6
  export type RunUpdate = (command: string, args: readonly string[]) => Promise<RunUpdateResult>;
7
+ export interface SpawnInvocationPlan {
8
+ command: string;
9
+ args: string[];
10
+ options: {
11
+ stdio: 'inherit';
12
+ windowsHide?: boolean;
13
+ };
14
+ }
7
15
  /**
8
- * Build the spawn options for the upgrade child. The non-obvious part is
9
- * Windows: npm ships as `npm.cmd` (a batch shim), and Node 22+ refuses to
10
- * spawn `.cmd` / `.bat` files without `shell: true` after CVE-2024-27980.
11
- * Detect by file extension rather than by `process.platform` so the same
12
- * code path works for both real Windows runs and our cross-platform unit
13
- * tests (which inject `platform: 'win32'` so that `getNpmCommand` returns
14
- * `npm.cmd`).
16
+ * Plan how `defaultRunUpdate` should hand the npm invocation to
17
+ * `child_process.spawn`. Two non-obvious cases collapse here.
18
+ *
19
+ * 1. Node 22+ refuses to spawn `.cmd` / `.bat` files directly after
20
+ * CVE-2024-27980 unless `shell: true` is passed. We do not want
21
+ * `shell: true` though its arg-stringification path joins argv
22
+ * without quoting, so an install prefix containing spaces (the
23
+ * common Windows case `C:\Program Files\nodejs`) gets word-split
24
+ * by cmd.exe and `--prefix` only sees the first token, so npm
25
+ * silently installs hive to the wrong directory.
26
+ * 2. Wrapping with `cmd.exe /d /s /c <npm.cmd> <args>` solves both:
27
+ * spawning `cmd.exe` (a native exe) avoids the .cmd refusal, and
28
+ * Node's own argv-quoting builds an lpCommandLine where each
29
+ * space-containing arg is wrapped in double quotes that cmd.exe
30
+ * then re-parses correctly. `/d` skips AutoRun, `/s` keeps the
31
+ * quote-handling consistent with `/c`.
15
32
  *
16
- * Known limitation: with `shell: true` the args are stringified through
17
- * cmd.exe without quoting, so an install prefix containing spaces (e.g.
18
- * `C:\Program Files\nodejs`) will be tokenized incorrectly. The common
19
- * Windows prefix `%APPDATA%\npm` does not have this problem; fixing the
20
- * spaces case requires a verbatim `cmd.exe /d /s /c call npm.cmd …`
21
- * wrapper similar to `agent-command-resolver` and is tracked separately.
33
+ * Detect by filename suffix instead of `process.platform` so unit
34
+ * tests can inject `platform: 'win32'` and exercise the wrap.
22
35
  */
23
- export declare const buildSpawnOptionsForCommand: (command: string) => {
24
- shell: boolean;
25
- stdio: "inherit";
26
- };
36
+ export declare const planSpawnInvocation: (command: string, args: readonly string[], platform?: NodeJS.Platform) => SpawnInvocationPlan;
27
37
  /**
28
38
  * Signals the upgrade child should receive when the parent runtime is
29
39
  * interrupted. Beyond the POSIX-only SIGTERM/SIGINT, SIGHUP is what
@@ -33,6 +43,24 @@ export declare const buildSpawnOptionsForCommand: (command: string) => {
33
43
  * Windows exit paths.
34
44
  */
35
45
  export declare const FORWARDED_UPDATE_SIGNALS: readonly NodeJS.Signals[];
46
+ /**
47
+ * Forward a parent-process signal to the spawned npm child. On POSIX
48
+ * we hand the signal straight to the child; on Windows there are no
49
+ * real signals, so `child.kill(SIGTERM)` resolves to TerminateProcess
50
+ * against cmd.exe (our wrapper) only — npm itself, plus any install
51
+ * scripts it spawned, become orphans. `taskkill /pid <pid> /t /f`
52
+ * walks the wrapper's process tree so the whole branch dies together.
53
+ * If taskkill is unavailable (restricted PATH, locked-down policy)
54
+ * we fall back to `child.kill` so the wrapper at least exits.
55
+ *
56
+ * Exported so the win32 path can be unit-tested by injecting a stub
57
+ * `killTree` runner — the real `taskkillProcessTree` shells out, and
58
+ * we don't want that running during the test suite.
59
+ */
60
+ export declare const killUpdateChild: (child: {
61
+ pid?: number | undefined;
62
+ kill: (signal: NodeJS.Signals) => boolean;
63
+ }, signal: NodeJS.Signals, platform?: NodeJS.Platform, killTree?: (pid: number, onFailure?: () => void) => boolean) => void;
36
64
  export declare const defaultRunUpdate: RunUpdate;
37
65
  export declare const resolveHiveUpdateInstallArgs: (moduleUrl?: string) => string[];
38
66
  interface RunHiveUpdateOptions {
@@ -2,7 +2,9 @@ import { spawn } from 'node:child_process';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
  import { basename, dirname, join } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
+ import { taskkillProcessTree } from '../server/agent-manager-support.js';
5
6
  import { getNpmCommand, INSTALL_COMMAND_ARGS, INSTALL_COMMAND_DISPLAY, PACKAGE_NAME, } from '../server/package-version.js';
7
+ import { buildCmdCallCommand } from '../server/windows-command-line.js';
6
8
  export const HIVE_UPDATE_USAGE = [
7
9
  'Usage:',
8
10
  ' hive update',
@@ -20,25 +22,36 @@ export const HIVE_UPDATE_USAGE = [
20
22
  ' -h, --help Print this help.',
21
23
  ].join('\n');
22
24
  /**
23
- * Build the spawn options for the upgrade child. The non-obvious part is
24
- * Windows: npm ships as `npm.cmd` (a batch shim), and Node 22+ refuses to
25
- * spawn `.cmd` / `.bat` files without `shell: true` after CVE-2024-27980.
26
- * Detect by file extension rather than by `process.platform` so the same
27
- * code path works for both real Windows runs and our cross-platform unit
28
- * tests (which inject `platform: 'win32'` so that `getNpmCommand` returns
29
- * `npm.cmd`).
25
+ * Plan how `defaultRunUpdate` should hand the npm invocation to
26
+ * `child_process.spawn`. Two non-obvious cases collapse here.
30
27
  *
31
- * Known limitation: with `shell: true` the args are stringified through
32
- * cmd.exe without quoting, so an install prefix containing spaces (e.g.
33
- * `C:\Program Files\nodejs`) will be tokenized incorrectly. The common
34
- * Windows prefix `%APPDATA%\npm` does not have this problem; fixing the
35
- * spaces case requires a verbatim `cmd.exe /d /s /c call npm.cmd …`
36
- * wrapper similar to `agent-command-resolver` and is tracked separately.
28
+ * 1. Node 22+ refuses to spawn `.cmd` / `.bat` files directly after
29
+ * CVE-2024-27980 unless `shell: true` is passed. We do not want
30
+ * `shell: true` though its arg-stringification path joins argv
31
+ * without quoting, so an install prefix containing spaces (the
32
+ * common Windows case `C:\Program Files\nodejs`) gets word-split
33
+ * by cmd.exe and `--prefix` only sees the first token, so npm
34
+ * silently installs hive to the wrong directory.
35
+ * 2. Wrapping with `cmd.exe /d /s /c <npm.cmd> <args>` solves both:
36
+ * spawning `cmd.exe` (a native exe) avoids the .cmd refusal, and
37
+ * Node's own argv-quoting builds an lpCommandLine where each
38
+ * space-containing arg is wrapped in double quotes that cmd.exe
39
+ * then re-parses correctly. `/d` skips AutoRun, `/s` keeps the
40
+ * quote-handling consistent with `/c`.
41
+ *
42
+ * Detect by filename suffix instead of `process.platform` so unit
43
+ * tests can inject `platform: 'win32'` and exercise the wrap.
37
44
  */
38
- export const buildSpawnOptionsForCommand = (command) => ({
39
- shell: /\.(cmd|bat)$/i.test(command),
40
- stdio: 'inherit',
41
- });
45
+ export const planSpawnInvocation = (command, args, platform = process.platform) => {
46
+ if (platform === 'win32' && /\.(cmd|bat)$/i.test(command)) {
47
+ return {
48
+ command: 'cmd.exe',
49
+ args: ['/d', '/s', '/c', buildCmdCallCommand(command, args)],
50
+ options: { stdio: 'inherit', windowsHide: false },
51
+ };
52
+ }
53
+ return { command, args: [...args], options: { stdio: 'inherit' } };
54
+ };
42
55
  /**
43
56
  * Signals the upgrade child should receive when the parent runtime is
44
57
  * interrupted. Beyond the POSIX-only SIGTERM/SIGINT, SIGHUP is what
@@ -53,21 +66,46 @@ export const FORWARDED_UPDATE_SIGNALS = [
53
66
  'SIGHUP',
54
67
  'SIGBREAK',
55
68
  ];
69
+ /**
70
+ * Forward a parent-process signal to the spawned npm child. On POSIX
71
+ * we hand the signal straight to the child; on Windows there are no
72
+ * real signals, so `child.kill(SIGTERM)` resolves to TerminateProcess
73
+ * against cmd.exe (our wrapper) only — npm itself, plus any install
74
+ * scripts it spawned, become orphans. `taskkill /pid <pid> /t /f`
75
+ * walks the wrapper's process tree so the whole branch dies together.
76
+ * If taskkill is unavailable (restricted PATH, locked-down policy)
77
+ * we fall back to `child.kill` so the wrapper at least exits.
78
+ *
79
+ * Exported so the win32 path can be unit-tested by injecting a stub
80
+ * `killTree` runner — the real `taskkillProcessTree` shells out, and
81
+ * we don't want that running during the test suite.
82
+ */
83
+ export const killUpdateChild = (child, signal, platform = process.platform, killTree = (pid, onFailure) => taskkillProcessTree(pid, platform, undefined, onFailure)) => {
84
+ const fallback = () => {
85
+ try {
86
+ child.kill(signal);
87
+ }
88
+ catch {
89
+ // child.kill on Windows throws if the signal name isn't
90
+ // implemented; we forward what we can and ignore the rest.
91
+ }
92
+ };
93
+ if (platform === 'win32' && typeof child.pid === 'number' && child.pid > 0) {
94
+ if (killTree(child.pid, fallback))
95
+ return;
96
+ }
97
+ fallback();
98
+ };
56
99
  export const defaultRunUpdate = (command, args) => new Promise((resolve) => {
57
- const child = spawn(command, [...args], buildSpawnOptionsForCommand(command));
100
+ const plan = planSpawnInvocation(command, args);
101
+ const child = spawn(plan.command, plan.args, plan.options);
58
102
  let resolved = false;
59
103
  // Handlers are registered with `once` so they don't accumulate
60
104
  // across invocations and explicitly removed at finalize().
61
105
  const handlers = new Map();
62
106
  for (const signal of FORWARDED_UPDATE_SIGNALS) {
63
107
  const handler = () => {
64
- try {
65
- child.kill(signal);
66
- }
67
- catch {
68
- // child.kill on Windows throws if the signal name isn't
69
- // implemented; we forward what we can and ignore the rest.
70
- }
108
+ killUpdateChild(child, signal);
71
109
  };
72
110
  handlers.set(signal, handler);
73
111
  process.once(signal, handler);
@@ -33,6 +33,31 @@ type RunHiveCommandOptions = {
33
33
  export declare const SHUTDOWN_SIGNALS: readonly ["SIGINT", "SIGTERM", "SIGHUP", "SIGBREAK"];
34
34
  export declare const HIVE_USAGE: string;
35
35
  export declare const handleHiveInfoCommand: (argv: string[]) => boolean;
36
+ /**
37
+ * Resolve the directory where Hive persists its SQLite DB and supporting
38
+ * state. Platform-aware because `~/.config/hive` is a hidden dot-directory
39
+ * convention that Windows Explorer treats as second-class — Windows users
40
+ * can't navigate there from the address bar without typing the full path,
41
+ * and the standard roaming-profile location is `%APPDATA%\<app>` instead.
42
+ *
43
+ * Resolution order:
44
+ * 1. HIVE_DATA_DIR override (any platform, for tests / opinionated users).
45
+ * 2. Windows: %APPDATA%\hive — roaming user state, follows the user
46
+ * across machines on a domain profile. APPDATA, not LOCALAPPDATA,
47
+ * because Hive's DB is user data, not a machine-local cache.
48
+ * Falls back to homedir()\AppData\Roaming\hive when APPDATA is
49
+ * stripped from the env (some Windows Task Scheduler configs do this).
50
+ * 3. POSIX: $XDG_CONFIG_HOME/hive, falling back to ~/.config/hive.
51
+ *
52
+ * Migration: pre-fix Windows installs wrote to ~/.config/hive. When that
53
+ * legacy directory exists but the new %APPDATA%\hive does not, prefer the
54
+ * legacy path so an upgrade does not surface as an empty workspace list.
55
+ * This is a one-way ratchet — once the new location is populated, it wins.
56
+ *
57
+ * Exported so the resolution rules are unit-testable without touching env
58
+ * or the real filesystem.
59
+ */
60
+ export declare const resolveDataDir: (platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv, pathExists?: (path: string) => boolean) => string;
36
61
  /**
37
62
  * Recovery hint formatter for the "port already in use" error. Platform-aware
38
63
  * because the lsof / xargs / kill pipeline is POSIX-only; on Windows a user
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { once } from 'node:events';
3
- import { realpathSync } from 'node:fs';
3
+ import { existsSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
5
  import { join } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { createAgentManager } from '../server/agent-manager.js';
8
8
  import { createApp } from '../server/app.js';
9
9
  import { readPackageVersion } from '../server/package-version.js';
10
+ import { sameFilesystemPath } from '../server/path-canonicalization.js';
10
11
  import { createRuntimeStore } from '../server/runtime-store.js';
11
12
  import { createVersionService } from '../server/version-service.js';
12
13
  import { runHiveUpdateCommand } from './hive-update.js';
@@ -80,7 +81,44 @@ const parsePort = (argv) => {
80
81
  }
81
82
  return parsedPort ?? 3000;
82
83
  };
83
- const resolveDataDir = () => process.env.HIVE_DATA_DIR || join(homedir(), '.config', 'hive');
84
+ /**
85
+ * Resolve the directory where Hive persists its SQLite DB and supporting
86
+ * state. Platform-aware because `~/.config/hive` is a hidden dot-directory
87
+ * convention that Windows Explorer treats as second-class — Windows users
88
+ * can't navigate there from the address bar without typing the full path,
89
+ * and the standard roaming-profile location is `%APPDATA%\<app>` instead.
90
+ *
91
+ * Resolution order:
92
+ * 1. HIVE_DATA_DIR override (any platform, for tests / opinionated users).
93
+ * 2. Windows: %APPDATA%\hive — roaming user state, follows the user
94
+ * across machines on a domain profile. APPDATA, not LOCALAPPDATA,
95
+ * because Hive's DB is user data, not a machine-local cache.
96
+ * Falls back to homedir()\AppData\Roaming\hive when APPDATA is
97
+ * stripped from the env (some Windows Task Scheduler configs do this).
98
+ * 3. POSIX: $XDG_CONFIG_HOME/hive, falling back to ~/.config/hive.
99
+ *
100
+ * Migration: pre-fix Windows installs wrote to ~/.config/hive. When that
101
+ * legacy directory exists but the new %APPDATA%\hive does not, prefer the
102
+ * legacy path so an upgrade does not surface as an empty workspace list.
103
+ * This is a one-way ratchet — once the new location is populated, it wins.
104
+ *
105
+ * Exported so the resolution rules are unit-testable without touching env
106
+ * or the real filesystem.
107
+ */
108
+ export const resolveDataDir = (platform = process.platform, env = process.env, pathExists = existsSync) => {
109
+ const override = env.HIVE_DATA_DIR;
110
+ if (override)
111
+ return override;
112
+ if (platform === 'win32') {
113
+ const appData = env.APPDATA ?? join(homedir(), 'AppData', 'Roaming');
114
+ const target = join(appData, 'hive');
115
+ const legacy = join(homedir(), '.config', 'hive');
116
+ if (!pathExists(target) && pathExists(legacy))
117
+ return legacy;
118
+ return target;
119
+ }
120
+ return join(env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'hive');
121
+ };
84
122
  const maybePrintUpdateHint = async (versionService) => {
85
123
  const info = await versionService.getVersionInfo();
86
124
  if (!info.update_available)
@@ -210,7 +248,7 @@ export const runHiveCommand = async (argv, options = {}) => {
210
248
  };
211
249
  };
212
250
  const isMainModule = process.argv[1]
213
- ? fileURLToPath(import.meta.url) === realpathSync(process.argv[1])
251
+ ? sameFilesystemPath(fileURLToPath(import.meta.url), process.argv[1])
214
252
  : false;
215
253
  if (isMainModule) {
216
254
  const argv = process.argv.slice(2);
@@ -10,6 +10,7 @@ export interface ParsedReportArgs {
10
10
  }
11
11
  export declare const parseReportArgs: (args: string[], command?: string) => ParsedReportArgs;
12
12
  export declare const parseCancelArgs: (args: string[]) => ParsedCancelArgs;
13
+ export declare const decodeStdinBuffer: (buffer: Buffer) => string;
13
14
  export declare const readStdinToString: (command?: string) => Promise<string>;
14
15
  export declare const runTeamCommand: (argv: string[]) => Promise<void>;
15
16
  export {};