@songsid/agend 2.3.0-beta.8 → 2.3.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 (736) hide show
  1. package/README.md +16 -8
  2. package/README.zh-TW.md +16 -8
  3. package/dist/adapter-world.js.map +1 -1
  4. package/dist/agend-home.d.ts +5 -0
  5. package/dist/agend-home.js +7 -0
  6. package/dist/agend-home.js.map +1 -0
  7. package/dist/agent-cli-instructions.md +9 -2
  8. package/dist/agent-cli.js +44 -0
  9. package/dist/agent-cli.js.map +1 -1
  10. package/dist/agent-endpoint.d.ts +9 -0
  11. package/dist/agent-endpoint.js +23 -0
  12. package/dist/agent-endpoint.js.map +1 -1
  13. package/dist/agent-file-read.js +1 -1
  14. package/dist/agent-file-read.js.map +1 -1
  15. package/dist/agent-whats-new.d.ts +16 -0
  16. package/dist/agent-whats-new.js +54 -11
  17. package/dist/agent-whats-new.js.map +1 -1
  18. package/dist/apply-job.js.map +1 -1
  19. package/dist/atomic-write.js.map +1 -1
  20. package/dist/auth-api.d.ts +12 -0
  21. package/dist/auth-api.js +77 -22
  22. package/dist/auth-api.js.map +1 -1
  23. package/dist/authority/authority-state.d.ts +4 -0
  24. package/dist/authority/authority-state.js +17 -8
  25. package/dist/authority/authority-state.js.map +1 -1
  26. package/dist/authority/session-authorizations.js.map +1 -1
  27. package/dist/authority/web-authority.d.ts +16 -8
  28. package/dist/authority/web-authority.js +19 -5
  29. package/dist/authority/web-authority.js.map +1 -1
  30. package/dist/backend/antigravity.d.ts +2 -2
  31. package/dist/backend/antigravity.js +2 -1
  32. package/dist/backend/antigravity.js.map +1 -1
  33. package/dist/backend/claude-code.js +13 -4
  34. package/dist/backend/claude-code.js.map +1 -1
  35. package/dist/backend/codex-metadata.d.ts +8 -0
  36. package/dist/backend/codex-metadata.js +74 -17
  37. package/dist/backend/codex-metadata.js.map +1 -1
  38. package/dist/backend/codex-package.d.ts +30 -0
  39. package/dist/backend/codex-package.js +133 -0
  40. package/dist/backend/codex-package.js.map +1 -0
  41. package/dist/backend/codex-session-lookup.js.map +1 -1
  42. package/dist/backend/codex.d.ts +2 -0
  43. package/dist/backend/codex.js +25 -5
  44. package/dist/backend/codex.js.map +1 -1
  45. package/dist/backend/credential-profile.d.ts +2 -0
  46. package/dist/backend/credential-profile.js +2 -0
  47. package/dist/backend/credential-profile.js.map +1 -1
  48. package/dist/backend/effort-metadata.js.map +1 -1
  49. package/dist/backend/grok.js +58 -3
  50. package/dist/backend/grok.js.map +1 -1
  51. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  52. package/dist/backend/kiro-kas-store.js.map +1 -1
  53. package/dist/backend/kiro-v2-store.js.map +1 -1
  54. package/dist/backend/kiro-v3-identity.js.map +1 -1
  55. package/dist/backend/kiro.d.ts +9 -1
  56. package/dist/backend/kiro.js +102 -1
  57. package/dist/backend/kiro.js.map +1 -1
  58. package/dist/backend/launch-effort.js.map +1 -1
  59. package/dist/backend/mock.js.map +1 -1
  60. package/dist/backend/muse.js.map +1 -1
  61. package/dist/backend/opencode-permission.js +1 -1
  62. package/dist/backend/opencode-permission.js.map +1 -1
  63. package/dist/backend/opencode.js +2 -1
  64. package/dist/backend/opencode.js.map +1 -1
  65. package/dist/backend/session-signals.js.map +1 -1
  66. package/dist/backend-availability.js.map +1 -1
  67. package/dist/backend-command.d.ts +2 -0
  68. package/dist/backend-command.js +3 -1
  69. package/dist/backend-command.js.map +1 -1
  70. package/dist/backend-outage.js.map +1 -1
  71. package/dist/binary-probe.js +1 -1
  72. package/dist/binary-probe.js.map +1 -1
  73. package/dist/bug-report-cli.d.ts +30 -0
  74. package/dist/bug-report-cli.js +139 -0
  75. package/dist/bug-report-cli.js.map +1 -0
  76. package/dist/bug-report.d.ts +47 -0
  77. package/dist/bug-report.js +137 -0
  78. package/dist/bug-report.js.map +1 -0
  79. package/dist/bundled-skills.d.ts +18 -0
  80. package/dist/bundled-skills.js +38 -0
  81. package/dist/bundled-skills.js.map +1 -0
  82. package/dist/cache-service.js +1 -1
  83. package/dist/cache-service.js.map +1 -1
  84. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  85. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  86. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  87. package/dist/channel/adapters/discord.d.ts +4 -0
  88. package/dist/channel/adapters/discord.js +57 -12
  89. package/dist/channel/adapters/discord.js.map +1 -1
  90. package/dist/channel/adapters/telegram.d.ts +6 -0
  91. package/dist/channel/adapters/telegram.js +54 -14
  92. package/dist/channel/adapters/telegram.js.map +1 -1
  93. package/dist/channel/mcp-server.js +21 -4
  94. package/dist/channel/mcp-server.js.map +1 -1
  95. package/dist/channel/mcp-tools.d.ts +3 -1
  96. package/dist/channel/mcp-tools.js +43 -6
  97. package/dist/channel/mcp-tools.js.map +1 -1
  98. package/dist/channel/message-queue.js.map +1 -1
  99. package/dist/channel/sticker-download.js.map +1 -1
  100. package/dist/channel/tool-router.js.map +1 -1
  101. package/dist/child-process.d.ts +21 -0
  102. package/dist/child-process.js +54 -0
  103. package/dist/child-process.js.map +1 -0
  104. package/dist/classic-channel-manager.js +3 -2
  105. package/dist/classic-channel-manager.js.map +1 -1
  106. package/dist/cli-entry.js.map +1 -1
  107. package/dist/cli-flags.js.map +1 -1
  108. package/dist/cli.d.ts +1 -0
  109. package/dist/cli.js +443 -311
  110. package/dist/cli.js.map +1 -1
  111. package/dist/command-table.d.ts +1 -1
  112. package/dist/command-table.js +1 -1
  113. package/dist/command-table.js.map +1 -1
  114. package/dist/compact-watch.js.map +1 -1
  115. package/dist/completion-install.js.map +1 -1
  116. package/dist/completion.js.map +1 -1
  117. package/dist/config-validator.d.ts +6 -0
  118. package/dist/config-validator.js +81 -8
  119. package/dist/config-validator.js.map +1 -1
  120. package/dist/config.d.ts +15 -3
  121. package/dist/config.js +41 -10
  122. package/dist/config.js.map +1 -1
  123. package/dist/connection-secrets.js.map +1 -1
  124. package/dist/context-guardian.js.map +1 -1
  125. package/dist/coredump-filter.js.map +1 -1
  126. package/dist/cpu-profile.js.map +1 -1
  127. package/dist/cross-instance-envelope.js.map +1 -1
  128. package/dist/cross-instance-notice.d.ts +7 -0
  129. package/dist/cross-instance-notice.js +10 -0
  130. package/dist/cross-instance-notice.js.map +1 -1
  131. package/dist/daemon.d.ts +39 -1
  132. package/dist/daemon.js +234 -15
  133. package/dist/daemon.js.map +1 -1
  134. package/dist/daily-summary.js.map +1 -1
  135. package/dist/darwin-memory.js +1 -1
  136. package/dist/darwin-memory.js.map +1 -1
  137. package/dist/dashboard-live.d.ts +8 -0
  138. package/dist/dashboard-live.js +35 -0
  139. package/dist/dashboard-live.js.map +1 -0
  140. package/dist/dashboard-permissions.d.ts +69 -0
  141. package/dist/dashboard-permissions.js +138 -0
  142. package/dist/dashboard-permissions.js.map +1 -0
  143. package/dist/decision-list.d.ts +25 -0
  144. package/dist/decision-list.js +30 -0
  145. package/dist/decision-list.js.map +1 -0
  146. package/dist/delivery-consumed-watch.js.map +1 -1
  147. package/dist/delivery-outbox.js +5 -0
  148. package/dist/delivery-outbox.js.map +1 -1
  149. package/dist/delivery-outbox.test.js.map +1 -1
  150. package/dist/delivery-reconciliation.test.js.map +1 -1
  151. package/dist/diagnostic-redaction.d.ts +10 -0
  152. package/dist/diagnostic-redaction.js +167 -0
  153. package/dist/diagnostic-redaction.js.map +1 -0
  154. package/dist/doctor.d.ts +8 -0
  155. package/dist/doctor.js +52 -0
  156. package/dist/doctor.js.map +1 -1
  157. package/dist/e2e-isolation.js.map +1 -1
  158. package/dist/event-log.js.map +1 -1
  159. package/dist/export-import.js +7 -5
  160. package/dist/export-import.js.map +1 -1
  161. package/dist/fleet-control-audit.js.map +1 -1
  162. package/dist/fleet-label.js.map +1 -1
  163. package/dist/fleet-level-config.d.ts +1 -1
  164. package/dist/fleet-level-config.js +2 -0
  165. package/dist/fleet-level-config.js.map +1 -1
  166. package/dist/fleet-lock.js +3 -2
  167. package/dist/fleet-lock.js.map +1 -1
  168. package/dist/fleet-manager.d.ts +258 -15
  169. package/dist/fleet-manager.js +1605 -304
  170. package/dist/fleet-manager.js.map +1 -1
  171. package/dist/fleet-process-handlers.d.ts +18 -0
  172. package/dist/fleet-process-handlers.js +70 -0
  173. package/dist/fleet-process-handlers.js.map +1 -0
  174. package/dist/fleet-startup.d.ts +14 -0
  175. package/dist/fleet-startup.js +30 -0
  176. package/dist/fleet-startup.js.map +1 -0
  177. package/dist/fleet-traffic.d.ts +36 -0
  178. package/dist/fleet-traffic.js +100 -0
  179. package/dist/fleet-traffic.js.map +1 -1
  180. package/dist/forged-envelope-scan.d.ts +0 -20
  181. package/dist/forged-envelope-scan.js +3 -2
  182. package/dist/forged-envelope-scan.js.map +1 -1
  183. package/dist/full-restart.d.ts +5 -5
  184. package/dist/full-restart.js +5 -5
  185. package/dist/full-restart.js.map +1 -1
  186. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  187. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +52 -11
  188. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +5 -1
  189. package/dist/general-knowledge/skills/fleet-config/SKILL.md +3 -1
  190. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +8 -9
  191. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
  192. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  193. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  194. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +321 -0
  195. package/dist/general-knowledge/skills/web-appearance/SKILL.md +28 -4
  196. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +553 -0
  197. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +170 -35
  198. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  199. package/dist/general-knowledge/steering/core-rules.md +1 -1
  200. package/dist/host-memory.js.map +1 -1
  201. package/dist/idle-gate-log.d.ts +19 -0
  202. package/dist/idle-gate-log.js +15 -0
  203. package/dist/idle-gate-log.js.map +1 -0
  204. package/dist/instance-config-impact.js +25 -4
  205. package/dist/instance-config-impact.js.map +1 -1
  206. package/dist/instance-lifecycle.d.ts +10 -1
  207. package/dist/instance-lifecycle.js +86 -25
  208. package/dist/instance-lifecycle.js.map +1 -1
  209. package/dist/instructions.d.ts +12 -6
  210. package/dist/instructions.js +19 -10
  211. package/dist/instructions.js.map +1 -1
  212. package/dist/kiro-compact-pane.js.map +1 -1
  213. package/dist/kiro-db-reader.js.map +1 -1
  214. package/dist/kiro-engine-status.js.map +1 -1
  215. package/dist/kiro-log-gate.js.map +1 -1
  216. package/dist/kiro-transcript-lane.js.map +1 -1
  217. package/dist/locale.js +428 -20
  218. package/dist/locale.js.map +1 -1
  219. package/dist/log-service.js.map +1 -1
  220. package/dist/log-steps.d.ts +1 -0
  221. package/dist/log-steps.js +15 -0
  222. package/dist/log-steps.js.map +1 -1
  223. package/dist/logger.js +3 -2
  224. package/dist/logger.js.map +1 -1
  225. package/dist/login-controller.d.ts +45 -0
  226. package/dist/login-controller.js +101 -5
  227. package/dist/login-controller.js.map +1 -1
  228. package/dist/login-flows.js +1 -1
  229. package/dist/login-manager.js.map +1 -1
  230. package/dist/login-shell.js +3 -1
  231. package/dist/login-shell.js.map +1 -1
  232. package/dist/ls-attach-resolve.d.ts +96 -0
  233. package/dist/ls-attach-resolve.js +180 -0
  234. package/dist/ls-attach-resolve.js.map +1 -0
  235. package/dist/ls-rows.d.ts +12 -0
  236. package/dist/ls-rows.js +83 -4
  237. package/dist/ls-rows.js.map +1 -1
  238. package/dist/memory-pressure.js.map +1 -1
  239. package/dist/muse-usage-relay.js.map +1 -1
  240. package/dist/needs-you-hub.d.ts +98 -1
  241. package/dist/needs-you-hub.js +313 -44
  242. package/dist/needs-you-hub.js.map +1 -1
  243. package/dist/needs-you-live.d.ts +5 -2
  244. package/dist/needs-you-live.js +6 -3
  245. package/dist/needs-you-live.js.map +1 -1
  246. package/dist/needs-you.d.ts +68 -3
  247. package/dist/needs-you.js +38 -4
  248. package/dist/needs-you.js.map +1 -1
  249. package/dist/network-family.js.map +1 -1
  250. package/dist/operator-control-paths.js +3 -4
  251. package/dist/operator-control-paths.js.map +1 -1
  252. package/dist/outbound-handlers.d.ts +3 -0
  253. package/dist/outbound-handlers.js +76 -19
  254. package/dist/outbound-handlers.js.map +1 -1
  255. package/dist/outbound-schemas.d.ts +112 -64
  256. package/dist/outbound-schemas.js +39 -12
  257. package/dist/outbound-schemas.js.map +1 -1
  258. package/dist/outbox-open-error.js.map +1 -1
  259. package/dist/pane-context-cache.js.map +1 -1
  260. package/dist/paths.js.map +1 -1
  261. package/dist/private-dir.js.map +1 -1
  262. package/dist/probe-worker-pool.js.map +1 -1
  263. package/dist/profile-control.js.map +1 -1
  264. package/dist/prompt-file-ref.js.map +1 -1
  265. package/dist/provider-probe.js.map +1 -1
  266. package/dist/provider-secret-registry.d.ts +5 -3
  267. package/dist/provider-secret-registry.js +8 -2
  268. package/dist/provider-secret-registry.js.map +1 -1
  269. package/dist/public-link-auto-open.d.ts +32 -0
  270. package/dist/public-link-auto-open.js +86 -0
  271. package/dist/public-link-auto-open.js.map +1 -0
  272. package/dist/public-link-owner.d.ts +10 -0
  273. package/dist/public-link-owner.js +36 -0
  274. package/dist/public-link-owner.js.map +1 -0
  275. package/dist/public-link-progress.d.ts +12 -1
  276. package/dist/public-link-progress.js +5 -10
  277. package/dist/public-link-progress.js.map +1 -1
  278. package/dist/public-preview.js.map +1 -1
  279. package/dist/public-tunnel-config.d.ts +11 -0
  280. package/dist/public-tunnel-config.js +68 -0
  281. package/dist/public-tunnel-config.js.map +1 -0
  282. package/dist/public-web-gateway.d.ts +2 -0
  283. package/dist/public-web-gateway.js +19 -6
  284. package/dist/public-web-gateway.js.map +1 -1
  285. package/dist/public-web-link.d.ts +48 -3
  286. package/dist/public-web-link.js +177 -51
  287. package/dist/public-web-link.js.map +1 -1
  288. package/dist/quickstart-api.d.ts +9 -0
  289. package/dist/quickstart-api.js +82 -0
  290. package/dist/quickstart-api.js.map +1 -1
  291. package/dist/quickstart.d.ts +5 -0
  292. package/dist/quickstart.js +40 -25
  293. package/dist/quickstart.js.map +1 -1
  294. package/dist/reply-buttons-controller.js.map +1 -1
  295. package/dist/reply-dedup.js.map +1 -1
  296. package/dist/resource-report.js +1 -1
  297. package/dist/resource-report.js.map +1 -1
  298. package/dist/restart-guard.js.map +1 -1
  299. package/dist/restart-progress.d.ts +80 -1
  300. package/dist/restart-progress.js +306 -21
  301. package/dist/restart-progress.js.map +1 -1
  302. package/dist/rollout-index.js.map +1 -1
  303. package/dist/runtime-cpu-profile.js.map +1 -1
  304. package/dist/scheduler/db.d.ts +40 -13
  305. package/dist/scheduler/db.js +123 -89
  306. package/dist/scheduler/db.js.map +1 -1
  307. package/dist/scheduler/scheduler.js.map +1 -1
  308. package/dist/scheduler/task-board-text.d.ts +26 -0
  309. package/dist/scheduler/task-board-text.js +140 -0
  310. package/dist/scheduler/task-board-text.js.map +1 -0
  311. package/dist/scheduler/task-board.d.ts +730 -0
  312. package/dist/scheduler/task-board.js +2179 -0
  313. package/dist/scheduler/task-board.js.map +1 -0
  314. package/dist/scheduler/types.d.ts +6 -1
  315. package/dist/sd-notify.js +2 -1
  316. package/dist/sd-notify.js.map +1 -1
  317. package/dist/search/search-docs.d.ts +58 -0
  318. package/dist/search/search-docs.js +63 -0
  319. package/dist/search/search-docs.js.map +1 -0
  320. package/dist/search/search-index.d.ts +141 -0
  321. package/dist/search/search-index.js +440 -0
  322. package/dist/search/search-index.js.map +1 -0
  323. package/dist/search/search-store.d.ts +122 -0
  324. package/dist/search/search-store.js +236 -0
  325. package/dist/search/search-store.js.map +1 -0
  326. package/dist/search/search-worker.d.ts +1 -0
  327. package/dist/search/search-worker.js +163 -0
  328. package/dist/search/search-worker.js.map +1 -0
  329. package/dist/secret-file.js.map +1 -1
  330. package/dist/secret-store.d.ts +2 -0
  331. package/dist/secret-store.js +21 -0
  332. package/dist/secret-store.js.map +1 -1
  333. package/dist/service-installer.d.ts +75 -0
  334. package/dist/service-installer.js +206 -0
  335. package/dist/service-installer.js.map +1 -1
  336. package/dist/service-owner.d.ts +44 -0
  337. package/dist/service-owner.js +48 -0
  338. package/dist/service-owner.js.map +1 -0
  339. package/dist/service-plan.d.ts +39 -2
  340. package/dist/service-plan.js +188 -6
  341. package/dist/service-plan.js.map +1 -1
  342. package/dist/session-oom.d.ts +21 -0
  343. package/dist/session-oom.js +139 -0
  344. package/dist/session-oom.js.map +1 -0
  345. package/dist/settings-api.d.ts +13 -0
  346. package/dist/settings-api.js +73 -12
  347. package/dist/settings-api.js.map +1 -1
  348. package/dist/settings-baseline.js.map +1 -1
  349. package/dist/settings-change.d.ts +5 -0
  350. package/dist/settings-change.js +167 -6
  351. package/dist/settings-change.js.map +1 -1
  352. package/dist/settings-confirmation.d.ts +9 -1
  353. package/dist/settings-confirmation.js +18 -8
  354. package/dist/settings-confirmation.js.map +1 -1
  355. package/dist/settings-control.js +2 -1
  356. package/dist/settings-control.js.map +1 -1
  357. package/dist/settings-effect.js +29 -4
  358. package/dist/settings-effect.js.map +1 -1
  359. package/dist/settings-http-confirmation.d.ts +13 -0
  360. package/dist/settings-http-confirmation.js +15 -3
  361. package/dist/settings-http-confirmation.js.map +1 -1
  362. package/dist/settings-instance-patch.js +13 -1
  363. package/dist/settings-instance-patch.js.map +1 -1
  364. package/dist/settings-request-capability.d.ts +4 -1
  365. package/dist/settings-request-capability.js +11 -3
  366. package/dist/settings-request-capability.js.map +1 -1
  367. package/dist/settings-transaction.js.map +1 -1
  368. package/dist/setup-auth.d.ts +1 -0
  369. package/dist/setup-auth.js +2 -1
  370. package/dist/setup-auth.js.map +1 -1
  371. package/dist/setup-form.d.ts +1 -1
  372. package/dist/setup-form.js +23 -7
  373. package/dist/setup-form.js.map +1 -1
  374. package/dist/setup-host.d.ts +6 -1
  375. package/dist/setup-host.js +15 -4
  376. package/dist/setup-host.js.map +1 -1
  377. package/dist/setup-wizard.js +6 -3
  378. package/dist/setup-wizard.js.map +1 -1
  379. package/dist/spawn-gate.d.ts +5 -0
  380. package/dist/spawn-gate.js +28 -8
  381. package/dist/spawn-gate.js.map +1 -1
  382. package/dist/start-attempt.d.ts +1 -0
  383. package/dist/start-attempt.js +18 -0
  384. package/dist/start-attempt.js.map +1 -0
  385. package/dist/start-failure-notice.d.ts +4 -0
  386. package/dist/start-failure-notice.js +20 -0
  387. package/dist/start-failure-notice.js.map +1 -0
  388. package/dist/start-failure.d.ts +147 -0
  389. package/dist/start-failure.js +281 -0
  390. package/dist/start-failure.js.map +1 -0
  391. package/dist/startup-memory-deadline.d.ts +23 -0
  392. package/dist/startup-memory-deadline.js +55 -0
  393. package/dist/startup-memory-deadline.js.map +1 -0
  394. package/dist/statusline-watcher.js.map +1 -1
  395. package/dist/storm-window.js.map +1 -1
  396. package/dist/systemd-peer-bus.js.map +1 -1
  397. package/dist/target-queue-worker.js.map +1 -1
  398. package/dist/task-board-summary.d.ts +101 -0
  399. package/dist/task-board-summary.js +177 -0
  400. package/dist/task-board-summary.js.map +1 -0
  401. package/dist/task-board-tool.d.ts +25 -0
  402. package/dist/task-board-tool.js +155 -0
  403. package/dist/task-board-tool.js.map +1 -0
  404. package/dist/task-board-waker.d.ts +51 -0
  405. package/dist/task-board-waker.js +141 -0
  406. package/dist/task-board-waker.js.map +1 -0
  407. package/dist/tips.js.map +1 -1
  408. package/dist/tmux-control.js +1 -1
  409. package/dist/tmux-control.js.map +1 -1
  410. package/dist/tmux-exec-helper.d.ts +104 -0
  411. package/dist/tmux-exec-helper.js +412 -0
  412. package/dist/tmux-exec-helper.js.map +1 -0
  413. package/dist/tmux-manager.d.ts +1 -0
  414. package/dist/tmux-manager.js +29 -13
  415. package/dist/tmux-manager.js.map +1 -1
  416. package/dist/tmux-read.js +2 -2
  417. package/dist/tmux-read.js.map +1 -1
  418. package/dist/tool-permissions.js +9 -42
  419. package/dist/tool-permissions.js.map +1 -1
  420. package/dist/tool-progress.js.map +1 -1
  421. package/dist/topic-archiver.js.map +1 -1
  422. package/dist/topic-commands.d.ts +13 -0
  423. package/dist/topic-commands.js +56 -33
  424. package/dist/topic-commands.js.map +1 -1
  425. package/dist/topic-link.js.map +1 -1
  426. package/dist/transcript-delta-reader.js.map +1 -1
  427. package/dist/transcript-jsonl.js.map +1 -1
  428. package/dist/transcript-monitor.js +5 -0
  429. package/dist/transcript-monitor.js.map +1 -1
  430. package/dist/transcript-sources.d.ts +2 -0
  431. package/dist/transcript-sources.js +4 -0
  432. package/dist/transcript-sources.js.map +1 -1
  433. package/dist/tunnel/cloudflared-install.js +3 -1
  434. package/dist/tunnel/cloudflared-install.js.map +1 -1
  435. package/dist/tunnel/cloudflared.d.ts +7 -16
  436. package/dist/tunnel/cloudflared.js +100 -13
  437. package/dist/tunnel/cloudflared.js.map +1 -1
  438. package/dist/tunnel/lease.js +3 -2
  439. package/dist/tunnel/lease.js.map +1 -1
  440. package/dist/tunnel/manager.js.map +1 -1
  441. package/dist/tunnel/named-diagnostics.d.ts +13 -0
  442. package/dist/tunnel/named-diagnostics.js +52 -0
  443. package/dist/tunnel/named-diagnostics.js.map +1 -0
  444. package/dist/tunnel/public-fetch.js.map +1 -1
  445. package/dist/tunnel/purpose-lane.js.map +1 -1
  446. package/dist/tunnel/types.d.ts +4 -1
  447. package/dist/tunnel/types.js.map +1 -1
  448. package/dist/types.d.ts +55 -2
  449. package/dist/ui/app-effects.js +27 -5
  450. package/dist/ui/app.html +2 -0
  451. package/dist/ui/backend-signin.js +297 -0
  452. package/dist/ui/chat-effects.js +15 -7
  453. package/dist/ui/chat-msg-actions.js +176 -0
  454. package/dist/ui/chat-render.js +67 -20
  455. package/dist/ui/chat-store.js +218 -48
  456. package/dist/ui/chat-strings.js +42 -10
  457. package/dist/ui/chat-thread.js +249 -23
  458. package/dist/ui/effect-trial-controller.js +13 -8
  459. package/dist/ui/effect-trial-offer.js +2 -31
  460. package/dist/ui/effects-waker.js +2 -0
  461. package/dist/ui/fleet-canvas-layout.js +13 -4
  462. package/dist/ui/fleet-canvas.js +120 -43
  463. package/dist/ui/fleet-office-editor.js +149 -0
  464. package/dist/ui/fleet-office-model.js +358 -0
  465. package/dist/ui/fleet-office-pack.js +199 -0
  466. package/dist/ui/fleet-office-skin.js +474 -0
  467. package/dist/ui/fleet-office-store.js +49 -0
  468. package/dist/ui/fleet-office-zones.js +177 -0
  469. package/dist/ui/fleet-office.js +434 -0
  470. package/dist/ui/fleet-org.js +21 -8
  471. package/dist/ui/fleet-route-scene.js +272 -0
  472. package/dist/ui/fleet-route-worker.js +21 -0
  473. package/dist/ui/fleet-route.js +88 -0
  474. package/dist/ui/fleet-task-card.js +436 -0
  475. package/dist/ui/fleet-task-common.js +482 -0
  476. package/dist/ui/fleet-task-work.js +107 -0
  477. package/dist/ui/fleet-tasks.js +569 -0
  478. package/dist/ui/fleet-traffic.js +227 -45
  479. package/dist/ui/fleet-usage.js +36 -2
  480. package/dist/ui/guest-refused.html +41 -0
  481. package/dist/ui/icons/icon-192.png +0 -0
  482. package/dist/ui/icons/icon-512.png +0 -0
  483. package/dist/ui/image-lightbox.js +45 -1
  484. package/dist/ui/image-zoom.js +242 -0
  485. package/dist/ui/panel-chat.js +282 -89
  486. package/dist/ui/panel-details.js +2 -0
  487. package/dist/ui/panel-fleet.js +32 -71
  488. package/dist/ui/panel-needs.js +23 -6
  489. package/dist/ui/panel-settings.js +158 -32
  490. package/dist/ui/preview-panel.js +2 -1
  491. package/dist/ui/search-dialog.js +350 -0
  492. package/dist/ui/settings-appearance.js +170 -10
  493. package/dist/ui/settings-apply.js +3 -3
  494. package/dist/ui/settings-confirm.js +7 -2
  495. package/dist/ui/settings-dialogs.js +46 -21
  496. package/dist/ui/settings-discord.js +6 -2
  497. package/dist/ui/settings-guests.js +2 -1
  498. package/dist/ui/settings-learn.js +1 -0
  499. package/dist/ui/settings-model.js +51 -18
  500. package/dist/ui/settings-mods.js +8 -3
  501. package/dist/ui/settings-owner-effects.js +6 -4
  502. package/dist/ui/settings-strings.js +180 -6
  503. package/dist/ui/settings-token.js +3 -1
  504. package/dist/ui/settings-web-authority.js +13 -5
  505. package/dist/ui/settings-wizard.js +13 -7
  506. package/dist/ui/shared/agend-auth.js +3 -1
  507. package/dist/ui/shared/app-i18n.js +12 -5
  508. package/dist/ui/shared/app-needs.js +2 -2
  509. package/dist/ui/shared/app-prefs.js +15 -0
  510. package/dist/ui/shared/app-route.js +18 -0
  511. package/dist/ui/shared/app-shell.js +172 -28
  512. package/dist/ui/shared/app-store.js +3 -2
  513. package/dist/ui/shared/app-stream.js +7 -2
  514. package/dist/ui/shared/app-themes.js +116 -1
  515. package/dist/ui/shared/app.css +681 -54
  516. package/dist/ui/shared/app.js +9 -1
  517. package/dist/ui/shared/card-strings.js +4 -0
  518. package/dist/ui/shared/effect-app-runtime.js +7 -5
  519. package/dist/ui/shared/effect-chat-runtime.js +2 -7
  520. package/dist/ui/shared/effect-cursor.js +3 -2
  521. package/dist/ui/shared/effect-display-metadata.js +39 -0
  522. package/dist/ui/shared/effect-owner-source.js +8 -1
  523. package/dist/ui/shared/effect-policy.js +2 -2
  524. package/dist/ui/shared/effect-registry.js +30 -11
  525. package/dist/ui/shared/effect-runtime-loader.js +3 -1
  526. package/dist/ui/shared/effect-stock-snapshot.js +9 -0
  527. package/dist/ui/shared/effect-stock-trust.js +40 -9
  528. package/dist/ui/shared/effect-trial-source.js +1 -1
  529. package/dist/ui/shared/effect-trial-stage.js +27 -21
  530. package/dist/ui/shared/effect-trial.css +1 -1
  531. package/dist/ui/shared/feature-flags.js +127 -0
  532. package/dist/ui/shared/guest-refused.js +35 -0
  533. package/dist/ui/shared/header-tools.js +2 -2
  534. package/dist/ui/shared/identity-card.js +26 -8
  535. package/dist/ui/shared/identity-color.js +17 -10
  536. package/dist/ui/shared/instance-nav.js +183 -28
  537. package/dist/ui/shared/owner-css.js +30 -1
  538. package/dist/ui/shared/panel-view.js +14 -29
  539. package/dist/ui/shared/text-undo.js +210 -0
  540. package/dist/ui/shared/tokens.css +13 -3
  541. package/dist/ui/shared/ui-dialog.js +5 -4
  542. package/dist/ui/shared/ui-icons.js +4 -0
  543. package/dist/ui/shared/ui-menu.js +20 -7
  544. package/dist/ui/shared/ui-toast.js +16 -3
  545. package/dist/ui/shared/usage-dialog.js +206 -16
  546. package/dist/ui/shared/view-strings.js +68 -4
  547. package/dist/ui/shared/web-authorize.js +50 -1
  548. package/dist/ui/signin.html +2 -0
  549. package/dist/ui/web-terminal/terminal.css +4 -4
  550. package/dist/ui/web-terminal/terminal.html +10 -10
  551. package/dist/ui/web-terminal/terminal.js +58 -12
  552. package/dist/update-check.js.map +1 -1
  553. package/dist/update-dispatch.js +1 -1
  554. package/dist/update-dispatch.js.map +1 -1
  555. package/dist/update-install.js.map +1 -1
  556. package/dist/update-launch.d.ts +10 -1
  557. package/dist/update-launch.js +39 -14
  558. package/dist/update-launch.js.map +1 -1
  559. package/dist/update-marker.js.map +1 -1
  560. package/dist/update-preflight.js +1 -1
  561. package/dist/update-preflight.js.map +1 -1
  562. package/dist/update-progress.d.ts +3 -1
  563. package/dist/update-progress.js +3 -1
  564. package/dist/update-progress.js.map +1 -1
  565. package/dist/usage/format-rich.js +6 -3
  566. package/dist/usage/format-rich.js.map +1 -1
  567. package/dist/usage/i18n-keys.d.ts +1 -1
  568. package/dist/usage/i18n-keys.js +2 -1
  569. package/dist/usage/i18n-keys.js.map +1 -1
  570. package/dist/usage/i18n.d.ts +13 -1
  571. package/dist/usage/i18n.js +29 -0
  572. package/dist/usage/i18n.js.map +1 -1
  573. package/dist/usage/providers.d.ts +89 -2
  574. package/dist/usage/providers.js +247 -41
  575. package/dist/usage/providers.js.map +1 -1
  576. package/dist/usage/statusline-usage.js.map +1 -1
  577. package/dist/usage/usage-api.d.ts +5 -1
  578. package/dist/usage/usage-api.js +185 -58
  579. package/dist/usage/usage-api.js.map +1 -1
  580. package/dist/usage-report.js.map +1 -1
  581. package/dist/usage-rollup.js +1 -1
  582. package/dist/usage-rollup.js.map +1 -1
  583. package/dist/user-systemd.d.ts +24 -0
  584. package/dist/user-systemd.js +75 -0
  585. package/dist/user-systemd.js.map +1 -0
  586. package/dist/view-api.d.ts +7 -0
  587. package/dist/view-api.js +16 -4
  588. package/dist/view-api.js.map +1 -1
  589. package/dist/wake-coordinator.js.map +1 -1
  590. package/dist/web-ambient.js +9 -2
  591. package/dist/web-ambient.js.map +1 -1
  592. package/dist/web-api.d.ts +58 -10
  593. package/dist/web-api.js +1104 -71
  594. package/dist/web-api.js.map +1 -1
  595. package/dist/web-app-icon-worker.d.ts +1 -0
  596. package/dist/web-app-icon-worker.js +16 -0
  597. package/dist/web-app-icon-worker.js.map +1 -0
  598. package/dist/web-app-icon.d.ts +109 -0
  599. package/dist/web-app-icon.js +257 -0
  600. package/dist/web-app-icon.js.map +1 -0
  601. package/dist/web-appearance-policy.js.map +1 -1
  602. package/dist/web-appearance-tools.d.ts +1 -0
  603. package/dist/web-appearance-tools.js +7 -3
  604. package/dist/web-appearance-tools.js.map +1 -1
  605. package/dist/web-appearance-view.js.map +1 -1
  606. package/dist/web-appearance.d.ts +4 -10
  607. package/dist/web-appearance.js +7 -4
  608. package/dist/web-appearance.js.map +1 -1
  609. package/dist/web-auth.d.ts +8 -4
  610. package/dist/web-auth.js +33 -21
  611. package/dist/web-auth.js.map +1 -1
  612. package/dist/web-backend-login.d.ts +130 -0
  613. package/dist/web-backend-login.js +225 -0
  614. package/dist/web-backend-login.js.map +1 -0
  615. package/dist/web-card.js.map +1 -1
  616. package/dist/web-channel-echo.js.map +1 -1
  617. package/dist/web-chat-history.d.ts +118 -8
  618. package/dist/web-chat-history.js +220 -25
  619. package/dist/web-chat-history.js.map +1 -1
  620. package/dist/web-commands.d.ts +5 -1
  621. package/dist/web-commands.js +1 -1
  622. package/dist/web-commands.js.map +1 -1
  623. package/dist/web-cookie-names.d.ts +5 -0
  624. package/dist/web-cookie-names.js +12 -0
  625. package/dist/web-cookie-names.js.map +1 -0
  626. package/dist/web-effect-bundle.d.ts +2 -2
  627. package/dist/web-effect-library.js.map +1 -1
  628. package/dist/web-effect-playback.js.map +1 -1
  629. package/dist/web-effect-profiles.d.ts +16 -16
  630. package/dist/web-effect-profiles.js +3 -2
  631. package/dist/web-effect-profiles.js.map +1 -1
  632. package/dist/web-effect-service.js.map +1 -1
  633. package/dist/web-effect-tools.js.map +1 -1
  634. package/dist/web-effect-trials.js.map +1 -1
  635. package/dist/web-effect-upload.js.map +1 -1
  636. package/dist/web-effect.d.ts +6 -6
  637. package/dist/web-effect.js.map +1 -1
  638. package/dist/web-experimental-http.d.ts +2 -0
  639. package/dist/web-experimental-http.js +12 -0
  640. package/dist/web-experimental-http.js.map +1 -0
  641. package/dist/web-experimental.d.ts +11 -0
  642. package/dist/web-experimental.js +49 -0
  643. package/dist/web-experimental.js.map +1 -0
  644. package/dist/web-guest-access.d.ts +4 -2
  645. package/dist/web-guest-access.js +22 -10
  646. package/dist/web-guest-access.js.map +1 -1
  647. package/dist/web-guest-rooms.js.map +1 -1
  648. package/dist/web-host-guard.d.ts +11 -0
  649. package/dist/web-host-guard.js +11 -0
  650. package/dist/web-host-guard.js.map +1 -1
  651. package/dist/web-icons.d.ts +33 -1
  652. package/dist/web-icons.js +83 -11
  653. package/dist/web-icons.js.map +1 -1
  654. package/dist/web-identity-color.js.map +1 -1
  655. package/dist/web-image-meta.d.ts +13 -0
  656. package/dist/web-image-meta.js +62 -0
  657. package/dist/web-image-meta.js.map +1 -1
  658. package/dist/web-images.js.map +1 -1
  659. package/dist/web-libavoid.d.ts +14 -0
  660. package/dist/web-libavoid.js +46 -0
  661. package/dist/web-libavoid.js.map +1 -0
  662. package/dist/web-login.js.map +1 -1
  663. package/dist/web-mentions.js.map +1 -1
  664. package/dist/web-mod-agent.js.map +1 -1
  665. package/dist/web-mod-store.d.ts +2 -0
  666. package/dist/web-mod-store.js +10 -0
  667. package/dist/web-mod-store.js.map +1 -1
  668. package/dist/web-office-store.d.ts +104 -0
  669. package/dist/web-office-store.js +236 -0
  670. package/dist/web-office-store.js.map +1 -0
  671. package/dist/web-owner-css-store.d.ts +44 -1
  672. package/dist/web-owner-css-store.js +70 -9
  673. package/dist/web-owner-css-store.js.map +1 -1
  674. package/dist/web-owner-css.js.map +1 -1
  675. package/dist/web-png.d.ts +33 -0
  676. package/dist/web-png.js +329 -0
  677. package/dist/web-png.js.map +1 -0
  678. package/dist/web-prefs.d.ts +6 -0
  679. package/dist/web-prefs.js +29 -3
  680. package/dist/web-prefs.js.map +1 -1
  681. package/dist/web-preview.d.ts +2 -0
  682. package/dist/web-preview.js +2 -0
  683. package/dist/web-preview.js.map +1 -1
  684. package/dist/web-profile.js.map +1 -1
  685. package/dist/web-reactions.d.ts +35 -0
  686. package/dist/web-reactions.js +47 -0
  687. package/dist/web-reactions.js.map +1 -0
  688. package/dist/web-request-context.d.ts +5 -0
  689. package/dist/web-request-context.js.map +1 -1
  690. package/dist/web-returning-device.d.ts +4 -2
  691. package/dist/web-returning-device.js +13 -6
  692. package/dist/web-returning-device.js.map +1 -1
  693. package/dist/web-schedules.js.map +1 -1
  694. package/dist/web-send-dedupe.d.ts +36 -0
  695. package/dist/web-send-dedupe.js +69 -0
  696. package/dist/web-send-dedupe.js.map +1 -0
  697. package/dist/web-session-authorization.js.map +1 -1
  698. package/dist/web-session.js +1 -1
  699. package/dist/web-session.js.map +1 -1
  700. package/dist/web-settings-patch.d.ts +3 -1
  701. package/dist/web-settings-patch.js +11 -5
  702. package/dist/web-settings-patch.js.map +1 -1
  703. package/dist/web-settings-tools.d.ts +6 -0
  704. package/dist/web-settings-tools.js +52 -17
  705. package/dist/web-settings-tools.js.map +1 -1
  706. package/dist/web-terminal-http.js +15 -2
  707. package/dist/web-terminal-http.js.map +1 -1
  708. package/dist/web-terminal.js +1 -1
  709. package/dist/web-terminal.js.map +1 -1
  710. package/dist/web-theme-store.d.ts +2 -2
  711. package/dist/web-theme-store.js +6 -3
  712. package/dist/web-theme-store.js.map +1 -1
  713. package/dist/web-theme.d.ts +29 -0
  714. package/dist/web-theme.js +89 -1
  715. package/dist/web-theme.js.map +1 -1
  716. package/dist/web-upload.d.ts +18 -1
  717. package/dist/web-upload.js +45 -7
  718. package/dist/web-upload.js.map +1 -1
  719. package/dist/web-widget.js.map +1 -1
  720. package/dist/webhook-emitter.js.map +1 -1
  721. package/dist/windows-shortcuts.d.ts +78 -0
  722. package/dist/windows-shortcuts.js +203 -0
  723. package/dist/windows-shortcuts.js.map +1 -0
  724. package/dist/workflow-templates/default.md +4 -1
  725. package/dist/ws-server.js.map +1 -1
  726. package/dist/yaml-shared-nodes.d.ts +25 -0
  727. package/dist/yaml-shared-nodes.js +185 -0
  728. package/dist/yaml-shared-nodes.js.map +1 -0
  729. package/launcher/agend-install.cjs +828 -0
  730. package/launcher/install-admission.cjs +4 -39
  731. package/launcher/postinstall.cjs +14 -12
  732. package/launcher/private-home.cjs +43 -0
  733. package/package.json +16 -13
  734. package/scripts/preinstall-guard.cjs +2 -5
  735. package/templates/windows-shortcuts.ps1 +181 -0
  736. package/launcher/old-updater-note.cjs +0 -48
