@songsid/agend 2.3.0-beta.22 → 2.3.0-beta.23

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 (256) hide show
  1. package/README.md +2 -2
  2. package/README.zh-TW.md +2 -2
  3. package/dist/agent-cli.js +4 -2
  4. package/dist/agent-cli.js.map +1 -1
  5. package/dist/agent-endpoint.d.ts +1 -0
  6. package/dist/agent-endpoint.js +4 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-whats-new.d.ts +5 -0
  9. package/dist/agent-whats-new.js +16 -1
  10. package/dist/agent-whats-new.js.map +1 -1
  11. package/dist/auth-api.d.ts +7 -0
  12. package/dist/auth-api.js +8 -2
  13. package/dist/auth-api.js.map +1 -1
  14. package/dist/backend/codex-package.d.ts +30 -0
  15. package/dist/backend/codex-package.js +133 -0
  16. package/dist/backend/codex-package.js.map +1 -0
  17. package/dist/backend/codex.d.ts +2 -0
  18. package/dist/backend/codex.js +9 -2
  19. package/dist/backend/codex.js.map +1 -1
  20. package/dist/backend/kiro.d.ts +9 -1
  21. package/dist/backend/kiro.js +102 -1
  22. package/dist/backend/kiro.js.map +1 -1
  23. package/dist/backend/opencode-permission.js +1 -1
  24. package/dist/backend/opencode-permission.js.map +1 -1
  25. package/dist/backend/opencode.js +2 -1
  26. package/dist/backend/opencode.js.map +1 -1
  27. package/dist/binary-probe.js +1 -1
  28. package/dist/bug-report-cli.js +1 -1
  29. package/dist/bundled-skills.d.ts +7 -3
  30. package/dist/bundled-skills.js +16 -7
  31. package/dist/bundled-skills.js.map +1 -1
  32. package/dist/channel/adapters/discord.js +3 -3
  33. package/dist/channel/adapters/discord.js.map +1 -1
  34. package/dist/channel/mcp-server.js +12 -4
  35. package/dist/channel/mcp-server.js.map +1 -1
  36. package/dist/channel/mcp-tools.d.ts +3 -1
  37. package/dist/channel/mcp-tools.js +36 -0
  38. package/dist/channel/mcp-tools.js.map +1 -1
  39. package/dist/child-process.d.ts +21 -0
  40. package/dist/child-process.js +54 -0
  41. package/dist/child-process.js.map +1 -0
  42. package/dist/cli.js +167 -182
  43. package/dist/cli.js.map +1 -1
  44. package/dist/command-table.d.ts +1 -1
  45. package/dist/command-table.js +1 -1
  46. package/dist/command-table.js.map +1 -1
  47. package/dist/config-validator.js +20 -1
  48. package/dist/config-validator.js.map +1 -1
  49. package/dist/config.js +3 -1
  50. package/dist/config.js.map +1 -1
  51. package/dist/daemon.d.ts +8 -0
  52. package/dist/daemon.js +31 -8
  53. package/dist/daemon.js.map +1 -1
  54. package/dist/darwin-memory.js +1 -1
  55. package/dist/doctor.d.ts +5 -0
  56. package/dist/doctor.js +42 -0
  57. package/dist/doctor.js.map +1 -1
  58. package/dist/export-import.js +1 -1
  59. package/dist/export-import.js.map +1 -1
  60. package/dist/fleet-level-config.d.ts +1 -1
  61. package/dist/fleet-level-config.js +2 -0
  62. package/dist/fleet-level-config.js.map +1 -1
  63. package/dist/fleet-manager.d.ts +45 -0
  64. package/dist/fleet-manager.js +301 -21
  65. package/dist/fleet-manager.js.map +1 -1
  66. package/dist/full-restart.d.ts +5 -5
  67. package/dist/full-restart.js +5 -5
  68. package/dist/full-restart.js.map +1 -1
  69. package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -1
  70. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +7 -8
  71. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
  72. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +319 -0
  73. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +529 -0
  74. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +16 -3
  75. package/dist/general-knowledge/steering/core-rules.md +1 -1
  76. package/dist/instance-config-impact.js +10 -3
  77. package/dist/instance-config-impact.js.map +1 -1
  78. package/dist/instance-lifecycle.js +4 -4
  79. package/dist/locale.js +118 -4
  80. package/dist/locale.js.map +1 -1
  81. package/dist/login-flows.js +1 -1
  82. package/dist/login-shell.js +3 -1
  83. package/dist/login-shell.js.map +1 -1
  84. package/dist/ls-rows.js +1 -1
  85. package/dist/needs-you-hub.d.ts +98 -1
  86. package/dist/needs-you-hub.js +313 -44
  87. package/dist/needs-you-hub.js.map +1 -1
  88. package/dist/needs-you-live.d.ts +5 -2
  89. package/dist/needs-you-live.js +6 -3
  90. package/dist/needs-you-live.js.map +1 -1
  91. package/dist/needs-you.d.ts +58 -3
  92. package/dist/needs-you.js +36 -3
  93. package/dist/needs-you.js.map +1 -1
  94. package/dist/outbound-schemas.d.ts +3 -3
  95. package/dist/public-link-progress.d.ts +12 -1
  96. package/dist/public-link-progress.js +5 -10
  97. package/dist/public-link-progress.js.map +1 -1
  98. package/dist/public-tunnel-config.d.ts +5 -0
  99. package/dist/public-tunnel-config.js +45 -1
  100. package/dist/public-tunnel-config.js.map +1 -1
  101. package/dist/public-web-link.js +6 -5
  102. package/dist/public-web-link.js.map +1 -1
  103. package/dist/quickstart.js +14 -8
  104. package/dist/quickstart.js.map +1 -1
  105. package/dist/resource-report.js +1 -1
  106. package/dist/scheduler/task-board.d.ts +41 -1
  107. package/dist/scheduler/task-board.js +150 -4
  108. package/dist/scheduler/task-board.js.map +1 -1
  109. package/dist/sd-notify.js +2 -1
  110. package/dist/sd-notify.js.map +1 -1
  111. package/dist/service-installer.d.ts +69 -0
  112. package/dist/service-installer.js +181 -0
  113. package/dist/service-installer.js.map +1 -1
  114. package/dist/service-owner.d.ts +44 -0
  115. package/dist/service-owner.js +48 -0
  116. package/dist/service-owner.js.map +1 -0
  117. package/dist/settings-api.d.ts +4 -0
  118. package/dist/settings-api.js +21 -3
  119. package/dist/settings-api.js.map +1 -1
  120. package/dist/settings-change.js +85 -2
  121. package/dist/settings-change.js.map +1 -1
  122. package/dist/settings-effect.js +2 -0
  123. package/dist/settings-effect.js.map +1 -1
  124. package/dist/settings-http-confirmation.d.ts +2 -0
  125. package/dist/settings-http-confirmation.js +3 -2
  126. package/dist/settings-http-confirmation.js.map +1 -1
  127. package/dist/settings-request-capability.d.ts +4 -1
  128. package/dist/settings-request-capability.js +11 -3
  129. package/dist/settings-request-capability.js.map +1 -1
  130. package/dist/setup-host.js +1 -1
  131. package/dist/setup-wizard.js +1 -1
  132. package/dist/setup-wizard.js.map +1 -1
  133. package/dist/task-board-summary.d.ts +101 -0
  134. package/dist/task-board-summary.js +177 -0
  135. package/dist/task-board-summary.js.map +1 -0
  136. package/dist/task-board-waker.d.ts +8 -1
  137. package/dist/task-board-waker.js +15 -6
  138. package/dist/task-board-waker.js.map +1 -1
  139. package/dist/tmux-control.js +1 -1
  140. package/dist/tmux-control.js.map +1 -1
  141. package/dist/tmux-exec-helper.d.ts +5 -1
  142. package/dist/tmux-exec-helper.js +13 -21
  143. package/dist/tmux-exec-helper.js.map +1 -1
  144. package/dist/tmux-manager.js +1 -1
  145. package/dist/tmux-read.js +1 -1
  146. package/dist/tmux-read.js.map +1 -1
  147. package/dist/topic-commands.d.ts +3 -0
  148. package/dist/topic-commands.js +29 -18
  149. package/dist/topic-commands.js.map +1 -1
  150. package/dist/transcript-monitor.js +5 -0
  151. package/dist/transcript-monitor.js.map +1 -1
  152. package/dist/transcript-sources.d.ts +2 -0
  153. package/dist/transcript-sources.js +4 -0
  154. package/dist/transcript-sources.js.map +1 -1
  155. package/dist/tunnel/cloudflared-install.js +3 -1
  156. package/dist/tunnel/cloudflared-install.js.map +1 -1
  157. package/dist/tunnel/cloudflared.d.ts +1 -1
  158. package/dist/tunnel/cloudflared.js +51 -10
  159. package/dist/tunnel/cloudflared.js.map +1 -1
  160. package/dist/tunnel/named-diagnostics.d.ts +13 -0
  161. package/dist/tunnel/named-diagnostics.js +52 -0
  162. package/dist/tunnel/named-diagnostics.js.map +1 -0
  163. package/dist/tunnel/types.d.ts +2 -1
  164. package/dist/tunnel/types.js.map +1 -1
  165. package/dist/types.d.ts +9 -0
  166. package/dist/ui/app-effects.js +10 -4
  167. package/dist/ui/chat-strings.js +3 -0
  168. package/dist/ui/chat-thread.js +14 -7
  169. package/dist/ui/effect-trial-offer.js +2 -31
  170. package/dist/ui/effects-waker.js +2 -70
  171. package/dist/ui/fleet-task-card.js +5 -5
  172. package/dist/ui/fleet-task-common.js +41 -7
  173. package/dist/ui/fleet-task-work.js +107 -0
  174. package/dist/ui/fleet-tasks.js +46 -12
  175. package/dist/ui/image-lightbox.js +39 -1
  176. package/dist/ui/image-zoom.js +242 -0
  177. package/dist/ui/panel-chat.js +24 -13
  178. package/dist/ui/panel-details.js +2 -0
  179. package/dist/ui/panel-fleet.js +6 -2
  180. package/dist/ui/panel-needs.js +21 -6
  181. package/dist/ui/panel-settings.js +35 -13
  182. package/dist/ui/preview-panel.js +2 -1
  183. package/dist/ui/search-dialog.js +2 -1
  184. package/dist/ui/settings-appearance.js +33 -8
  185. package/dist/ui/settings-dialogs.js +33 -17
  186. package/dist/ui/settings-discord.js +6 -2
  187. package/dist/ui/settings-guests.js +2 -1
  188. package/dist/ui/settings-model.js +2 -11
  189. package/dist/ui/settings-mods.js +6 -3
  190. package/dist/ui/settings-strings.js +2 -0
  191. package/dist/ui/settings-token.js +3 -1
  192. package/dist/ui/settings-web-authority.js +6 -3
  193. package/dist/ui/settings-wizard.js +13 -7
  194. package/dist/ui/shared/app-i18n.js +2 -2
  195. package/dist/ui/shared/app-needs.js +2 -2
  196. package/dist/ui/shared/app-store.js +2 -2
  197. package/dist/ui/shared/app.css +80 -9
  198. package/dist/ui/shared/app.js +5 -1
  199. package/dist/ui/shared/effect-app-runtime.js +7 -5
  200. package/dist/ui/shared/effect-chat-runtime.js +2 -7
  201. package/dist/ui/shared/effect-cursor.js +3 -2
  202. package/dist/ui/shared/effect-display-metadata.js +39 -0
  203. package/dist/ui/shared/effect-registry.js +4 -2
  204. package/dist/ui/shared/effect-runtime-loader.js +3 -1
  205. package/dist/ui/shared/effect-stock-trust.js +1 -1
  206. package/dist/ui/shared/feature-flags.js +127 -0
  207. package/dist/ui/shared/identity-color.js +6 -3
  208. package/dist/ui/shared/instance-nav.js +3 -2
  209. package/dist/ui/shared/text-undo.js +210 -0
  210. package/dist/ui/shared/ui-dialog.js +4 -3
  211. package/dist/ui/shared/ui-icons.js +2 -0
  212. package/dist/update-dispatch.js +1 -1
  213. package/dist/update-launch.d.ts +1 -1
  214. package/dist/update-launch.js +1 -2
  215. package/dist/update-launch.js.map +1 -1
  216. package/dist/usage/providers.js +1 -1
  217. package/dist/usage/providers.js.map +1 -1
  218. package/dist/view-api.js +1 -1
  219. package/dist/web-api.d.ts +6 -0
  220. package/dist/web-api.js +152 -14
  221. package/dist/web-api.js.map +1 -1
  222. package/dist/web-appearance-tools.d.ts +1 -0
  223. package/dist/web-appearance-tools.js +7 -3
  224. package/dist/web-appearance-tools.js.map +1 -1
  225. package/dist/web-appearance.d.ts +4 -10
  226. package/dist/web-appearance.js +4 -3
  227. package/dist/web-appearance.js.map +1 -1
  228. package/dist/web-chat-history.d.ts +14 -0
  229. package/dist/web-chat-history.js +26 -1
  230. package/dist/web-chat-history.js.map +1 -1
  231. package/dist/web-experimental-http.d.ts +2 -0
  232. package/dist/web-experimental-http.js +12 -0
  233. package/dist/web-experimental-http.js.map +1 -0
  234. package/dist/web-experimental.d.ts +11 -0
  235. package/dist/web-experimental.js +49 -0
  236. package/dist/web-experimental.js.map +1 -0
  237. package/dist/web-guest-access.d.ts +1 -0
  238. package/dist/web-guest-access.js +4 -2
  239. package/dist/web-guest-access.js.map +1 -1
  240. package/dist/web-mod-store.d.ts +2 -0
  241. package/dist/web-mod-store.js +10 -0
  242. package/dist/web-mod-store.js.map +1 -1
  243. package/dist/web-preview.d.ts +2 -0
  244. package/dist/web-preview.js +2 -0
  245. package/dist/web-preview.js.map +1 -1
  246. package/dist/web-settings-patch.d.ts +2 -1
  247. package/dist/web-settings-patch.js +5 -1
  248. package/dist/web-settings-patch.js.map +1 -1
  249. package/dist/web-settings-tools.d.ts +1 -0
  250. package/dist/web-settings-tools.js +14 -10
  251. package/dist/web-settings-tools.js.map +1 -1
  252. package/dist/web-terminal.js +1 -1
  253. package/dist/web-theme-store.d.ts +2 -2
  254. package/dist/web-theme-store.js +6 -3
  255. package/dist/web-theme-store.js.map +1 -1
  256. package/package.json +1 -1