package/dist/cli.js CHANGED
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ // #2481: first — counts a `fleet start` attempt before anything below can fail (start-failure.ts).
3
+ import "./start-attempt.js";
2
4
  import "./node-warnings.js";
5
+ import { installFleetProcessHandlers } from "./fleet-process-handlers.js";
3
6
  import { applyNetworkReliabilityDefaults } from "./network-family.js";
4
7
  // Preserve Node's dual-stack Happy Eyeballs behavior, but avoid its 250ms
5
8
  // premature-connect failure on WSL/VPN/high-load hosts (#658).
@@ -12,32 +15,37 @@ applyNetworkReliabilityDefaults();
12
15
  // never restarts into a binary that would crash at the first native DB open.
13
16
  import { checkNodeVersion } from "./node-version-guard.js";
14
17
  checkNodeVersion();
15
- import { Command } from "commander";
18
+ import { Command, Option } from "commander";
19
+ import { registerBugReportCommand } from "./bug-report-cli.js";
16
20
  import { join, dirname } from "node:path";
17
21
  import { SchedulerDb } from "./scheduler/db.js";
18
22
  import { Cron } from "croner";
19
23
  import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, mkdirSync, chmodSync, readdirSync, rmSync, statSync, realpathSync, openSync, writeSync, closeSync, } from "node:fs";
20
24
  import { homedir, totalmem, freemem, userInfo } from "node:os";
21
25
  import { fileURLToPath } from "node:url";
22
- import { spawn, spawnSync, execSync, execFileSync } from "node:child_process";
26
+ import { spawnSync, execSync, execFileSync } from "node:child_process";
27
+ import { spawn } from "./child-process.js";
23
28
  import { getAgendHome, getTmuxSocketName, isDefaultAgendHome } from "./paths.js";
24
29
  import { hasPausedMarker } from "./pause-marker.js";
25
30
  import { COMPLETION_SHELLS, completionScript } from "./completion.js";
26
31
  import { getUpdateSelector, isExactVersion, isUnrequestedDowngrade, lookupTargetVersion, reportUpdateRestart, shouldSkipUpdate, runningFleetPredatesInstall, fleetProvenStopped, readFleetPidEvidence, pidAlive, processStartMs, } from "./update-check.js";
27
- import { clearUpdateMarker, markUpdateInProgress, setUpdateProgressStage } from "./update-marker.js";
32
+ import { clearUpdateMarker, markUpdateInProgress, readUpdateProgress, setUpdateProgressStage } from "./update-marker.js";
28
33
  import { describeSignalSource, forceAllowed, gateFleetControl, recordInstanceControl, withOrigin } from "./fleet-control-audit.js";