@@ -0,0 +1,21 @@
1
+ /**
2
+ * #2374: `spawn` and `execFile` for the fleet's own code, with every piped stdio stream heard.
3
+ *
4
+ * A child's stdin/stdout/stderr pipes are EventEmitters of their own: `child.on("error")` and the `execFile` callback
5
+ * never see their errors, and an `error` nobody listens to is an uncaught exception, which stops the fleet (#2243).
6
+ * Here each pipe gets a listener for its whole life. By default a pipe error kills the child and is passed on as the
7
+ * child's own `error` (when anything listens for one), so the call fails with it: `execFile`'s callback and promise get
8
+ * the pipe error even if the child then exits 0, or had already exited while something else held the pipe, and nothing
9
+ * half-read passes for an answer. A caller that listens on the pipe itself keeps its own handling; one with a different
10
+ * response passes `onError` to `guardStdio`. The `*Sync` forms have no streams and come from node:child_process directly.
11
+ */
12
+ import * as cp from "node:child_process";
13
+ /**
14
+ * An `error` listener on each of the child's pipes. The default response kills the child and passes the error on as
15
+ * the child's `error` (only to a listener: unheard, it would be uncaught again), unless the caller heard that pipe's
16
+ * errors itself (another listener): then the caller's own handling is the response. Tolerates test doubles.
17
+ */
18
+ export declare function guardStdio<T>(child: T, onError?: (err: Error) => void): T;
19
+ export declare const spawn: typeof cp.spawn;
20
+ export declare const execFile: typeof cp.execFile;
21
+ export declare const exec: typeof cp.exec;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * #2374: `spawn` and `execFile` for the fleet's own code, with every piped stdio stream heard.
3
+ *
4
+ * A child's stdin/stdout/stderr pipes are EventEmitters of their own: `child.on("error")` and the `execFile` callback
5
+ * never see their errors, and an `error` nobody listens to is an uncaught exception, which stops the fleet (#2243).
6
+ * Here each pipe gets a listener for its whole life. By default a pipe error kills the child and is passed on as the
7
+ * child's own `error` (when anything listens for one), so the call fails with it: `execFile`'s callback and promise get
8
+ * the pipe error even if the child then exits 0, or had already exited while something else held the pipe, and nothing
9
+ * half-read passes for an answer. A caller that listens on the pipe itself keeps its own handling; one with a different
10
+ * response passes `onError` to `guardStdio`. The `*Sync` forms have no streams and come from node:child_process directly.
11
+ */
12
+ import * as cp from "node:child_process";
13
+ import { promisify } from "node:util";
14
+ /**
15
+ * An `error` listener on each of the child's pipes. The default response kills the child and passes the error on as
16
+ * the child's `error` (only to a listener: unheard, it would be uncaught again), unless the caller heard that pipe's
17
+ * errors itself (another listener): then the caller's own handling is the response. Tolerates test doubles.
18
+ */
19
+ export function guardStdio(child, onError) {
20
+ const c = child;
21
+ if (!c)
22
+ return child;
23
+ for (const stream of [c.stdin, c.stdout, c.stderr]) {
24
+ if (!stream || typeof stream.on !== "function")
25
+ continue;
26
+ stream.on("error", onError ?? ((err) => {
27
+ if ((stream.listenerCount?.("error") ?? 1) > 1)
28
+ return;
29
+ try {
30
+ c.kill?.();
31
+ }
32
+ catch { /* already gone */ }
33
+ if ((c.listenerCount?.("error") ?? 0) > 0)
34
+ c.emit?.("error", err);
35
+ }));
36
+ }
37
+ return child;
38
+ }
39
+ export const spawn = ((...args) => guardStdio(cp.spawn(...args)));
40
+ /** `execFile`/`exec` guarded; `promisify` of it keeps node's own { stdout, stderr } form, with its `.child` guarded. */
41
+ function guarded(original) {
42
+ const call = (...args) => guardStdio(original()(...args));
43
+ Object.defineProperty(call, promisify.custom, {
44
+ value: (...args) => {
45
+ const promise = promisify(original())(...args);
46
+ guardStdio(promise.child);
47
+ return promise;
48
+ },
49
+ });
50
+ return call;
51
+ }
52
+ export const execFile = guarded(() => cp.execFile);
53
+ export const exec = guarded(() => cp.exec);
54
+ //# sourceMappingURL=child-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-process.js","sourceRoot":"","sources":["../src/child-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAMtC;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAI,KAAQ,EAAE,OAA8B;IACpE,MAAM,CAAC,GAAG,KAAiC,CAAC;IAC5C,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACrB,KAAK,MAAM,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAc,EAAE,CAAC;QAChE,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,UAAU;YAAE,SAAS;QACzD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC,GAAU,EAAE,EAAE;YAC5C,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;gBAAE,OAAO;YACvD,IAAI,CAAC;gBAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;YAChD,IAAI,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;gBAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC,CAAC;IACN,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,IAAe,EAAE,EAAE,CAC3C,UAAU,CAAE,EAAE,CAAC,KAA8C,CAAC,GAAG,IAAI,CAAC,CAAC,CAAoB,CAAC;AAE9F,wHAAwH;AACxH,SAAS,OAAO,CAAI,QAAiB;IACnC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,UAAU,CAAE,QAAQ,EAA2C,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC/G,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE;QAC5C,KAAK,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE;YAC5B,MAAM,OAAO,GAAI,SAAS,CAAC,QAAQ,EAAW,CAAiE,CAAC,GAAG,IAAI,CAAC,CAAC;YACzH,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC1B,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC,CAAC;IACH,OAAO,IAAoB,CAAC;AAC9B,CAAC;AACD,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;AACnD,MAAM,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC"}
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ applyNetworkReliabilityDefaults();
13
13
  // never restarts into a binary that would crash at the first native DB open.
14
14
  import { checkNodeVersion } from "./node-version-guard.js";
15
15
  checkNodeVersion();
16
- import { Command } from "commander";
16
+ import { Command, Option } from "commander";
17
17
  import { registerBugReportCommand } from "./bug-report-cli.js";
18
18
  import { join, dirname } from "node:path";
19
19
  import { SchedulerDb } from "./scheduler/db.js";
@@ -21,7 +21,8 @@ import { Cron } from "croner";
21
21
  import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, mkdirSync, chmodSync, readdirSync, rmSync, statSync, realpathSync, openSync, writeSync, closeSync, } from "node:fs";