29
34
  import { FLEET_STOP_TIMEOUT_MS, stopDetachedOwner, systemdStopTimeoutMs, detachedProcessStart, detachedProcessExited } from "./fleet-stop-budget.js";
30
35
  import { acquireFleetLock, isFleetStartCommandLine, readProcessCommandLine, releaseProcessFleetLock, setProcessFleetLock } from "./fleet-lock.js";
31
36
  import { limitFleetCoreDumps } from "./coredump-filter.js";
32
- import { SYSTEMD_RESTART_TIMEOUT_MS } from "./service-installer.js";
37
+ import { SYSTEMD_RESTART_TIMEOUT_MS, getServicePath, getSystemServicePath } from "./service-installer.js";
38
+ import { withFsPlanLock } from "./service-plan.js";
33
39
  import { selectSystemdRestartTarget, SYSTEMD_RESTART_INDETERMINATE_EXIT_CODE, } from "./service-restart-selection.js";
34
40
  import { loadRawFleetConfig } from "./config.js";
35
- import { setLocale, t } from "./locale.js";
41
+ import { detectLocale, setLocale, t } from "./locale.js";
42
+ import { settingsSourceLabel } from "./settings-confirmation.js";
36
43
  import { DeliveryOutbox, deliveryStatusSelector } from "./delivery-outbox.js";
37
44
  import { DeliveryStatusArgs } from "./outbound-schemas.js";
38
45
  import { requestCpuProfile } from "./profile-control.js";
39
46
  import { requestSettingsConfirmation } from "./settings-control.js";
40
47
  import { canonicalCliEntry, selfCommand } from "./cli-entry.js";
48
+ import { DIRECT_FLEET_START_ENV, redirectsToService, startedByServiceManager } from "./service-owner.js";
41
49
  import { handOff, shouldHandOff } from "./launchd-handoff.js";
42
50
  /** Prefix tmux args with -L when socket isolation is active. */
43
51
  function tmuxArgs(args) {
@@ -65,6 +73,7 @@ function planDeps() {
65
73
  unlinkSync(path);
66
74
  }
67
75
  catch { /* absent */ } },