22
22
  import { homedir, totalmem, freemem, userInfo } from "node:os";
23
23
  import { fileURLToPath } from "node:url";
24
- import { spawn, spawnSync, execSync, execFileSync } from "node:child_process";
24
+ import { spawnSync, execSync, execFileSync } from "node:child_process";
25
+ import { spawn } from "./child-process.js";
25
26
  import { getAgendHome, getTmuxSocketName, isDefaultAgendHome } from "./paths.js";
26
27
  import { hasPausedMarker } from "./pause-marker.js";
27
28
  import { COMPLETION_SHELLS, completionScript } from "./completion.js";
@@ -31,7 +32,7 @@ import { describeSignalSource, forceAllowed, gateFleetControl, recordInstanceCon
31
32
  import { FLEET_STOP_TIMEOUT_MS, stopDetachedOwner, systemdStopTimeoutMs, detachedProcessStart, detachedProcessExited } from "./fleet-stop-budget.js";
32
33
  import { acquireFleetLock, isFleetStartCommandLine, readProcessCommandLine, releaseProcessFleetLock, setProcessFleetLock } from "./fleet-lock.js";
33
34
  import { limitFleetCoreDumps } from "./coredump-filter.js";
34
- import { SYSTEMD_RESTART_TIMEOUT_MS } from "./service-installer.js";
35
+ import { SYSTEMD_RESTART_TIMEOUT_MS, getServicePath, getSystemServicePath } from "./service-installer.js";
35
36
  import { selectSystemdRestartTarget, SYSTEMD_RESTART_INDETERMINATE_EXIT_CODE, } from "./service-restart-selection.js";
36
37
  import { loadRawFleetConfig } from "./config.js";
37
38
  import { setLocale, t } from "./locale.js";
@@ -40,6 +41,7 @@ import { DeliveryStatusArgs } from "./outbound-schemas.js";
40
41
  import { requestCpuProfile } from "./profile-control.js";
41
42
  import { requestSettingsConfirmation } from "./settings-control.js";
42
43
  import { canonicalCliEntry, selfCommand } from "./cli-entry.js";
44
+ import { DIRECT_FLEET_START_ENV, redirectsToService, startedByServiceManager } from "./service-owner.js";
43
45
  import { handOff, shouldHandOff } from "./launchd-handoff.js";
44
46
  /** Prefix tmux args with -L when socket isolation is active. */
45
47
  function tmuxArgs(args) {
@@ -189,7 +191,7 @@ settingsCommand.command("confirm <id>").option("--yes", "Explicit noninteractive
189
191
  process.exitCode = 1;
190
192
  }
191
193
  });
192
- settingsCommand.command("reject <id>").action(async (id) => {
194
+ settingsCommand.command("reject <id>").description("Reject a pending Settings change on this host").action(async (id) => {
193
195
  try {
194
196
  const inspected = await requestSettingsConfirmation(DATA_DIR, { action: "inspect", id });
195
197
  console.log(`Source: ${inspected.pending_change.source}\nRequester: ${inspected.pending_change.requested_by}\n${inspected.pending_change.summary.join("\n")}`);
@@ -218,12 +220,24 @@ fleet
218
220
  .command("start")
219
221
  .description("Start fleet or specific instance")
220
222
  .argument("[instance]", "Specific instance to start")
221
- .action(async (instance) => {
223
+ .option("--direct", "Start here even though an AgEnD service is installed (operators only: the service cannot run on this host)")
224
+ .action(async (instance, opts) => {
222
225
  // Root running fleet = sandbox environment (e.g. Docker, WSL root service)
223
226
  // Set IS_SANDBOX so child processes (claude-code) don't refuse to run
224
227
  if (process.getuid?.() === 0 && !process.env.IS_SANDBOX) {
225
228
  process.env.IS_SANDBOX = "1";
226
229
  }
230
+ const marked = process.env[DIRECT_FLEET_START_ENV] === "1";
231
+ delete process.env[DIRECT_FLEET_START_ENV];
232
+ // #2356: the way out when the installed service cannot run here (never activated, no GUI session for launchd) — an
233
+ // operator's own decision, as `restart --force` is; the fleet's singleton lock still refuses a second fleet.
234
+ if (opts?.direct && !forceAllowed()) {
235
+ console.error(" ✗ --direct is for an operator's own shell, not a fleet agent session or the fleet's own commands. Nothing was started.");
236
+ process.exit(1);
237
+ }
238
+ const direct = marked || !!opts?.direct;
239
+ if (!instance && redirectToService("start", [], direct))
240
+ return;
227
241
  if (instance) {
228
242
  // If fleet daemon is already running, delegate via HTTP API
229
243
  const { loadFleetConfig } = await import("./config.js");
@@ -318,6 +332,8 @@ fleet
318
332
  .argument("[instance]", "Specific instance to stop")
319
333
  .option("-y, --yes", "Confirm when run from a fleet agent session (this affects every instance)")
320
334
  .action(async (instance, opts) => {
335
+ if (!instance && redirectToService("stop", opts?.yes ? ["--yes"] : [], false))
336
+ return;
321
337
  if (instance)
322
338
  recordInstanceControl(DATA_DIR, "instance-stop", instance);
323
339
  else if (!gateFleetControl(DATA_DIR, "fleet-stop", { yes: opts?.yes }))
@@ -392,159 +408,37 @@ fleet
392
408
  });
393
409
  fleet
394
410
  .command("restart")
395
- .description("Graceful restart: wait for instances to idle, then restart")
396
- .argument("[instance]", "Specific instance to restart (immediate, no idle wait)")
397
- .option("--reload", "Full process restart to load new code")
411
+ .description("Deprecated: runs `agend restart` (removed in 2.4)")
412
+ .argument("[instance]", "Restart only this instance (now `agend restart <instance>`)")
413
+ .option("--reload", "Ignored: every full restart is `agend restart`")
398
414
  .option("-y, --yes", "Confirm when run from a fleet agent session (this affects every instance)")
399
- .action(async (instance, opts) => {
415
+ .action((instance, opts) => {
400
416
  if (instance && opts?.reload) {
401
417
  console.error("--reload restarts the entire fleet process. Cannot combine with instance name.");
402
418
  process.exit(1);
403
419
  }
404
- if (instance)
405
- recordInstanceControl(DATA_DIR, "instance-restart", instance);
406
- else if (!gateFleetControl(DATA_DIR, opts?.reload ? "fleet-restart-reload" : "fleet-restart", { yes: opts?.yes }))
407
- process.exit(1);
408
- if (instance) {
409
- // Single instance restart via fleet's HTTP API
410
- const { loadFleetConfig } = await import("./config.js");
411
- const fleet = loadFleetConfig(FLEET_CONFIG_PATH);
412
- const port = fleet.health_port ?? 19280;
413
- let token = "";
414
- try {
415
- token = readFileSync(join(DATA_DIR, "web.token"), "utf-8").trim();
416
- }
417
- catch { /* fleet may not be running */ }
418
- try {
419
- const resp = await fetch(`http://127.0.0.1:${port}/restart/${encodeURIComponent(instance)}`, {
420
- method: "POST",
421
- headers: token ? { "X-Agend-Token": token } : {},
422
- });
423
- const body = await resp.json();
424
- if (resp.ok) {
425
- console.log(`Instance "${instance}" restarted (immediate)`);
426
- }
427
- else {
428
- console.error(`Restart failed: ${body.error ?? resp.statusText}`);
429
- process.exit(1);
430
- }
431
- }
432
- catch (err) {
433
- console.error(`Cannot connect to fleet (port ${port}). Is the fleet running?`);
434
- process.exit(1);
435
- }
436
- return;
437
- }
438
- const pidPath = join(DATA_DIR, "fleet.pid");
439
- if (!existsSync(pidPath)) {
440
- console.error("Fleet is not running (no PID file found)");
441
- process.exit(1);
442
- }
443
- const pid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
444
- if (!Number.isInteger(pid) || pid <= 0) {
445
- console.error(`Fleet PID file at ${pidPath} is corrupted`);
446
- process.exit(1);
447
- }
448
- if (opts?.reload) {
449
- // Check if managed by launchd — if so, just signal and let launchd restart
450
- let managedByLaunchd = false;
451
- try {
452
- const ppid = parseInt(execFileSync("ps", ["-o", "ppid=", "-p", String(pid)]).toString().trim(), 10);
453
- managedByLaunchd = ppid === 1;
454
- }
455
- catch { /* ignore */ }
456
- try {
457
- process.kill(pid, "SIGUSR1");
458
- }
459
- catch {
460
- console.error("Failed to send reload signal (process may have exited)");
461
- process.exit(1);
462
- }
463
- if (managedByLaunchd) {
464
- console.log("Fleet is managed by launchd — sent reload signal.");
465
- console.log("launchd will automatically restart with new code.");
466
- // Wait briefly for old process to exit, then confirm new one started
467
- const deadline = Date.now() + 6 * 60 * 1000;
468
- while (Date.now() < deadline) {
469
- try {
470
- process.kill(pid, 0);
471
- await new Promise(r => setTimeout(r, 500));
472
- }
473
- catch {
474
- break;
475
- }
476
- }
477
- // Wait for launchd to start new process
478
- await new Promise(r => setTimeout(r, 2000));
479
- if (existsSync(pidPath)) {
480
- const newPid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
481
- if (newPid !== pid) {
482
- console.log(`New fleet process started (PID ${newPid})`);
483
- }
484
- }
485
- return;
486
- }
487
- console.log("Full restart signal sent — waiting for fleet to stop...");
488
- // Wait for old process to exit (up to 6 minutes: 5 min idle wait + buffer)
489
- const deadline = Date.now() + 6 * 60 * 1000;
490
- while (Date.now() < deadline) {
491
- try {
492
- process.kill(pid, 0); // check if alive
493
- await new Promise(r => setTimeout(r, 500));
494
- }
495
- catch {
496
- break; // process exited
497
- }
498
- }
499
- // Verify it actually exited
500
- try {
501
- process.kill(pid, 0);
502
- console.error("Old fleet process still running after timeout — aborting");
503
- process.exit(1);
504
- }
505
- catch {
506
- // Good, it exited
507
- }
508
- console.log("Old fleet stopped. Starting with new code...");
509
- // Start new fleet in this process (new Node.js process = new code loaded)
510
- if (!claimFleetSingleton())
511
- return;
512
- const { FleetManager } = await import("./fleet-manager.js");
513
- const fm = new FleetManager(DATA_DIR);
514
- const handlers = installFleetProcessHandlers({
515
- dataDir: DATA_DIR, manager: fm, runtime: process, console,
516
- describeSignal: () => describeSignalSource(DATA_DIR, "SIGTERM"),
517
- });
518
- if (handlers.stopping)
519
- return;
520
- await fm.startAll(FLEET_CONFIG_PATH);
521
- console.log("Fleet restarted with new code");
522
- }
523
- else {
524
- // Instance-only restart (existing behavior)
525
- try {
526
- process.kill(pid, "SIGUSR2");
527
- console.log("Graceful restart signal sent — fleet will restart when all instances are idle");
528
- }
529
- catch {
530
- console.error("Failed to send restart signal (process may have exited)");
531
- process.exit(1);
532
- }
533
- }
420
+ // #2356: no restart of its own any more. Its SIGUSR1 reload made the fleet exit 0 and took over in this process, so
421
+ // under a Restart=on-failure unit the fleet stayed down (and this process, in an agent pane, died with the unit's
422
+ // cgroup). It runs `agend restart` itself — this Node, this CLI — with the same gate, guard and exit code.
423
+ const args = instance ? ["restart", instance] : ["restart", ...(opts?.yes ? ["--yes"] : [])];
424
+ console.error(`⚠ \`agend fleet restart\` is deprecated and goes away in 2.4 — running \`agend ${args.join(" ")}\`.`);
425
+ const self = selfCommand(args);
426
+ const result = spawnSync(self.command, self.args, { stdio: "inherit" });
427
+ process.exit(result.status ?? 1);
534
428
  });
535
429
  fleet
536
- .command("status")
537
- .description("Show fleet status, workspace disk usage, unregistered instance directories, and host memory/swap")
430
+ .command("status", { hidden: true })
431
+ .description("Alias of `agend ls --resources`")
538
432
  .option("--json", "Output as JSON")
539
433
  .action(async (opts) => {
540
- // Delegate to the `ls` command implementation
541
434
  await lsAction({ ...opts, resources: true });
542
435
  });
543
436
  fleet
544
- .command("logs")
545
- .description("Alias for `agend logs`")
437
+ .command("logs", { hidden: true })
438
+ .description("Removed; use `agend logs`")
546
439
  .action(() => {
547
- console.log("Use `agend logs` instead. Run `agend logs --help` for options.");
440
+ console.error("`agend fleet logs` was removed. Use `agend logs`.");
441
+ process.exitCode = 1;
548
442
  });
549
443
  fleet
550
444
  .command("history")
@@ -876,9 +770,6 @@ backend
876
770
  console.error(`Unknown backend: ${backendName}. Available: ${BACKENDS.map(b => b.id).join(", ")}`);
877
771
  process.exit(1);
878
772
  }
879
- if (info.deprecated) {
880
- console.warn(`⚠️ ${info.label} is deprecated (stops 2026-06-18). Consider switching to backend: antigravity`);
881
- }
882
773
  let issues = 0;
883
774
  const ok = (msg) => console.log(` \x1b[32m✓\x1b[0m ${msg}`);
884
775
  const fail = (msg) => { issues++; console.log(` \x1b[31m✗\x1b[0m ${msg}`); };
@@ -1142,7 +1033,7 @@ async function doctorMcp() {
1142
1033
  }
1143
1034
  if (!ipcOk) {
1144
1035
  issues.push("IPC ✗ (socket not responding)");
1145
- suggestions.push("agend fleet restart");
1036
+ suggestions.push("agend restart");
1146
1037
  }
1147
1038
  // 2. MCP config check
1148
1039
  let mcpOk = false;
@@ -1894,9 +1785,12 @@ program
1894
1785
  program
1895
1786
  .command("install")
1896
1787
  .description("Install the AgEnD system service and start it by default")
1897
- .option("--activate", "Deprecated; service activation is now the default")
1788
+ .addOption(new Option("--activate").hideHelp())
1898
1789
  .option("--no-activate", "Write the service file without activating it")
1899
1790
  .action(async (opts) => {
1791
+ if (process.argv.includes("--activate")) {
1792
+ console.error("`agend install --activate` was removed; service activation is the default.");
1793
+ }
1900
1794
  const { installService, activateService, detectPlatform, renderLaunchdPlist, launchdPlistPath, getServicePath, getSystemServicePath, serviceHomeFor } = await import("./service-installer.js");
1901
1795
  // #1631 B: an older updater (2.2.0-beta.1 and before) refreshes a hand-started fleet's service too, through this
1902
1796
  // command — which would CREATE a unit or plist, and every later update would then take a manager it may not reach
@@ -1977,6 +1871,13 @@ program
1977
1871
  process.exitCode = 1;
1978
1872
  }
1979
1873
  }
1874
+ // #2331: a host whose fleet runs under the system unit gets no second, user unit from the refresh — it would only
1875
+ // sit beside it unused, and be named as a leftover on every update after.
1876
+ if (systemUnit) {
1877
+ console.log(t("install.path", systemUnit));
1878
+ console.log(t("install.updated"));
1879
+ return;
1880
+ }
1980
1881
  const svcPath = installService(serviceVars);
1981
1882
  console.log(t("install.path", svcPath));
1982
1883
  if (opts.activate !== false) {
@@ -2075,7 +1976,17 @@ program
2075
1976
  .action(async (opts) => {
2076
1977
  if (!gateFleetControl(DATA_DIR, "stop", { yes: opts.yes }))
2077
1978
  process.exit(1);
2078
- const { getServicePath, stopService } = await import("./service-installer.js");
1979
+ const { getServicePath, getSystemServicePath, stopService, stopSystemService } = await import("./service-installer.js");
1980
+ // #2356: the system unit first, as `agend start` and `agend restart` choose it; a SIGTERM would only stop the process.
1981
+ if (getSystemServicePath()) {
1982
+ if (stopSystemService()) {
1983
+ console.log("Service stopped (system).");
1984
+ return;
1985
+ }
1986
+ console.error("Failed to stop the system service: systemctl stop agend (run it as root).");
1987
+ process.exitCode = 1;
1988
+ return;
1989
+ }
2079
1990
  if (!getServicePath()) {
2080
1991
  // No service — try killing by PID
2081
1992
  const pidPath = join(DATA_DIR, "fleet.pid");
@@ -2124,10 +2035,10 @@ program
2124
2035
  const systemServicePath = getSystemServicePath();
2125
2036
  if (!userServicePath && !systemServicePath) {
2126
2037
  console.log("No service installed. Starting fleet directly...");
2127
- const { spawn } = await import("node:child_process");
2038
+ const { spawn } = await import("./child-process.js");
2128
2039
  // This Node on this CLI, not `sh -c "agend fleet start"` through PATH (#1450 C5).
2129
2040
  const start = selfCommand(["fleet", "start"]);
2130
- const child = spawn(start.command, start.args, { detached: true, stdio: "ignore" });
2041
+ const child = spawn(start.command, start.args, { detached: true, stdio: "ignore", env: { ...process.env, [DIRECT_FLEET_START_ENV]: "1" } });
2131
2042
  child.unref();
2132
2043
  console.log("Fleet starting in background.");
2133
2044
  return;
@@ -2140,16 +2051,79 @@ program
2140
2051
  // A service file means the service manager owns this fleet. Falling back
2141
2052
  // to a detached process here creates a second fleet when the bus recovers.
2142
2053
  console.error("Failed to start the installed AgEnD service; refusing to start a detached duplicate.");
2143
- console.error("Check your service manager, then retry: systemctl --user status com.agend.fleet");
2054
+ console.error(`Check your service manager, then retry: ${process.platform === "darwin" ? "launchctl print gui/$(id -u)/com.agend.fleet" : systemServicePath ? "systemctl status agend" : "systemctl --user status com.agend.fleet"}`);
2055
+ // #2356: a service file that cannot run here (never activated, no session for it) would otherwise leave no start.
2056
+ console.error("If the service cannot run on this host, start the fleet without it: agend fleet start --direct");
2144
2057
  process.exitCode = 1;
2145
2058
  }
2146
2059
  });
2060
+ /**
2061
+ * #2356 (user decision): on a host with an AgEnD service installed, a hand-run `fleet start` / `fleet stop` would run or
2062
+ * kill a fleet the service manager does not own — a duplicate beside the unit, or a stop systemd undoes. Run `agend
2063
+ * start` / `agend stop` instead (this Node, this CLI; their own gate and exit code). The manager's own `fleet start`
2064
+ * (the unit's ExecStart, the launchd job) and AgEnD's marked detached start go ahead directly; so does every host
2065
+ * without a service (Docker, manual setups). Exits when it redirected; false to go on.
2066
+ */
2067
+ function redirectToService(command, extra, directMarker) {
2068
+ let ownCgroup = null;
2069
+ try {
2070
+ ownCgroup = readFileSync("/proc/self/cgroup", "utf-8");
2071
+ }
2072
+ catch { /* not Linux */ }
2073
+ const byServiceManager = startedByServiceManager({ platform: process.platform, env: process.env, ownCgroup, ppid: process.ppid, launchdLabel: "com.agend.fleet" });
2074
+ const installed = getSystemServicePath() ?? getServicePath();
2075
+ if (!redirectsToService({ command, directMarker, byServiceManager, installedServicePath: installed }))
2076
+ return false;
2077
+ console.error(`ℹ An AgEnD service is installed here (${installed}): running \`agend ${[command, ...extra].join(" ")}\` so the service manager owns the fleet.`);
2078
+ const self = selfCommand([command, ...extra]);
2079
+ const result = spawnSync(self.command, self.args, { stdio: "inherit" });
2080
+ process.exit(result.status ?? 1);
2081
+ }
2082
+ /** One instance, at once, through the running fleet's HTTP API; the fleet process keeps running. */
2083
+ async function restartOneInstance(instance) {
2084
+ recordInstanceControl(DATA_DIR, "instance-restart", instance);
2085
+ const { loadFleetConfig } = await import("./config.js");
2086
+ const fleet = loadFleetConfig(FLEET_CONFIG_PATH);
2087
+ const port = fleet.health_port ?? 19280;
2088
+ let token = "";
2089
+ try {
2090
+ token = readFileSync(join(DATA_DIR, "web.token"), "utf-8").trim();
2091
+ }
2092
+ catch { /* fleet may not be running */ }
2093
+ try {
2094
+ const resp = await fetch(`http://127.0.0.1:${port}/restart/${encodeURIComponent(instance)}`, {
2095
+ method: "POST",
2096
+ headers: token ? { "X-Agend-Token": token } : {},
2097
+ });
2098
+ const body = await resp.json();
2099
+ if (resp.ok) {
2100
+ console.log(`Instance "${instance}" restarted (immediate)`);
2101
+ }
2102
+ else {
2103
+ console.error(`Restart failed: ${body.error ?? resp.statusText}`);
2104
+ process.exit(1);
2105
+ }
2106
+ }
2107
+ catch {
2108
+ console.error(`Cannot connect to fleet (port ${port}). Is the fleet running?`);
2109
+ process.exit(1);
2110
+ }
2111
+ }
2147
2112
  program
2148
2113
  .command("restart")
2149
- .description("Restart the AgEnD service (auto-detects systemd/launchd/detached)")
2114
+ .description("Restart the AgEnD service (auto-detects systemd/launchd/detached) — the one full restart; or one instance")
2115
+ .argument("[instance]", "Restart only this instance, immediately; the fleet keeps running")
2150
2116
  .option("-y, --yes", "Confirm when run from a fleet agent session (this affects every instance)")
2151
2117
  .option("--force", "Restart even when the loaded service does not start this install on its selected Node (operators only)")
2152
- .action(async (opts) => {
2118
+ .action(async (instance, opts) => {
2119
+ if (instance) {
2120
+ if (opts.force) {
2121
+ console.error("--force applies to the whole service, not one instance.");
2122
+ process.exit(1);
2123
+ }
2124
+ await restartOneInstance(instance);
2125
+ return;
2126
+ }
2153
2127
  // --force overrides the C6 guard: an operator's own decision, never a fleet agent's or a fleet-internal spawn's.
2154
2128
  if (opts.force && !forceAllowed()) {
2155
2129
  console.error(" ✗ Not restarting: --force is for an operator's own shell, not a fleet agent session or the fleet's own commands. Nothing was stopped.");
@@ -2199,8 +2173,9 @@ program
2199
2173
  // service's own HOME, which may differ from what this command resolves (sudo,
2200
2174
  // AGEND_HOME) — an absent pid file does NOT mean it's stopped. Ask the service
2201
2175
  // manager first. This is the canonical restart the `update` flow spawns.
2202
- const { detectPlatform, getServicePath, getSystemServicePath, getSystemdServiceState, systemdRestartOutcome, ensureSystemdUnitHardening, effectiveUnitDropIns, unitCoredumpFilterState, } = await import("./service-installer.js");
2203
- const { spawn, spawnSync } = await import("node:child_process");
2176
+ const { detectPlatform, getServicePath, getSystemServicePath, getSystemdServiceState, systemdRestartOutcome, ensureSystemdUnitHardening, effectiveUnitDropIns, unitCoredumpFilterState, loadedUnitCheck, } = await import("./service-installer.js");
2177
+ const { spawnSync } = await import("node:child_process");
2178
+ const { spawn } = await import("./child-process.js");
2204
2179
  const plat = detectPlatform();
2205
2180
  const pidPath = join(DATA_DIR, "fleet.pid");
2206
2181
  const run = (cmd) => {
@@ -2329,7 +2304,11 @@ program
2329
2304
  .filter(([key, state]) => key !== "KillMode" && (state === "added" || state === "upgraded"))
2330
2305
  .map(([key]) => key);
2331
2306
  if (updated.length > 0)
2332
- console.log(` ✓ ${unitPath}: updated ${updated.join(", ")} (#908/#1071/#1113: graceful stop, small crash dumps, bounded start, start limit)`);
2307
+ console.log(` ✓ ${unitPath}: updated ${updated.join(", ")} (#908/#1071/#1113/#2331: graceful stop, small crash dumps, bounded start, start limit, the fleet's ready signal)`);
2308
+ // #2331: what systemd LOADED — a drop-in that refuses the fleet's READY is overridden by AgEnD's own, leftovers
2309
+ // are named. Not a gate: the fleet still restarts, and the line says what will happen and what to remove.
2310
+ for (const line of loadedUnitCheck({ user: systemdTarget.user, unit: systemdTarget.unit, unitPath }))
2311
+ console.log(line);
2333
2312
  // Re-judged after the reload, against what systemd now applies: an
2334
2313
  // operator's own mask (anywhere — main file, unit or type drop-in, or a
2335
2314
  // trailing empty assignment that restores the inherited one) is warned
@@ -2566,7 +2545,7 @@ program
2566
2545
  unlinkSync(pidPath);
2567
2546
  }
2568
2547
  catch { /* old owner already removed it */ }
2569
- const child = spawn(start.command, start.args, { detached: true, stdio: "ignore" });
2548
+ const child = spawn(start.command, start.args, { detached: true, stdio: "ignore", env: { ...process.env, [DIRECT_FLEET_START_ENV]: "1" } });
2570
2549
  child.unref();
2571
2550
  console.log("Fleet restarting in background.");
2572
2551
  return;
@@ -2583,7 +2562,7 @@ program
2583
2562
  });
2584
2563
  program
2585
2564
  .command("setup")
2586
- .description("Open the guided setup page in a browser (before a fleet exists)")
2565
+ .description("Browser first-run page, before a fleet exists (not the CLI wizard; see `agend quickstart`)")
2587
2566
  .option("--reset", "Allow setup again after it has already been completed")
2588
2567
  .option("--port <port>", "Port for the setup page (default: the health port)")
2589
2568
  .option("--tunnel", "Expose the setup page through a Cloudflare quick tunnel so a phone can open it")
@@ -2678,14 +2657,14 @@ program
2678
2657
  });
2679
2658
  program
2680
2659
  .command("quickstart")
2681
- .description("Guided setup for a Backend, chat channel, projects, and fleet service")
2660
+ .description("CLI guided setup: backend, chat channel, projects, and fleet service (recommended)")
2682
2661
  .action(async () => {
2683
2662
  const { runQuickstart } = await import("./quickstart.js");
2684
2663
  await runQuickstart();
2685
2664
  });
2686
2665
  program
2687
2666
  .command("init")
2688
- .description("Interactive setup wizard (advanced)")
2667
+ .description("Interactive advanced wizard (setup-wizard); for first-run use `agend quickstart` or `agend setup`")
2689
2668
  .action(async () => {
2690
2669
  const { runSetupWizard } = await import("./setup-wizard.js");
2691
2670
  await runSetupWizard();
@@ -3006,11 +2985,11 @@ schedule
3006
2985
  }
3007
2986
  });
3008
2987
  schedule
3009
- .command("trigger")
3010
- .description("Show how to manually trigger a schedule")
3011
- .argument("<id>", "Schedule ID")
3012
- .action((id) => {
3013
- console.log("Manual trigger requires fleet manager running. Use the Telegram interface instead.");
2988
+ .command("trigger", { hidden: true })
2989
+ .argument("[id]", "Schedule ID")
2990
+ .action(() => {
2991
+ console.error("`agend schedule trigger` was removed. There is no Telegram trigger; use `agend schedule enable` or the Task Board.");
2992
+ process.exitCode = 1;
3014
2993
  });
3015
2994
  // === Chat Export ===
3016
2995
  program
@@ -3511,16 +3490,21 @@ async function lsAction(opts) {
3511
3490
  console.log("Tip: agend completion install — then agend attach <TAB> completes instance names.");
3512
3491
  }
3513
3492
  }
3514
- program
3515
- .command("ls")
3516
- .description("List all instances with status, backend, team, and last activity")
3517
- .option("--json", "Output as JSON")
3518
- .option("--names-only", "Print instance names one per line (for shell completion)")
3519
- .option("--resources", "Also report workspace disk usage, unregistered instance directories, and host memory/swap (text only)")
3520
- .option("--running", "Show only running instances")
3521
- .option("--team <name>", "Filter by team name")
3522
- .option("--backend <name>", "Filter by backend (exact match)")
3523
- .option("--display", "Show display names as last column")
3493
+ function addLsOptions(cmd) {
3494
+ return cmd
3495
+ .option("--json", "Output as JSON")
3496
+ .option("--names-only", "Print instance names one per line (for shell completion)")
3497
+ .option("--resources", "Also report workspace disk usage, unregistered instance directories, and host memory/swap (text only); `agend fleet status` is a hidden alias of this")
3498
+ .option("--running", "Show only running instances")
3499
+ .option("--team <name>", "Filter by team name")
3500
+ .option("--backend <name>", "Filter by backend (exact match)")
3501
+ .option("--display", "Show display names as last column");
3502
+ }
3503
+ addLsOptions(program.command("ls").description("List all instances with status, backend, team, and last activity"))
3504
+ .action(async (opts) => {
3505
+ await lsAction(opts);
3506
+ });
3507
+ addLsOptions(program.command("status", { hidden: true }).description("Alias of `agend ls`"))
3524
3508
  .action(async (opts) => {
3525
3509
  await lsAction(opts);
3526
3510
  });
@@ -3547,9 +3531,10 @@ Then reload the shell. Tab-completes instance names for \`agend attach\` and
3547
3531
  // Subcommand names come from commander itself, so the script can't drift
3548
3532
  // out of sync with the CLI the way a hardcoded list would.
3549
3533
  const fleetCmd = program.commands.find(c => c.name() === "fleet");
3534
+ const visible = (cmds) => cmds.filter(c => !c._hidden).map(c => c.name());
3550
3535
  const spec = {
3551
- topLevel: program.commands.map(c => c.name()),
3552
- fleetSub: fleetCmd?.commands.map(c => c.name()) ?? [],
3536
+ topLevel: visible(program.commands),
3537
+ fleetSub: visible(fleetCmd?.commands ?? []),
3553
3538
  instanceCommands: ["attach"],
3554
3539
  fleetInstanceCommands: ["start", "stop", "restart"],
3555
3540
  };
@@ -3606,7 +3591,7 @@ Then reload the shell. Tab-completes instance names for \`agend attach\` and
3606
3591
  console.log(completionScript(target, spec));
3607
3592
  });
3608
3593
  program
3609
- .command("health").description("Fleet health check — shows problems and diagnostics")
3594
+ .command("health").description("Live fleet health of running instances (HTTP, tmux, IPC); for host/setup diagnostics use `agend doctor`")
3610
3595
  .option("--json", "Output as JSON")
3611
3596
  .option("-q, --quiet", "One-line summary only")
3612
3597
  .action(async (opts) => {
@@ -3844,7 +3829,7 @@ program
3844
3829
  });
3845
3830
  program
3846
3831
  .command("logs")
3847
- .description("Show fleet log (alias for `agend fleet logs`)")
3832
+ .description("Show fleet log (daemon.log, or fleet.log if the daemon log is not there yet)")
3848
3833
  .option("-n, --lines <count>", "Number of lines to show", "50")
3849
3834
  .option("-f, --follow", "Follow log output (like tail -f)")
3850
3835
  .option("--instance <name>", "Filter by instance name")