76
+ withPlanLock: (fn) => withFsPlanLock(DATA_DIR, fn),
68
77
  realpath: (path) => { try {
69
78
  return realpathSync(path);
70
79
  }
@@ -103,15 +112,38 @@ async function handOffLaunchdActivation(action) {
103
112
  return false;
104
113
  }
105
114
  const FLEET_CONFIG_PATH = join(DATA_DIR, "fleet.yaml");
106
- // CLI commands run outside FleetManager. Honor an explicit fleet locale while
107
- // keeping the historical English CLI default when config is missing/unset.
115
+ // CLI commands run outside FleetManager, in the fleet's language as the fleet decides it (#2390 follow-up): an explicit
116
+ // defaults.locale, else the host's zone (UTC+8 → zh-TW), else English. A line another program reads (the updater, the
117
+ // Windows installer and shortcuts, the rehearsal scripts) is never a locale string: tests/cli-machine-lines-2390 pins them.
118
+ let configForLocale;
108
119
  try {
109
- const configuredLocale = loadRawFleetConfig(FLEET_CONFIG_PATH).defaults?.locale;
110
- if (configuredLocale === "en" || configuredLocale === "zh-TW")
111
- setLocale(configuredLocale);
120
+ configForLocale = loadRawFleetConfig(FLEET_CONFIG_PATH);
112
121
  }
113
- catch { /* malformed or missing config: keep the English CLI default */ }
122
+ catch { /* malformed or missing config: the zone decides */ }
123
+ setLocale(detectLocale(configForLocale));
114
124
  const program = new Command();
125
+ /**
126
+ * #2481: an installed version whose fleet keeps failing to start is said on every command, with what to run to go back.
127
+ * Not for the fleet process itself (its own `fleet start`/`stop`, whose output is the service's log), completion, or
128
+ * machine output (--json); free once the installed version has started here (start-failure.ts).
129
+ */
130
+ const NO_START_BANNER = new Set(["fleet", "completion", "web-token", "health"]);
131
+ program.hook("preAction", async (_hooked, action) => {
132
+ let top = action;
133
+ while (top.parent && top.parent !== program)
134
+ top = top.parent;
135
+ if (NO_START_BANNER.has(top.name()) || action.opts().json)
136
+ return;
137
+ try {
138
+ const { checkStartFailure } = await import("./start-failure.js");
139
+ const failure = checkStartFailure(DATA_DIR);
140
+ if (!failure.failing)
141
+ return;
142
+ const { startFailureLines } = await import("./start-failure-notice.js");
143
+ console.error(["", ...startFailureLines(failure, DATA_DIR), ""].join("\n"));
144
+ }
145
+ catch { /* the banner never stands in a command's way */ }
146
+ });
115
147
  function claimFleetSingleton() {
116
148
  try {
117
149
  setProcessFleetLock(acquireFleetLock(DATA_DIR));
@@ -144,6 +176,7 @@ program
144
176
  // The root's own options only before a subcommand: otherwise `agend update --version 2.1.9` is read as
145
177
  // `agend --version`, prints this version and installs nothing.
146
178
  .enablePositionalOptions();
179
+ registerBugReportCommand(program, pkgVersion);
147
180
  function signalFleetReload() {
148
181
  const pidPath = join(DATA_DIR, "fleet.pid");
149
182
  try {
@@ -162,15 +195,15 @@ settingsCommand.command("confirm <id>").option("--yes", "Explicit noninteractive
162
195
  .action(async (id, options) => {
163
196
  try {
164
197
  const inspected = await requestSettingsConfirmation(DATA_DIR, { action: "inspect", id });
165
- console.log(`Source: ${inspected.pending_change.source}\nRequester: ${inspected.pending_change.requested_by}\n${inspected.pending_change.summary.join("\n")}`);
198
+ console.log([t("settings_confirm.source", settingsSourceLabel(inspected.pending_change.source)), t("settings_confirm.requester", inspected.pending_change.requested_by), ...inspected.pending_change.summary].join("\n"));
166
199
  let confirmed = options.yes === true;
167
200
  if (!confirmed) {
168
201
  if (!process.stdin.isTTY)
169
- throw new Error("Interactive confirmation required; --yes explicitly confirms the printed diff.");
202
+ throw new Error(t("settings_confirm.cli_interactive"));
170
203
  const { createInterface } = await import("node:readline/promises");
171
204
  const input = createInterface({ input: process.stdin, output: process.stdout });
172
205
  try {
173
- confirmed = (await input.question("Apply this exact change? [y/N] ")).trim().toLowerCase() === "y";
206
+ confirmed = (await input.question(t("settings_confirm.cli_question"))).trim().toLowerCase() === "y";
174
207
  }
175
208
  finally {
176
209
  input.close();
@@ -186,10 +219,10 @@ settingsCommand.command("confirm <id>").option("--yes", "Explicit noninteractive
186
219
  process.exitCode = 1;
187
220
  }
188
221
  });
189
- settingsCommand.command("reject <id>").action(async (id) => {
222
+ settingsCommand.command("reject <id>").description("Reject a pending Settings change on this host").action(async (id) => {
190
223
  try {
191
224
  const inspected = await requestSettingsConfirmation(DATA_DIR, { action: "inspect", id });
192
- console.log(`Source: ${inspected.pending_change.source}\nRequester: ${inspected.pending_change.requested_by}\n${inspected.pending_change.summary.join("\n")}`);
225
+ console.log([t("settings_confirm.source", settingsSourceLabel(inspected.pending_change.source)), t("settings_confirm.requester", inspected.pending_change.requested_by), ...inspected.pending_change.summary].join("\n"));
193
226
  const result = await requestSettingsConfirmation(DATA_DIR, { action: "reject", ticket: inspected.ticket });
194
227
  console.log(result.pending_change.outcome?.message ?? result.pending_change.state);
195
228
  }
@@ -215,12 +248,24 @@ fleet
215
248
  .command("start")
216
249
  .description("Start fleet or specific instance")
217
250
  .argument("[instance]", "Specific instance to start")
218
- .action(async (instance) => {
251
+ .option("--direct", "Start here even though an AgEnD service is installed (operators only: the service cannot run on this host)")
252
+ .action(async (instance, opts) => {
219
253
  // Root running fleet = sandbox environment (e.g. Docker, WSL root service)
220
254
  // Set IS_SANDBOX so child processes (claude-code) don't refuse to run
221
255
  if (process.getuid?.() === 0 && !process.env.IS_SANDBOX) {
222
256
  process.env.IS_SANDBOX = "1";
223
257
  }
258
+ const marked = process.env[DIRECT_FLEET_START_ENV] === "1";
259
+ delete process.env[DIRECT_FLEET_START_ENV];
260
+ // #2356: the way out when the installed service cannot run here (never activated, no GUI session for launchd) — an
261
+ // operator's own decision, as `restart --force` is; the fleet's singleton lock still refuses a second fleet.
262
+ if (opts?.direct && !forceAllowed()) {
263
+ console.error(t("cli.direct_refused"));
264
+ process.exit(1);
265
+ }
266
+ const direct = marked || !!opts?.direct;
267
+ if (!instance && redirectToService("start", [], direct))
268
+ return;
224
269
  if (instance) {
225
270
  // If fleet daemon is already running, delegate via HTTP API
226
271
  const { loadFleetConfig } = await import("./config.js");
@@ -275,67 +320,16 @@ fleet
275
320
  // without these a Ctrl-C or `systemctl stop` in that window got Node's default
276
321
  // behaviour: instant death with fleet.pid already written, databases unclosed,
277
322
  // and every tmux window orphaned.
278
- let stopping = false;
279
- const shutdown = async () => {
280
- if (stopping)
281
- return; // SIGINT and SIGTERM share this, and crash paths call stopAll too
282
- stopping = true;
283
- console.log("\nStopping fleet...");
284
- // stopAll fences new profile requests synchronously, before it awaits
285
- // the shared owner's stop/save. Stopping the env handle first would leave
286
- // the manager open to replacing that closed owner during the save.
287
- await fm.stopAll().catch(err => console.error("Shutdown error:", err));
288
- process.exit(0);
289
- };
290
- process.on("SIGINT", shutdown);
291
- process.on("SIGTERM", () => {
292
- // The restart that interrupted a fleet used to leave no trace of who asked.
293
- console.log(describeSignalSource(DATA_DIR, "SIGTERM"));
294
- void shutdown();
295
- });
296
- process.on("uncaughtException", (err) => {
297
- // An uncaught exception leaves unknown state — still the one case worth
298
- // tearing down for.
299
- console.error("Uncaught exception:", err);
300
- if (stopping)
301
- return;
302
- stopping = true;
303
- fm.stopAll().catch(() => { }).finally(() => process.exit(1));
304
- });
305
- process.on("unhandledRejection", (err) => {
306
- const msg = err instanceof Error ? err.message : String(err);
307
- // 409 = another bot poller exists — the adapter retries, don't crash.
308
- if (msg.includes("409") && msg.includes("getUpdates")) {
309
- console.error("Bot polling conflict (409) — retrying...");
310
- return;
311
- }
312
- // Do NOT take the fleet down. A rejected promise in one instance's tmux
313
- // call, health tick, or model switch used to stop every other working
314
- // instance and exit(1) — the widest blast radius in the process. The
315
- // per-instance health check and crash recovery handle a genuinely broken
316
- // instance on their own.
317
- //
318
- // Surviving quietly would be its own failure mode, so this reports to the
319
- // General topic as well as the log: the operator must not have to read
320
- // daemon.log to learn the fleet swallowed a fault. notifyFleetError()
321
- // throttles repeats, since these usually arrive from a loop.
322
- console.error("Unhandled rejection (fleet continues):", err);
323
- if (err instanceof Error && err.stack)
324
- console.error(err.stack);
325
- try {
326
- fm.notifyFleetError(`⚠️ Fleet caught an unhandled error and kept running:\n\`${msg}\`\nSee daemon.log for the stack trace.`);
327
- }
328
- catch (notifyErr) {
329
- // Never let the reporting path itself become an unhandled rejection.
330
- console.error("Failed to report unhandled rejection:", notifyErr);
331
- }
323
+ const handlers = installFleetProcessHandlers({
324
+ dataDir: DATA_DIR, manager: fm, runtime: process, console,
325
+ describeSignal: () => describeSignalSource(DATA_DIR, "SIGTERM"),
332
326
  });
333
327
  await fm.startCpuProfileControl().catch(err => console.warn(`Local profile control unavailable: ${String(err)}`));
334
328
  await fm.startSettingsConfirmationControl().catch(err => console.warn(`Local Settings confirmation unavailable: ${String(err)}`));
335
- if (stopping)
329
+ if (handlers.stopping)
336
330
  return;
337
331
  cpuProfile = await fm.startEnvironmentCpuProfile();
338
- if (stopping) {
332
+ if (handlers.stopping) {
339
333
  await cpuProfile?.stop("startup superseded by shutdown");
340
334
  return;
341
335
  }
@@ -366,6 +360,8 @@ fleet
366
360
  .argument("[instance]", "Specific instance to stop")
367
361
  .option("-y, --yes", "Confirm when run from a fleet agent session (this affects every instance)")
368
362
  .action(async (instance, opts) => {
363
+ if (!instance && redirectToService("stop", opts?.yes ? ["--yes"] : [], false))
364
+ return;
369
365
  if (instance)
370
366
  recordInstanceControl(DATA_DIR, "instance-stop", instance);
371
367
  else if (!gateFleetControl(DATA_DIR, "fleet-stop", { yes: opts?.yes }))
@@ -399,7 +395,7 @@ fleet
399
395
  }
400
396
  }
401
397
  catch {
402
- console.error(`Cannot connect to fleet (port ${port}). Is the fleet running?`);
398
+ console.error(t("cli.fleet_unreachable", port));
403
399
  process.exit(1);
404
400
  }
405
401
  return;
@@ -440,176 +436,37 @@ fleet
440
436
  });
441
437
  fleet
442
438
  .command("restart")
443
- .description("Graceful restart: wait for instances to idle, then restart")
444
- .argument("[instance]", "Specific instance to restart (immediate, no idle wait)")
445
- .option("--reload", "Full process restart to load new code")
439
+ .description("Deprecated: runs `agend restart` (removed in 2.4)")
440
+ .argument("[instance]", "Restart only this instance (now `agend restart <instance>`)")
441
+ .option("--reload", "Ignored: every full restart is `agend restart`")
446
442
  .option("-y, --yes", "Confirm when run from a fleet agent session (this affects every instance)")
447
- .action(async (instance, opts) => {
443
+ .action((instance, opts) => {
448
444
  if (instance && opts?.reload) {
449
445
  console.error("--reload restarts the entire fleet process. Cannot combine with instance name.");
450
446
  process.exit(1);
451
447
  }
452
- if (instance)
453
- recordInstanceControl(DATA_DIR, "instance-restart", instance);
454
- else if (!gateFleetControl(DATA_DIR, opts?.reload ? "fleet-restart-reload" : "fleet-restart", { yes: opts?.yes }))
455
- process.exit(1);
456
- if (instance) {
457
- // Single instance restart via fleet's HTTP API
458
- const { loadFleetConfig } = await import("./config.js");
459
- const fleet = loadFleetConfig(FLEET_CONFIG_PATH);
460
- const port = fleet.health_port ?? 19280;
461
- let token = "";
462
- try {
463
- token = readFileSync(join(DATA_DIR, "web.token"), "utf-8").trim();
464
- }
465
- catch { /* fleet may not be running */ }
466
- try {
467
- const resp = await fetch(`http://127.0.0.1:${port}/restart/${encodeURIComponent(instance)}`, {
468
- method: "POST",
469
- headers: token ? { "X-Agend-Token": token } : {},
470
- });
471
- const body = await resp.json();
472
- if (resp.ok) {
473
- console.log(`Instance "${instance}" restarted (immediate)`);
474
- }
475
- else {
476
- console.error(`Restart failed: ${body.error ?? resp.statusText}`);
477
- process.exit(1);
478
- }
479
- }
480
- catch (err) {
481
- console.error(`Cannot connect to fleet (port ${port}). Is the fleet running?`);
482
- process.exit(1);
483
- }
484
- return;
485
- }
486
- const pidPath = join(DATA_DIR, "fleet.pid");
487
- if (!existsSync(pidPath)) {
488
- console.error("Fleet is not running (no PID file found)");
489
- process.exit(1);
490
- }
491
- const pid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
492
- if (!Number.isInteger(pid) || pid <= 0) {
493
- console.error(`Fleet PID file at ${pidPath} is corrupted`);
494
- process.exit(1);
495
- }
496
- if (opts?.reload) {
497
- // Check if managed by launchd — if so, just signal and let launchd restart
498
- let managedByLaunchd = false;
499
- try {
500
- const ppid = parseInt(execFileSync("ps", ["-o", "ppid=", "-p", String(pid)]).toString().trim(), 10);
501
- managedByLaunchd = ppid === 1;
502
- }
503
- catch { /* ignore */ }
504
- try {
505
- process.kill(pid, "SIGUSR1");
506
- }
507
- catch {
508
- console.error("Failed to send reload signal (process may have exited)");
509
- process.exit(1);
510
- }
511
- if (managedByLaunchd) {
512
- console.log("Fleet is managed by launchd — sent reload signal.");
513
- console.log("launchd will automatically restart with new code.");
514
- // Wait briefly for old process to exit, then confirm new one started
515
- const deadline = Date.now() + 6 * 60 * 1000;
516
- while (Date.now() < deadline) {
517
- try {
518
- process.kill(pid, 0);
519
- await new Promise(r => setTimeout(r, 500));
520
- }
521
- catch {
522
- break;
523
- }
524
- }
525
- // Wait for launchd to start new process
526
- await new Promise(r => setTimeout(r, 2000));
527
- if (existsSync(pidPath)) {
528
- const newPid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
529
- if (newPid !== pid) {
530
- console.log(`New fleet process started (PID ${newPid})`);
531
- }
532
- }
533
- return;
534
- }
535
- console.log("Full restart signal sent — waiting for fleet to stop...");
536
- // Wait for old process to exit (up to 6 minutes: 5 min idle wait + buffer)
537
- const deadline = Date.now() + 6 * 60 * 1000;
538
- while (Date.now() < deadline) {
539
- try {
540
- process.kill(pid, 0); // check if alive
541
- await new Promise(r => setTimeout(r, 500));
542
- }
543
- catch {
544
- break; // process exited
545
- }
546
- }
547
- // Verify it actually exited
548
- try {
549
- process.kill(pid, 0);
550
- console.error("Old fleet process still running after timeout — aborting");
551
- process.exit(1);
552
- }
553
- catch {
554
- // Good, it exited
555
- }
556
- console.log("Old fleet stopped. Starting with new code...");
557
- // Start new fleet in this process (new Node.js process = new code loaded)
558
- if (!claimFleetSingleton())
559
- return;
560
- const { FleetManager } = await import("./fleet-manager.js");
561
- const fm = new FleetManager(DATA_DIR);
562
- await fm.startAll(FLEET_CONFIG_PATH);
563
- console.log("Fleet restarted with new code");
564
- // Keep process alive (same as fleet start)
565
- const shutdown = async () => {
566
- console.log("\nStopping fleet...");
567
- await fm.stopAll();
568
- process.exit(0);
569
- };
570
- process.on("SIGINT", shutdown);
571
- process.on("SIGTERM", shutdown);
572
- process.on("uncaughtException", async (err) => {
573
- console.error("Uncaught exception:", err);
574
- await fm.stopAll().catch(() => { });
575
- process.exit(1);
576
- });
577
- process.on("unhandledRejection", async (err) => {
578
- const msg = err instanceof Error ? err.message : String(err);
579
- if (msg.includes("409") && msg.includes("getUpdates")) {
580
- console.error("Bot polling conflict (409) — retrying...");
581
- return;
582
- }
583
- console.error("Unhandled rejection:", err);
584
- await fm.stopAll().catch(() => { });
585
- process.exit(1);
586
- });
587
- }
588
- else {
589
- // Instance-only restart (existing behavior)
590
- try {
591
- process.kill(pid, "SIGUSR2");
592
- console.log("Graceful restart signal sent — fleet will restart when all instances are idle");
593
- }
594
- catch {
595
- console.error("Failed to send restart signal (process may have exited)");
596
- process.exit(1);
597
- }
598
- }
448
+ // #2356: no restart of its own any more. Its SIGUSR1 reload made the fleet exit 0 and took over in this process, so
449
+ // under a Restart=on-failure unit the fleet stayed down (and this process, in an agent pane, died with the unit's
450
+ // cgroup). It runs `agend restart` itself — this Node, this CLI — with the same gate, guard and exit code.
451
+ const args = instance ? ["restart", instance] : ["restart", ...(opts?.yes ? ["--yes"] : [])];
452
+ console.error(t("cli.fleet_restart_deprecated", args.join(" ")));
453
+ const self = selfCommand(args);
454
+ const result = spawnSync(self.command, self.args, { stdio: "inherit" });
455
+ process.exit(result.status ?? 1);
599
456
  });
600
457
  fleet
601
- .command("status")
602
- .description("Show fleet status, workspace disk usage, unregistered instance directories, and host memory/swap")
458
+ .command("status", { hidden: true })
459
+ .description("Alias of `agend ls --resources`")
603
460
  .option("--json", "Output as JSON")
604
461
  .action(async (opts) => {
605
- // Delegate to the `ls` command implementation
606
462
  await lsAction({ ...opts, resources: true });
607
463
  });
608
464
  fleet
609
- .command("logs")
610
- .description("Alias for `agend logs`")
465
+ .command("logs", { hidden: true })
466
+ .description("Removed; use `agend logs`")
611
467
  .action(() => {
612
- console.log("Use `agend logs` instead. Run `agend logs --help` for options.");
468
+ console.error(t("cli.fleet_logs_removed"));
469
+ process.exitCode = 1;
613
470
  });
614
471
  fleet
615
472
  .command("history")
@@ -941,9 +798,6 @@ backend
941
798
  console.error(`Unknown backend: ${backendName}. Available: ${BACKENDS.map(b => b.id).join(", ")}`);
942
799
  process.exit(1);
943
800
  }
944
- if (info.deprecated) {
945
- console.warn(`⚠️ ${info.label} is deprecated (stops 2026-06-18). Consider switching to backend: antigravity`);
946
- }
947
801
  let issues = 0;
948
802
  const ok = (msg) => console.log(` \x1b[32m✓\x1b[0m ${msg}`);
949
803
  const fail = (msg) => { issues++; console.log(` \x1b[31m✗\x1b[0m ${msg}`); };
@@ -1207,7 +1061,7 @@ async function doctorMcp() {
1207
1061
  }
1208
1062
  if (!ipcOk) {
1209
1063
  issues.push("IPC ✗ (socket not responding)");
1210
- suggestions.push("agend fleet restart");
1064
+ suggestions.push("agend restart");
1211
1065
  }
1212
1066
  // 2. MCP config check
1213
1067
  let mcpOk = false;
@@ -1552,6 +1406,12 @@ program
1552
1406
  // the cleanup write later can prove it still owns the marker (checklist
1553
1407
  // 3/5). A missed capture only skips the marker write; retire still runs.
1554
1408
  let cleanupReceipt = null;
1409
+ // #2264 review: the old system copy is retired only for a confirmed 2.1-era layout — the copy this updater runs
1410
+ // from is a 2.1.x release (2.1's updater left it there). Never from "nvm" or "root" alone: a 2.2+ copy stays.
1411
+ if (verified.retireSystemCopy && !/^2\.1\./.test(pkgVersion)) {
1412
+ console.log(t("cli.system_install_kept", pkgVersion));
1413
+ delete verified.retireSystemCopy;
1414
+ }
1555
1415
  if (verified.retireSystemCopy) {
1556
1416
  try {
1557
1417
  const { markCleanupPending } = await import("./update-marker.js");
@@ -1658,12 +1518,16 @@ program
1658
1518
  console.log(" Note: the old system install was kept, since the new one is not running yet.");
1659
1519
  }
1660
1520
  }
1661
- // The refresh above (`install --no-activate`) records a launchd plan; this activation consumed it, whatever it
1662
- // came to — never leave one for a later `agend restart` to act on.
1521
+ // The refresh above (`install --no-activate`) records a launchd plan; this activation consumed it. Compare-and-
1522
+ // delete so a newer plan published during the slow bootout is left (#2376 / #2439).
1663
1523
  if (manager.kind === "launchd") {
1664
- const { planPath } = await import("./service-plan.js");
1665
- planDeps().removeFile(planPath(DATA_DIR));
1524
+ const { consumeCarriedOutPlan } = await import("./service-plan.js");
1525
+ const uid = process.getuid?.() ?? 501;
1526
+ consumeCarriedOutPlan(DATA_DIR, `gui/${uid}/com.agend.fleet`, planDeps());
1666
1527
  }
1528
+ // #1853: in a WSL terminal without the Windows desktop shortcuts, say once how to make them (never makes them).
1529
+ if (outcome.ok)
1530
+ await import("./windows-shortcuts.js").then(m => m.printUpdateShortcutsHint(DATA_DIR)).catch(() => { });
1667
1531
  if (outcome.ok && outcome.via === "launchd-activation") {
1668
1532
  console.log(" ✓ Service restarted (launchd loaded the verified job)");
1669
1533
  // The new fleet clears the update marker once it is up.
@@ -1750,6 +1614,7 @@ program
1750
1614
  }
1751
1615
  console.log(`\n ✓ Already up to date (v${pkgVersion})\n`);
1752
1616
  setUpdateProgressStage(DATA_DIR, "complete", { version: pkgVersion });
1617
+ await import("./windows-shortcuts.js").then(m => m.printUpdateShortcutsHint(DATA_DIR)).catch(() => { });
1753
1618
  return;
1754
1619
  }
1755
1620
  if (isUnrequestedDowngrade(pkgVersion, targetVersion, opts)) {
@@ -1796,6 +1661,49 @@ program
1796
1661
  if (verdict.kind === "unknown")
1797
1662
  console.log(verdict.note);
1798
1663
  }
1664
+ // ── #2264: the target release installs itself ──
1665
+ // `agend update` only chooses the version; the install — repair, `npm install -g`, proof, service, restart, the
1666
+ // chat's progress — is that release's own installer (launcher/agend-install.cjs), fetched pinned by npm with its
1667
+ // integrity checks. So every update runs the NEWEST install logic, never this (possibly old) package's, and the
1668
+ // one-liner runs the same program. The code below this branch is the compatibility path, unchanged: for a prefix
1669
+ // that needs sudo (the nvm route), and for a target from before the installer.
1670
+ // Only a known target that ships the installer runs it: `npm exec --package <a release without agend-install>` would
1671
+ // run whatever agend-install PATH has (#2264 review). An older target, or one npm cannot describe, takes the
1672
+ // compatibility path below; `--target` makes any other installer refuse.
1673
+ const shipsInstaller = () => {
1674
+ const viewed = spawnSync("npm", ["view", pkg, "bin", "--json"], { encoding: "utf-8", timeout: 60_000, stdio: ["ignore", "pipe", "ignore"] });
1675
+ try {
1676
+ const parsed = JSON.parse(viewed.stdout || "null");
1677
+ const bin = Array.isArray(parsed) ? parsed[parsed.length - 1] : parsed;
1678
+ return viewed.status === 0 && !!bin && typeof bin === "object" && Object.hasOwn(bin, "agend-install");
1679
+ }
1680
+ catch {
1681
+ return false;
1682
+ }
1683
+ };
1684
+ const viaInstaller = !needsSudo && targetVersion !== null && shipsInstaller();
1685
+ if (!needsSudo && !viaInstaller)
1686
+ console.log(t("cli.no_own_installer", pkg));
1687
+ if (viaInstaller) {
1688
+ console.log(`\n Updating AgEnD v${pkgVersion} → v${targetVersion} (its own installer)...\n`);
1689
+ markUpdateInProgress(DATA_DIR);
1690
+ setUpdateProgressStage(DATA_DIR, "downloading", { version: targetVersion });
1691
+ // AGEND_UPDATE_OWNER_PID: the marker this process just wrote is the installer's to carry on (and no one else's).
1692
+ const installer = spawnSync("npm", ["exec", "--yes", "--package", pkg, "--", "agend-install", "--upgrade", "--target", targetVersion], {
1693
+ stdio: "inherit", env: { ...withOrigin("agend-update"), AGEND_UPDATE_OWNER_PID: String(process.pid) },
1694
+ });
1695
+ if (installer.status === 0 || installer.status === 75) {
1696
+ process.exitCode = installer.status ?? 0;
1697
+ return;
1698
+ }
1699
+ // The installer records its own failure; one that never started (npm could not fetch it) is recorded here.
1700
+ const progress = readUpdateProgress(DATA_DIR)?.progress;
1701
+ if (progress && progress.stage !== "failed" && progress.stage !== "complete") {
1702
+ setUpdateProgressStage(DATA_DIR, "failed", { error: `the installer for ${pkg} did not run (npm exec exited ${installer.status ?? installer.signal ?? installer.error?.message}); nothing was changed` });
1703
+ }
1704
+ process.exitCode = installer.status || 1;
1705
+ return;
1706
+ }
1799
1707
  if (targetVersion) {
1800
1708
  console.log(`\n Updating AgEnD v${pkgVersion} → v${targetVersion}...\n`);
1801
1709
  }
@@ -1906,9 +1814,12 @@ program
1906
1814
  program
1907
1815
  .command("install")
1908
1816
  .description("Install the AgEnD system service and start it by default")
1909
- .option("--activate", "Deprecated; service activation is now the default")
1817
+ .addOption(new Option("--activate").hideHelp())
1910
1818
  .option("--no-activate", "Write the service file without activating it")
1911
1819
  .action(async (opts) => {
1820
+ if (process.argv.includes("--activate")) {
1821
+ console.error(t("cli.install_activate_removed"));
1822
+ }
1912
1823
  const { installService, activateService, detectPlatform, renderLaunchdPlist, launchdPlistPath, getServicePath, getSystemServicePath, serviceHomeFor } = await import("./service-installer.js");
1913
1824
  // #1631 B: an older updater (2.2.0-beta.1 and before) refreshes a hand-started fleet's service too, through this
1914
1825
  // command — which would CREATE a unit or plist, and every later update would then take a manager it may not reach
@@ -1940,9 +1851,9 @@ program
1940
1851
  // #1450 C6: on launchd, writing a new plist without activating is a PLANNED activation — proven, recorded, and
1941
1852
  // left for `agend restart` (a reload there would itself start the job). See service-plan.ts.
1942
1853
  if (opts.activate === false && detectPlatform() === "macos") {
1943
- const { refreshLaunchdWithoutActivating, planPath } = await import("./service-plan.js");
1854
+ const { refreshLaunchdWithoutActivating, revokePlan } = await import("./service-plan.js");
1944
1855
  // A refresh revokes any earlier plan first, whatever it comes to (a failed refresh leaves nothing to activate).
1945
- planDeps().removeFile(planPath(DATA_DIR));
1856
+ revokePlan(DATA_DIR, planDeps());
1946
1857
  const { expectedTuple } = await import("./restart-guard.js");
1947
1858
  const expectation = expectedTuple();
1948
1859
  if (!expectation.ok) {
@@ -1989,6 +1900,13 @@ program
1989
1900
  process.exitCode = 1;
1990
1901
  }
1991
1902
  }
1903
+ // #2331: a host whose fleet runs under the system unit gets no second, user unit from the refresh — it would only
1904
+ // sit beside it unused, and be named as a leftover on every update after.
1905
+ if (systemUnit) {
1906
+ console.log(t("install.path", systemUnit));
1907
+ console.log(t("install.updated"));
1908
+ return;
1909
+ }
1992
1910
  const svcPath = installService(serviceVars);
1993
1911
  console.log(t("install.path", svcPath));
1994
1912
  if (opts.activate !== false) {
@@ -2013,6 +1931,19 @@ program
2013
1931
  console.log(t("install.updated"));
2014
1932
  }
2015
1933
  });
1934
+ program
1935
+ .command("shortcuts")
1936
+ .description("WSL: create or update the Windows desktop shortcuts (the dashboard and restart)")
1937
+ .action(async () => {
1938
+ // #1853: for an install that predates the installer's shortcuts, or that is only ever updated with `agend update`.
1939
+ const { runShortcuts, realShortcutsDeps } = await import("./windows-shortcuts.js");
1940
+ const { spawnSync } = await import("node:child_process");
1941
+ const { userInfo } = await import("node:os");
1942
+ process.exitCode = await runShortcuts(realShortcutsDeps(DATA_DIR, userInfo().username, (command, args) => {
1943
+ const r = spawnSync(command, args, { encoding: "utf-8", timeout: 120_000, stdio: ["ignore", "pipe", "pipe"] });
1944
+ return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "", error: r.error };
1945
+ }));
1946
+ });
2016
1947
  program
2017
1948
  .command("uninstall")
2018
1949
  .description("Remove the AgEnD system service")
@@ -2074,7 +2005,22 @@ program
2074
2005
  .action(async (opts) => {
2075
2006
  if (!gateFleetControl(DATA_DIR, "stop", { yes: opts.yes }))
2076
2007
  process.exit(1);
2077
- const { getServicePath, stopService } = await import("./service-installer.js");
2008
+ // #2481: a deliberate stop — the start attempts so far are not a crash loop.
2009
+ {
2010
+ const { clearStartAttempts } = await import("./start-failure.js");
2011
+ clearStartAttempts(DATA_DIR);
2012
+ }
2013
+ const { getServicePath, getSystemServicePath, stopService, stopSystemService } = await import("./service-installer.js");
2014
+ // #2356: the system unit first, as `agend start` and `agend restart` choose it; a SIGTERM would only stop the process.
2015
+ if (getSystemServicePath()) {
2016
+ if (stopSystemService()) {
2017
+ console.log(t("cli.system_service_stopped"));
2018
+ return;
2019
+ }
2020
+ console.error(t("cli.system_service_stop_failed"));
2021
+ process.exitCode = 1;
2022
+ return;
2023
+ }
2078
2024
  if (!getServicePath()) {
2079
2025
  // No service — try killing by PID
2080
2026
  const pidPath = join(DATA_DIR, "fleet.pid");
@@ -2123,10 +2069,10 @@ program
2123
2069
  const systemServicePath = getSystemServicePath();
2124
2070
  if (!userServicePath && !systemServicePath) {
2125
2071
  console.log("No service installed. Starting fleet directly...");
2126
- const { spawn } = await import("node:child_process");
2072
+ const { spawn } = await import("./child-process.js");
2127
2073
  // This Node on this CLI, not `sh -c "agend fleet start"` through PATH (#1450 C5).
2128
2074
  const start = selfCommand(["fleet", "start"]);
2129
- const child = spawn(start.command, start.args, { detached: true, stdio: "ignore" });
2075
+ const child = spawn(start.command, start.args, { detached: true, stdio: "ignore", env: { ...process.env, [DIRECT_FLEET_START_ENV]: "1" } });
2130
2076
  child.unref();
2131
2077
  console.log("Fleet starting in background.");
2132
2078
  return;
@@ -2139,16 +2085,79 @@ program
2139
2085
  // A service file means the service manager owns this fleet. Falling back
2140
2086
  // to a detached process here creates a second fleet when the bus recovers.
2141
2087
  console.error("Failed to start the installed AgEnD service; refusing to start a detached duplicate.");
2142
- console.error("Check your service manager, then retry: systemctl --user status com.agend.fleet");
2088
+ console.error(t("cli.service_retry", process.platform === "darwin" ? "launchctl print gui/$(id -u)/com.agend.fleet" : systemServicePath ? "systemctl status agend" : "systemctl --user status com.agend.fleet"));
2089
+ // #2356: a service file that cannot run here (never activated, no session for it) would otherwise leave no start.
2090
+ console.error(t("cli.service_direct_hint"));
2143
2091
  process.exitCode = 1;
2144
2092
  }
2145
2093
  });
2094
+ /**
2095
+ * #2356 (user decision): on a host with an AgEnD service installed, a hand-run `fleet start` / `fleet stop` would run or
2096
+ * kill a fleet the service manager does not own — a duplicate beside the unit, or a stop systemd undoes. Run `agend
2097
+ * start` / `agend stop` instead (this Node, this CLI; their own gate and exit code). The manager's own `fleet start`
2098
+ * (the unit's ExecStart, the launchd job) and AgEnD's marked detached start go ahead directly; so does every host
2099
+ * without a service (Docker, manual setups). Exits when it redirected; false to go on.
2100
+ */
2101
+ function redirectToService(command, extra, directMarker) {
2102
+ let ownCgroup = null;
2103
+ try {
2104
+ ownCgroup = readFileSync("/proc/self/cgroup", "utf-8");
2105
+ }
2106
+ catch { /* not Linux */ }
2107
+ const byServiceManager = startedByServiceManager({ platform: process.platform, env: process.env, ownCgroup, ppid: process.ppid, launchdLabel: "com.agend.fleet" });
2108
+ const installed = getSystemServicePath() ?? getServicePath();
2109
+ if (!redirectsToService({ command, directMarker, byServiceManager, installedServicePath: installed }))
2110
+ return false;
2111
+ console.error(t("cli.service_redirect", String(installed), [command, ...extra].join(" ")));
2112
+ const self = selfCommand([command, ...extra]);
2113
+ const result = spawnSync(self.command, self.args, { stdio: "inherit" });
2114
+ process.exit(result.status ?? 1);
2115
+ }
2116
+ /** One instance, at once, through the running fleet's HTTP API; the fleet process keeps running. */
2117
+ async function restartOneInstance(instance) {
2118
+ recordInstanceControl(DATA_DIR, "instance-restart", instance);
2119
+ const { loadFleetConfig } = await import("./config.js");
2120
+ const fleet = loadFleetConfig(FLEET_CONFIG_PATH);
2121
+ const port = fleet.health_port ?? 19280;
2122
+ let token = "";
2123
+ try {
2124
+ token = readFileSync(join(DATA_DIR, "web.token"), "utf-8").trim();
2125
+ }
2126
+ catch { /* fleet may not be running */ }
2127
+ try {
2128
+ const resp = await fetch(`http://127.0.0.1:${port}/restart/${encodeURIComponent(instance)}`, {
2129
+ method: "POST",
2130
+ headers: token ? { "X-Agend-Token": token } : {},
2131
+ });
2132
+ const body = await resp.json();
2133
+ if (resp.ok) {
2134
+ console.log(t("cli.instance_restarted", instance));
2135
+ }
2136
+ else {
2137
+ console.error(t("cli.restart_failed", String(body.error ?? resp.statusText)));
2138
+ process.exit(1);
2139
+ }
2140
+ }
2141
+ catch {
2142
+ console.error(t("cli.fleet_unreachable", port));
2143
+ process.exit(1);
2144
+ }
2145
+ }
2146
2146
  program
2147
2147
  .command("restart")
2148
- .description("Restart the AgEnD service (auto-detects systemd/launchd/detached)")
2148
+ .description("Restart the AgEnD service (auto-detects systemd/launchd/detached) — the one full restart; or one instance")
2149
+ .argument("[instance]", "Restart only this instance, immediately; the fleet keeps running")
2149
2150
  .option("-y, --yes", "Confirm when run from a fleet agent session (this affects every instance)")
2150
2151
  .option("--force", "Restart even when the loaded service does not start this install on its selected Node (operators only)")
2151
- .action(async (opts) => {
2152
+ .action(async (instance, opts) => {
2153
+ if (instance) {
2154
+ if (opts.force) {
2155
+ console.error(t("cli.force_whole_service"));
2156
+ process.exit(1);
2157
+ }
2158
+ await restartOneInstance(instance);
2159
+ return;
2160
+ }
2152
2161
  // --force overrides the C6 guard: an operator's own decision, never a fleet agent's or a fleet-internal spawn's.
2153
2162
  if (opts.force && !forceAllowed()) {
2154
2163
  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.");
@@ -2198,8 +2207,9 @@ program
2198
2207
  // service's own HOME, which may differ from what this command resolves (sudo,
2199
2208
  // AGEND_HOME) — an absent pid file does NOT mean it's stopped. Ask the service
2200
2209
  // manager first. This is the canonical restart the `update` flow spawns.
2201
- const { detectPlatform, getServicePath, getSystemServicePath, getSystemdServiceState, systemdRestartOutcome, ensureSystemdUnitHardening, effectiveUnitDropIns, unitCoredumpFilterState, } = await import("./service-installer.js");
2202
- const { spawn, spawnSync } = await import("node:child_process");
2210
+ const { detectPlatform, getServicePath, getSystemServicePath, getSystemdServiceState, systemdRestartOutcome, ensureSystemdUnitHardening, effectiveUnitDropIns, unitCoredumpFilterState, loadedUnitCheck, } = await import("./service-installer.js");
2211
+ const { spawnSync } = await import("node:child_process");
2212
+ const { spawn } = await import("./child-process.js");
2203
2213
  const plat = detectPlatform();
2204
2214
  const pidPath = join(DATA_DIR, "fleet.pid");
2205
2215
  const run = (cmd) => {
@@ -2328,7 +2338,11 @@ program
2328
2338
  .filter(([key, state]) => key !== "KillMode" && (state === "added" || state === "upgraded"))
2329
2339
  .map(([key]) => key);
2330
2340
  if (updated.length > 0)
2331
- console.log(` ✓ ${unitPath}: updated ${updated.join(", ")} (#908/#1071/#1113: graceful stop, small crash dumps, bounded start, start limit)`);
2341
+ console.log(t("cli.unit_updated", String(unitPath), updated.join(", ")));
2342
+ // #2331: what systemd LOADED — a drop-in that refuses the fleet's READY is overridden by AgEnD's own, leftovers
2343
+ // are named. Not a gate: the fleet still restarts, and the line says what will happen and what to remove.
2344
+ for (const line of loadedUnitCheck({ user: systemdTarget.user, unit: systemdTarget.unit, unitPath }))
2345
+ console.log(line);
2332
2346
  // Re-judged after the reload, against what systemd now applies: an
2333
2347
  // operator's own mask (anywhere — main file, unit or type drop-in, or a
2334
2348
  // trailing empty assignment that restores the inherited one) is warned
@@ -2399,13 +2413,11 @@ program
2399
2413
  if (printed.status === 0) {
2400
2414
  const plistPath = getServicePath() ?? join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
2401
2415
  // Path 2 (#1450 C6): a proven new plist waits on disk with its plan — exactly one bootout + bootstrap.
2402
- const { readPlan, admitPlan, planPath, planCarriedOut } = await import("./service-plan.js");
2403
- const recorded = opts.force ? null : readPlan(DATA_DIR, deps);
2404
- // #1871: an interrupted update's plan whose job launchd already runs (after `agend start`) is done: restart that
2405
- // job as an unchanged one (path 1) instead of refusing it for its new pid.
2406
- const plan = recorded && planCarriedOut(recorded, target, deps) ? null : recorded;
2407
- if (recorded && !plan)
2408
- console.log(" ℹ launchd already runs the job an interrupted update planned; restarting it as it is.");
2416
+ const { pendingPlan, admitPlan, planPath, consumeUnchangedPlan, consumeCarriedOutPlan } = await import("./service-plan.js");
2417
+ // #1871: a plan whose job launchd already runs (an interrupted update, then `agend start`) is done: restart that
2418
+ // job as an unchanged one (path 1) instead of refusing it for its new pid. #2375: quietly (no note on every restart).
2419
+ const published = deps.readFile(planPath(DATA_DIR));
2420
+ const plan = opts.force ? null : pendingPlan(DATA_DIR, target, deps);
2409
2421
  if (plan && expectation.ok) {
2410
2422
  const pkgDir = dirname(dirname(expectation.expected.entry));
2411
2423
  const admitted = admitPlan(plan, { target, expected: expectation.expected, pkgDir }, deps);
@@ -2431,7 +2443,7 @@ program
2431
2443
  log: message => console.log(message),
2432
2444
  launchdPreimage: plan.preimage.plist,
2433
2445
  });
2434
- deps.removeFile(planPath(DATA_DIR));
2446
+ consumeUnchangedPlan(DATA_DIR, published, deps);
2435
2447
  if (outcome.ok) {
2436
2448
  console.log("Service restarted (launchd loaded the planned job).");
2437
2449
  return;
@@ -2440,7 +2452,9 @@ program
2440
2452
  process.exitCode = 1;
2441
2453
  return;
2442
2454
  }
2443
- // Path 1: an unchanged loaded job.
2455
+ // Path 1: an unchanged loaded job, or a leftover carried-out plan. Compare-and-delete that leftover (#2439);
2456
+ // pendingPlan itself stays read-only (#2376).
2457
+ consumeCarriedOutPlan(DATA_DIR, target, deps);
2444
2458
  if (expectation.ok && refuses(guard.guardLaunchd(captureRun, target, plistPath, deps.readFile, expectation.expected, guardDeps)))
2445
2459
  return;
2446
2460
  if (run(`launchctl kickstart -k ${target}`)) {
@@ -2565,7 +2579,7 @@ program
2565
2579
  unlinkSync(pidPath);
2566
2580
  }
2567
2581
  catch { /* old owner already removed it */ }
2568
- const child = spawn(start.command, start.args, { detached: true, stdio: "ignore" });
2582
+ const child = spawn(start.command, start.args, { detached: true, stdio: "ignore", env: { ...process.env, [DIRECT_FLEET_START_ENV]: "1" } });
2569
2583
  child.unref();
2570
2584
  console.log("Fleet restarting in background.");
2571
2585
  return;
@@ -2582,7 +2596,7 @@ program
2582
2596
  });
2583
2597
  program
2584
2598
  .command("setup")
2585
- .description("Open the guided setup page in a browser (before a fleet exists)")
2599
+ .description("Browser first-run page, before a fleet exists (not the CLI wizard; see `agend quickstart`)")
2586
2600
  .option("--reset", "Allow setup again after it has already been completed")
2587
2601
  .option("--port <port>", "Port for the setup page (default: the health port)")
2588
2602
  .option("--tunnel", "Expose the setup page through a Cloudflare quick tunnel so a phone can open it")
@@ -2677,14 +2691,14 @@ program
2677
2691
  });
2678
2692
  program
2679
2693
  .command("quickstart")
2680
- .description("Guided setup for a Backend, chat channel, projects, and fleet service")
2694
+ .description("CLI guided setup: backend, chat channel, projects, and fleet service (recommended)")
2681
2695
  .action(async () => {
2682
2696
  const { runQuickstart } = await import("./quickstart.js");
2683
2697
  await runQuickstart();
2684
2698
  });
2685
2699
  program
2686
2700
  .command("init")
2687
- .description("Interactive setup wizard (advanced)")
2701
+ .description("Interactive advanced wizard (setup-wizard); for first-run use `agend quickstart` or `agend setup`")
2688
2702
  .action(async () => {
2689
2703
  const { runSetupWizard } = await import("./setup-wizard.js");
2690
2704
  await runSetupWizard();
@@ -2733,7 +2747,7 @@ program
2733
2747
  minutes = Math.max(1, Math.round(((body.expiresAt ?? Date.now()) - Date.now()) / 60_000));
2734
2748
  }
2735
2749
  catch {
2736
- console.error(`Cannot connect to fleet (port ${port}). Is the fleet running?`);
2750
+ console.error(t("cli.fleet_unreachable", port));
2737
2751
  process.exit(1);
2738
2752
  }
2739
2753
  const signin = `http://localhost:${port}/signin`;
@@ -2776,9 +2790,24 @@ program
2776
2790
  program
2777
2791
  .command("validate")
2778
2792
  .description("Validate fleet.yaml and classicBot.yaml")
2779
- .action(async () => {
2780
- const { validateFleetConfig, validateClassicBotConfig } = await import("./config-validator.js");
2793
+ .option("--connections", "Only print how many chat connections fleet.yaml has, as CONNECTIONS=<n> (for scripts; CONNECTIONS=unknown and exit 1 when it cannot be read)")
2794
+ .action(async (opts) => {
2795
+ const { validateFleetConfig, validateClassicBotConfig, configuredConnectionCount } = await import("./config-validator.js");
2781
2796
  const yaml = (await import("js-yaml")).default;
2797
+ if (opts.connections) {
2798
+ // #2233: the Windows installer's question — did the setup page make a web-only fleet? Parsed, never grepped.
2799
+ let count = null;
2800
+ try {
2801
+ count = configuredConnectionCount(yaml.load(readFileSync(FLEET_CONFIG_PATH, "utf-8")));
2802
+ }
2803
+ catch {
2804
+ count = null;
2805
+ }
2806
+ console.log(`CONNECTIONS=${count ?? "unknown"}`);
2807
+ if (count === null)
2808
+ process.exit(1);
2809
+ return;
2810
+ }
2782
2811
  const red = (s) => `\x1b[31m${s}\x1b[0m`;
2783
2812
  const green = (s) => `\x1b[32m${s}\x1b[0m`;
2784
2813
  const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
@@ -2990,11 +3019,11 @@ schedule
2990
3019
  }
2991
3020
  });
2992
3021
  schedule
2993
- .command("trigger")
2994
- .description("Show how to manually trigger a schedule")
2995
- .argument("<id>", "Schedule ID")
2996
- .action((id) => {
2997
- console.log("Manual trigger requires fleet manager running. Use the Telegram interface instead.");
3022
+ .command("trigger", { hidden: true })
3023
+ .argument("[id]", "Schedule ID")
3024
+ .action(() => {
3025
+ console.error(t("cli.schedule_trigger_removed"));
3026
+ process.exitCode = 1;
2998
3027
  });
2999
3028
  // === Chat Export ===
3000
3029
  program
@@ -3081,26 +3110,88 @@ async function fuzzyMatch(query, names) {
3081
3110
  }
3082
3111
  async function resolveInstance(query, config) {
3083
3112
  const namesRaw = await listInstanceNames(config);
3084
- const names = [...new Set(namesRaw)]; // P3-3: deduplicate (a classicBot instanceName can equal a fleet name)
3085
- const { resolveName } = await import("./topic-commands.js");
3086
- const entries = names.map((name) => ({
3113
+ const names = [...new Set(namesRaw)];
3114
+ const { assignDisplayLabels } = await import("./topic-commands.js");
3115
+ const entries = names.map(name => ({
3087
3116
  name,
3088
- displayName: config.instances[name]?.display_name,
3117
+ displayName: config.instances[name]?.display_name ?? null,
3118
+ }));
3119
+ const labelMap = assignDisplayLabels(entries);
3120
+ // Build candidates with 1-based row index and status, matching ls order.
3121
+ const candidates = names.map((name, i) => ({
3122
+ idx: i + 1,
3123
+ realName: name,
3124
+ displayLabel: labelMap.get(name) ?? name,
3125
+ teams: getTeamsForInstance(config, name),
3126
+ status: getInstanceStatusStandalone(name),
3089
3127
  }));
3090
- const result = resolveName(query, entries);
3091
- if (result.kind === "name")
3092
- return result.realName;
3128
+ const { resolveCandidate } = await import("./ls-attach-resolve.js");
3129
+ // No argument on a TTY: show interactive picker.
3130
+ if (query === undefined) {
3131
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
3132
+ console.error("No instance specified.");
3133
+ process.exit(1);
3134
+ }
3135
+ return interactivePicker(candidates);
3136
+ }
3137
+ const result = resolveCandidate(query, candidates);
3138
+ if (result.kind === "exact")
3139
+ return result.candidate.realName;
3093
3140
  if (result.kind === "ambiguous") {
3094
- console.error(`"${query}" matches multiple instances: ${result.realNames.join(", ")}. Use the real name.`);
3141
+ if (process.stdin.isTTY && process.stdout.isTTY) {
3142
+ console.log(`"${query}" matches multiple instances:`);
3143
+ const { ambiguityLine } = await import("./ls-attach-resolve.js");
3144
+ const { computeLsNameWidth: cnw } = await import("./ls-rows.js");
3145
+ const maxNameW = cnw(result.candidates.map(c => c.realName), 0);
3146
+ for (const c of result.candidates) {
3147
+ console.log(ambiguityLine(c, maxNameW));
3148
+ }
3149
+ return interactivePicker(result.candidates);
3150
+ }
3151
+ const names2 = result.candidates.map(c => `${c.displayLabel} (${c.realName})`).join(", ");
3152
+ console.error(`"${query}" matches multiple instances: ${names2}. Use the real name.`);
3095
3153
  process.exit(1);
3096
3154
  }
3097
- // Fall through to the original fuzzy match on real names.
3098
- const match = await fuzzyMatch(query, names);
3099
- if (!match) {
3100
- console.error(`No instance matching "${query}". Available: ${names.join(", ")}`);
3101
- process.exit(1);
3155
+ // kind === "none" — show top-5 fuzzy suggestions, not the full list.
3156
+ const suggestions = result.suggestions;
3157
+ if (suggestions.length > 0) {
3158
+ console.error(`No instance matching "${query}". Did you mean:`);
3159
+ for (const c of suggestions) {
3160
+ console.error(` ${String(c.idx).padStart(2)}) ${c.displayLabel} (${c.realName})`);
3161
+ }
3162
+ }
3163
+ else {
3164
+ console.error(`No instance matching "${query}". Run "agend ls" to see all instances.`);
3102
3165
  }
3103
- return match;
3166
+ process.exit(1);
3167
+ }
3168
+ async function interactivePicker(candidates) {
3169
+ const running = candidates.filter(c => c.status === "running");
3170
+ const rest = candidates.filter(c => c.status !== "running");
3171
+ const ordered = [...running, ...rest];
3172
+ const { pickerDefaultIdx } = await import("./ls-attach-resolve.js");
3173
+ const defaultIdx = pickerDefaultIdx(ordered);
3174
+ const { pickerLine } = await import("./ls-attach-resolve.js");
3175
+ const { computeLsNameWidth: cnwPicker } = await import("./ls-rows.js");
3176
+ const maxNameW = cnwPicker(ordered.map(c => c.realName), 0);
3177
+ console.log("Select an instance:");
3178
+ for (const c of ordered) {
3179
+ console.log(pickerLine(c, maxNameW));
3180
+ }
3181
+ const { createInterface } = await import("node:readline");
3182
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
3183
+ // Use the unambiguous #N form so Enter always selects the displayed-first
3184
+ // row, even when its global idx matches a digit-string real name.
3185
+ const answer = await new Promise(resolve => rl.question(`\nRow number or name [#${defaultIdx}]: `, resolve));
3186
+ rl.close();
3187
+ const trimmed = answer.trim() || `#${defaultIdx}`;
3188
+ // Accept a number or re-run resolution.
3189
+ const { resolveCandidate } = await import("./ls-attach-resolve.js");
3190
+ const reResult = resolveCandidate(trimmed, candidates);
3191
+ if (reResult.kind === "exact")
3192
+ return reResult.candidate.realName;
3193
+ console.error(`Invalid selection "${trimmed}".`);
3194
+ process.exit(1);
3104
3195
  }
3105
3196
  function getInstanceStatusStandalone(name) {
3106
3197
  if (hasPausedMarker(join(DATA_DIR, "instances", name)))
@@ -3184,6 +3275,12 @@ async function lsAction(opts) {
3184
3275
  // status work (a tab press should not wait on the fleet) and must print
3185
3276
  // nothing but names — a "no instances" notice would become a candidate.
3186
3277
  if (opts.namesOnly) {
3278
+ const { validateNamesOnlyFilters } = await import("./ls-attach-resolve.js");
3279
+ const filterErr = validateNamesOnlyFilters(opts);
3280
+ if (filterErr) {
3281
+ console.error(filterErr);
3282
+ process.exit(1);
3283
+ }
3187
3284
  for (const n of await listInstanceNames(config))
3188
3285
  console.log(n);
3189
3286
  return;
@@ -3275,7 +3372,7 @@ async function lsAction(opts) {
3275
3372
  };
3276
3373
  // Row collection runs concurrently (see ls-rows.ts): the per-instance
3277
3374
  // tmux captures overlap instead of stacking their 2s bounds serially.
3278
- const { collectLsRows, capturePaneAsync, formatTimeSince, lsStatusIcon, lsStatusLabel, displayWidth } = await import("./ls-rows.js");
3375
+ const { collectLsRows, capturePaneAsync, formatTimeSince, lsStatusIcon, lsStatusLabel, displayWidth, computeLsNameWidth, padToDisplayWidth } = await import("./ls-rows.js");
3279
3376
  const { getTmuxSocketName } = await import("./paths.js");
3280
3377
  const socketName = getTmuxSocketName();
3281
3378
  const rows = await collectLsRows(allNames.map(name => {
@@ -3337,8 +3434,25 @@ async function lsAction(opts) {
3337
3434
  }
3338
3435
  }
3339
3436
  catch { /* fleet not running or API unreachable */ }
3437
+ // Assign stable global indices BEFORE any filtering (#P2-1 — filters must not
3438
+ // renumber rows, so `agend attach N` always resolves the same instance).
3439
+ const { applyLsFilters } = await import("./ls-attach-resolve.js");
3440
+ const allRowsIndexed = rows.map((r, i) => ({ ...r, globalIdx: i + 1 }));
3441
+ // Apply --running / --team / --backend CLI filters. Done before --json so
3442
+ // `ls --json --running` honours the flag (#P2-2).
3443
+ const filteredMeta = applyLsFilters(allRowsIndexed.map(r => ({
3444
+ globalIdx: r.globalIdx,
3445
+ realName: r.name,
3446
+ displayLabel: r.name,
3447
+ status: (r.status ?? "stopped"),
3448
+ teams: r.teams,
3449
+ backend: r.backend,
3450
+ })), { running: opts.running, team: opts.team, backend: opts.backend });
3451
+ const filteredNames = new Set(filteredMeta.map(m => m.realName));
3452
+ const displayRows = allRowsIndexed.filter(r => filteredNames.has(r.name));
3340
3453
  if (opts.json) {
3341
- console.log(JSON.stringify(rows, null, 2));
3454
+ // Strip internal globalIdx from JSON output to keep the schema stable.
3455
+ console.log(JSON.stringify(displayRows.map(({ globalIdx: _idx, ...rest }) => rest), null, 2));
3342
3456
  return;
3343
3457
  }
3344
3458
  // Merged State column (#1052): one glyph per lifecycle/execution state,
@@ -3350,19 +3464,24 @@ async function lsAction(opts) {
3350
3464
  // across the fleet (#1305 P2-1). --json and --names-only above keep the
3351
3465
  // real names (machine use + attach target).
3352
3466
  const { assignDisplayLabels } = await import("./topic-commands.js");
3353
- const lsLabels = assignDisplayLabels(rows.map((r) => ({
3467
+ const { lsDisplayAnnotation } = await import("./ls-attach-resolve.js");
3468
+ const lsLabels = assignDisplayLabels(displayRows.map((r) => ({
3354
3469
  name: r.name,
3355
3470
  displayName: config.instances[r.name]?.display_name,
3356
3471
  })));
3357
- const displayName = (row) => lsLabels.get(row.name) ?? row.name;
3358
- const nameW = Math.max(20, ...rows.map(r => displayWidth(displayName(r)) + 2));
3472
+ const displayLabel = (row) => lsLabels.get(row.name) ?? row.name;
3473
+ // Columns: # (global row number), Name (real, full, copyable), then the rest.
3474
+ // Display name (if set) appears at the end of the row as context — secondary.
3475
+ const numW = 3;
3476
+ const nameW = computeLsNameWidth(displayRows.map(r => r.name));
3359
3477
  const backendW = 14;
3360
3478
  const statusW = 12;
3361
3479
  const teamW = 20;
3362
3480
  const srcW = 4;
3363
3481
  const ctxW = 8;
3364
3482
  const memW = 8;
3365
- console.log("Name".padEnd(nameW) +
3483
+ console.log("#".padEnd(numW) +
3484
+ "Name".padEnd(nameW) +
3366
3485
  "Backend".padEnd(backendW) +
3367
3486
  "Status".padEnd(statusW) +
3368
3487
  "Team".padEnd(teamW) +
@@ -3370,25 +3489,28 @@ async function lsAction(opts) {
3370
3489
  "Ctx".padEnd(ctxW) +
3371
3490
  "Mem".padEnd(memW) +
3372
3491
  "Activity");
3373
- console.log("\u2500".repeat(nameW + backendW + statusW + teamW + srcW + ctxW + memW + 10));
3374
- for (const r of rows) {
3492
+ console.log("\u2500".repeat(numW + nameW + backendW + statusW + teamW + srcW + ctxW + memW + 10));
3493
+ for (const r of displayRows) {
3375
3494
  const teamStr = r.teams.length > 0 ? r.teams.join(",") : "-";
3376
3495
  const ctxStr = r.context != null ? `${Math.round(r.context)}%` : "-";
3377
3496
  const memStr = r.memMb != null ? `${r.memMb}MB` : "-";
3378
3497
  const actStr = r.lastActivity ?? "-";
3379
- console.log(padDisplay(displayName(r), nameW) +
3498
+ const dispAnnotation = lsDisplayAnnotation(r.name, displayLabel(r), opts.display ?? false);
3499
+ console.log(String(r.globalIdx).padEnd(numW) +
3500
+ padToDisplayWidth(r.name, nameW) +
3380
3501
  r.backend.padEnd(backendW) +
3381
3502
  statusIcon(r.status, r.idle, r.state) + " " + statusLabel(r.status, r.idle, r.state).padEnd(statusW - 2) +
3382
3503
  padDisplay(teamStr, teamW) +
3383
3504
  r.source.padEnd(srcW) +
3384
3505
  ctxStr.padEnd(ctxW) +
3385
3506
  memStr.padEnd(memW) +
3386
- actStr);
3507
+ actStr +
3508
+ dispAnnotation);
3387
3509
  }
3388
3510
  // System memory footer
3389
- const totalMemMb = rows.reduce((sum, r) => sum + (r.memMb ?? 0), 0);
3390
- const runningCount = rows.filter(r => r.status === "running").length;
3391
- const pausedCount = rows.filter(r => r.status === "paused").length;
3511
+ const totalMemMb = displayRows.reduce((sum, r) => sum + (r.memMb ?? 0), 0);
3512
+ const runningCount = displayRows.filter(r => r.status === "running").length;
3513
+ const pausedCount = displayRows.filter(r => r.status === "paused").length;
3392
3514
  const totalGB = totalmem() / (1024 ** 3);
3393
3515
  const usedGB = (totalmem() - freemem()) / (1024 ** 3);
3394
3516
  console.log(`\nInstances: ${runningCount} running, ${pausedCount} paused | Fleet Mem: ${(totalMemMb / 1024).toFixed(1)} GB | System Memory: ${usedGB.toFixed(1)} / ${totalGB.toFixed(1)} GB`);
@@ -3402,12 +3524,21 @@ async function lsAction(opts) {
3402
3524
  console.log("Tip: agend completion install — then agend attach <TAB> completes instance names.");
3403
3525
  }
3404
3526
  }
3405
- program
3406
- .command("ls")
3407
- .description("List all instances with status, backend, team, and last activity")
3408
- .option("--json", "Output as JSON")
3409
- .option("--names-only", "Print instance names one per line (for shell completion)")
3410
- .option("--resources", "Also report workspace disk usage, unregistered instance directories, and host memory/swap (text only)")
3527
+ function addLsOptions(cmd) {
3528
+ return cmd
3529
+ .option("--json", "Output as JSON")
3530
+ .option("--names-only", "Print instance names one per line (for shell completion)")
3531
+ .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")
3532
+ .option("--running", "Show only running instances")
3533
+ .option("--team <name>", "Filter by team name")
3534
+ .option("--backend <name>", "Filter by backend (exact match)")
3535
+ .option("--display", "Show display names as last column");
3536
+ }
3537
+ addLsOptions(program.command("ls").description("List all instances with status, backend, team, and last activity"))
3538
+ .action(async (opts) => {
3539
+ await lsAction(opts);
3540
+ });
3541
+ addLsOptions(program.command("status", { hidden: true }).description("Alias of `agend ls`"))
3411
3542
  .action(async (opts) => {
3412
3543
  await lsAction(opts);
3413
3544
  });
@@ -3434,9 +3565,10 @@ Then reload the shell. Tab-completes instance names for \`agend attach\` and
3434
3565
  // Subcommand names come from commander itself, so the script can't drift
3435
3566
  // out of sync with the CLI the way a hardcoded list would.
3436
3567
  const fleetCmd = program.commands.find(c => c.name() === "fleet");
3568
+ const visible = (cmds) => cmds.filter(c => !c._hidden).map(c => c.name());
3437
3569
  const spec = {
3438
- topLevel: program.commands.map(c => c.name()),
3439
- fleetSub: fleetCmd?.commands.map(c => c.name()) ?? [],
3570
+ topLevel: visible(program.commands),
3571
+ fleetSub: visible(fleetCmd?.commands ?? []),
3440
3572
  instanceCommands: ["attach"],
3441
3573
  fleetInstanceCommands: ["start", "stop", "restart"],
3442
3574
  };
@@ -3493,7 +3625,7 @@ Then reload the shell. Tab-completes instance names for \`agend attach\` and
3493
3625
  console.log(completionScript(target, spec));
3494
3626
  });
3495
3627
  program
3496
- .command("health").description("Fleet health check — shows problems and diagnostics")
3628
+ .command("health").description("Live fleet health of running instances (HTTP, tmux, IPC); for host/setup diagnostics use `agend doctor`")
3497
3629
  .option("--json", "Output as JSON")
3498
3630
  .option("-q, --quiet", "One-line summary only")
3499
3631
  .action(async (opts) => {
@@ -3642,8 +3774,8 @@ program
3642
3774
  });
3643
3775
  program
3644
3776
  .command("attach")
3645
- .description("Attach to an instance's tmux window (fuzzy match)")
3646
- .argument("<name>", "Instance name (supports fuzzy matching)")
3777
+ .description("Attach to an instance's tmux window. Accepts display name, instance name, row number from ls, or a substring. No argument: interactive picker.")
3778
+ .argument("[name]", "Instance name, display name, row number, or substring (omit for interactive picker)")
3647
3779
  .action(async (query) => {
3648
3780
  const yaml = (await import("js-yaml")).default;
3649
3781
  const config = yaml.load(readFileSync(FLEET_CONFIG_PATH, "utf-8"));
@@ -3731,7 +3863,7 @@ program
3731
3863
  });
3732
3864
  program
3733
3865
  .command("logs")
3734
- .description("Show fleet log (alias for `agend fleet logs`)")
3866
+ .description("Show fleet log (daemon.log, or fleet.log if the daemon log is not there yet)")
3735
3867
  .option("-n, --lines <count>", "Number of lines to show", "50")
3736
3868
  .option("-f, --follow", "Follow log output (like tail -f)")
3737
3869
  .option("--instance <name>", "Filter by instance name")