@agentprojectcontext/apx 1.77.3 → 1.79.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 (356) hide show
  1. package/README.md +23 -9
  2. package/package.json +12 -12
  3. package/src/core/agent/build-agent-system.js +3 -2
  4. package/src/core/agent/constants.js +3 -2
  5. package/src/core/agent/loop/greeting-guard.js +55 -0
  6. package/src/core/agent/loop/side-effects.js +52 -0
  7. package/src/core/agent/prompt-builder.js +49 -8
  8. package/src/core/agent/run-agent.js +43 -53
  9. package/src/core/agent/security.js +1 -1
  10. package/src/core/agent/self-memory.js +4 -2
  11. package/src/core/agent/skills/index-store.js +2 -2
  12. package/src/core/agent/skills/loader.js +3 -22
  13. package/src/core/agent/super-agent.js +0 -2
  14. package/src/core/agent/tool-summary.js +65 -0
  15. package/src/core/agent/tools/handlers/call-runtime.js +4 -4
  16. package/src/core/agent/tools/handlers/list-commitments.js +80 -0
  17. package/src/core/agent/tools/handlers/list-tasks.js +66 -27
  18. package/src/core/agent/tools/handlers/record-commitment.js +68 -0
  19. package/src/core/agent/tools/handlers/search-sessions.js +1 -1
  20. package/src/core/agent/tools/handlers/send-telegram.js +68 -2
  21. package/src/core/agent/tools/handlers/set-permission-mode.js +11 -4
  22. package/src/core/agent/tools/names.js +64 -0
  23. package/src/core/agent/tools/registry.js +9 -0
  24. package/src/core/agent/tools/tool-call-parser.js +70 -1
  25. package/src/core/apc/context-copy.js +1 -1
  26. package/src/core/apc/frontmatter.js +85 -0
  27. package/src/core/apc/parser.js +9 -21
  28. package/src/core/apc/paths.js +8 -8
  29. package/src/core/apc/scaffold.js +5 -4
  30. package/src/core/channels/telegram/ask-callbacks.js +35 -0
  31. package/src/core/channels/telegram/config.js +201 -0
  32. package/src/core/channels/telegram/dispatch.js +3 -0
  33. package/src/core/channels/telegram/reply.js +30 -5
  34. package/src/core/config/index.js +25 -183
  35. package/src/core/config/paths.js +57 -1
  36. package/src/core/config/redact.js +22 -0
  37. package/src/core/confirmation/adapters/web.js +1 -1
  38. package/src/core/daemon/service.js +238 -0
  39. package/src/core/deck/manifest.js +12 -12
  40. package/src/core/desktop/autostart.js +3 -2
  41. package/src/core/desktop/process.js +4 -4
  42. package/src/core/engines/gemini.js +322 -60
  43. package/src/core/engines/openai-compatible.js +21 -2
  44. package/src/core/engines/presets.js +2 -2
  45. package/src/core/http-tools/browser.js +1 -1
  46. package/src/core/http-tools/catalog.js +551 -0
  47. package/src/core/http-tools/fetch.js +1 -1
  48. package/src/core/http-tools/glob.js +3 -3
  49. package/src/core/http-tools/grep.js +2 -9
  50. package/src/core/http-tools/inline-handlers.js +59 -0
  51. package/src/core/http-tools/registry.js +31 -615
  52. package/src/core/http-tools/search.js +2 -2
  53. package/src/core/identity/self.js +6 -7
  54. package/src/core/identity/telegram.js +3 -3
  55. package/src/core/logging.js +6 -5
  56. package/src/core/mcp/sources.js +12 -16
  57. package/src/core/memory/consolidate.js +225 -0
  58. package/src/core/memory/indexer.js +2 -5
  59. package/src/core/nudge/index.js +192 -0
  60. package/src/core/nudge/policy.js +143 -0
  61. package/src/core/nudge/store.js +141 -0
  62. package/src/core/profiles/bundled/secretary/PROFILE.md +8 -9
  63. package/src/core/profiles/bundled/secretary/config.schema.json +33 -3
  64. package/src/core/profiles/bundled/secretary/routines/day-close.json +7 -3
  65. package/src/core/profiles/bundled/secretary/routines/day-open.json +7 -3
  66. package/src/core/profiles/bundled/secretary/routines/watch.json +13 -0
  67. package/src/core/profiles/lifecycle.js +0 -2
  68. package/src/core/profiles/store.js +2 -7
  69. package/src/core/routines/runner.js +103 -4
  70. package/src/core/routines/signals.js +270 -0
  71. package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
  72. package/src/core/runtime-skills/apx-profile/SKILL.md +3 -3
  73. package/src/core/runtime-skills/apx-project/SKILL.md +1 -1
  74. package/src/core/runtime-skills/apx-task/SKILL.md +8 -8
  75. package/src/core/runtime-skills/apx-telegram/SKILL.md +4 -4
  76. package/src/core/runtime-skills/apx-voice/SKILL.md +4 -4
  77. package/src/core/runtimes/detect.js +2 -2
  78. package/src/core/sessions/index.js +800 -0
  79. package/src/core/stores/code-sessions.js +3 -14
  80. package/src/core/stores/commitments.js +331 -0
  81. package/src/core/stores/engine-sessions.js +0 -4
  82. package/src/core/stores/messages.js +4 -5
  83. package/src/core/stores/organization.js +9 -11
  84. package/src/core/stores/routines.js +23 -14
  85. package/src/core/stores/runtime-callbacks.js +2 -1
  86. package/src/core/util/json-file.js +128 -0
  87. package/src/core/util/thinking.js +51 -0
  88. package/src/core/vars/sources.js +14 -21
  89. package/src/core/voice/stt-models.js +1 -1
  90. package/src/core/voice/tts.js +5 -4
  91. package/src/host/daemon/api/admin-config.js +5 -5
  92. package/src/host/daemon/api/admin.js +8 -7
  93. package/src/host/daemon/api/agents.js +16 -16
  94. package/src/host/daemon/api/artifact-preview.js +8 -8
  95. package/src/host/daemon/api/artifacts.js +7 -7
  96. package/src/host/daemon/api/code.js +8 -8
  97. package/src/host/daemon/api/commitments.js +135 -0
  98. package/src/host/daemon/api/config.js +6 -6
  99. package/src/host/daemon/api/confirm.js +2 -2
  100. package/src/host/daemon/api/connections.js +2 -2
  101. package/src/host/daemon/api/conversations.js +12 -12
  102. package/src/host/daemon/api/deck.js +6 -6
  103. package/src/host/daemon/api/desktop.js +8 -8
  104. package/src/host/daemon/api/embeddings.js +4 -4
  105. package/src/host/daemon/api/engines.js +8 -7
  106. package/src/host/daemon/api/exec.js +5 -4
  107. package/src/host/daemon/api/files-project.js +6 -6
  108. package/src/host/daemon/api/health.js +2 -2
  109. package/src/host/daemon/api/identity.js +3 -3
  110. package/src/host/daemon/api/inbox.js +2 -2
  111. package/src/host/daemon/api/integrations.js +23 -19
  112. package/src/host/daemon/api/mcps.js +16 -12
  113. package/src/host/daemon/api/messages.js +5 -5
  114. package/src/host/daemon/api/nudges.js +112 -0
  115. package/src/host/daemon/api/organization.js +8 -8
  116. package/src/host/daemon/api/pairing.js +6 -6
  117. package/src/host/daemon/api/plugins.js +3 -3
  118. package/src/host/daemon/api/prefix.js +20 -0
  119. package/src/host/daemon/api/profiles.js +9 -9
  120. package/src/host/daemon/api/projects.js +5 -5
  121. package/src/host/daemon/api/routines.js +32 -8
  122. package/src/host/daemon/api/run.js +2 -2
  123. package/src/host/daemon/api/runtimes.js +6 -6
  124. package/src/host/daemon/api/self-memory.js +50 -0
  125. package/src/host/daemon/api/sessions-search.js +18 -11
  126. package/src/host/daemon/api/sessions.js +6 -6
  127. package/src/host/daemon/api/shared.js +59 -13
  128. package/src/host/daemon/api/skills.js +12 -12
  129. package/src/host/daemon/api/super-agent.js +4 -4
  130. package/src/host/daemon/api/tasks.js +11 -11
  131. package/src/host/daemon/api/telegram.js +61 -24
  132. package/src/host/daemon/api/tools.js +7 -7
  133. package/src/host/daemon/api/top-level.js +7 -7
  134. package/src/host/daemon/api/transcribe.js +6 -6
  135. package/src/host/daemon/api/tts.js +3 -3
  136. package/src/host/daemon/api/vars.js +11 -8
  137. package/src/host/daemon/api/voice.js +9 -7
  138. package/src/host/daemon/api/web.js +28 -32
  139. package/src/host/daemon/api.js +83 -48
  140. package/src/host/daemon/callback-reconciler.js +16 -0
  141. package/src/host/daemon/db.js +1 -1
  142. package/src/host/daemon/desktop-ws.js +7 -3
  143. package/src/host/daemon/index.js +11 -5
  144. package/src/host/daemon/plugins/desktop/index.js +8 -2
  145. package/src/host/daemon/plugins/telegram/index.js +7 -2
  146. package/src/host/daemon/stt-venv.js +20 -81
  147. package/src/host/daemon/wakeup.js +18 -4
  148. package/src/interfaces/acp/index.js +3 -3
  149. package/src/interfaces/acp/session.js +2 -2
  150. package/src/interfaces/cli/commands/a2a.js +2 -2
  151. package/src/interfaces/cli/commands/agent.js +3 -3
  152. package/src/interfaces/cli/commands/artifact.js +13 -13
  153. package/src/interfaces/cli/commands/chat.js +3 -3
  154. package/src/interfaces/cli/commands/command.js +2 -2
  155. package/src/interfaces/cli/commands/commitment.js +154 -0
  156. package/src/interfaces/cli/commands/config.js +4 -4
  157. package/src/interfaces/cli/commands/daemon.js +74 -18
  158. package/src/interfaces/cli/commands/desktop.js +7 -14
  159. package/src/interfaces/cli/commands/exec.js +2 -2
  160. package/src/interfaces/cli/commands/log.js +3 -4
  161. package/src/interfaces/cli/commands/mcp.js +9 -9
  162. package/src/interfaces/cli/commands/memory.js +75 -2
  163. package/src/interfaces/cli/commands/messages.js +5 -5
  164. package/src/interfaces/cli/commands/model.js +0 -18
  165. package/src/interfaces/cli/commands/nudge.js +130 -0
  166. package/src/interfaces/cli/commands/obsidian.js +5 -5
  167. package/src/interfaces/cli/commands/org.js +5 -5
  168. package/src/interfaces/cli/commands/pair.js +6 -6
  169. package/src/interfaces/cli/commands/plugins.js +2 -2
  170. package/src/interfaces/cli/commands/profile.js +10 -10
  171. package/src/interfaces/cli/commands/project-config.js +6 -6
  172. package/src/interfaces/cli/commands/project.js +10 -10
  173. package/src/interfaces/cli/commands/routine.js +8 -9
  174. package/src/interfaces/cli/commands/runtime.js +2 -2
  175. package/src/interfaces/cli/commands/search.js +0 -1
  176. package/src/interfaces/cli/commands/session.js +12 -40
  177. package/src/interfaces/cli/commands/sessions.js +21 -798
  178. package/src/interfaces/cli/commands/setup.js +16 -6
  179. package/src/interfaces/cli/commands/skills.js +1 -2
  180. package/src/interfaces/cli/commands/status.js +3 -3
  181. package/src/interfaces/cli/commands/task.js +8 -8
  182. package/src/interfaces/cli/commands/telegram.js +21 -21
  183. package/src/interfaces/cli/commands/voice.js +6 -6
  184. package/src/interfaces/cli/help/index.js +2245 -0
  185. package/src/interfaces/cli/http.js +9 -4
  186. package/src/interfaces/cli/index.js +14 -2857
  187. package/src/interfaces/cli/routes/acp.js +13 -0
  188. package/src/interfaces/cli/routes/agent.js +27 -0
  189. package/src/interfaces/cli/routes/artifact.js +28 -0
  190. package/src/interfaces/cli/routes/chat.js +11 -0
  191. package/src/interfaces/cli/routes/code.js +11 -0
  192. package/src/interfaces/cli/routes/command.js +21 -0
  193. package/src/interfaces/cli/routes/commitment.js +19 -0
  194. package/src/interfaces/cli/routes/config.js +16 -0
  195. package/src/interfaces/cli/routes/connections.js +11 -0
  196. package/src/interfaces/cli/routes/conversations.js +21 -0
  197. package/src/interfaces/cli/routes/daemon.js +25 -0
  198. package/src/interfaces/cli/routes/desktop.js +20 -0
  199. package/src/interfaces/cli/routes/env.js +13 -0
  200. package/src/interfaces/cli/routes/exec.js +11 -0
  201. package/src/interfaces/cli/routes/identity.js +11 -0
  202. package/src/interfaces/cli/routes/index.js +75 -0
  203. package/src/interfaces/cli/routes/init.js +11 -0
  204. package/src/interfaces/cli/routes/log.js +20 -0
  205. package/src/interfaces/cli/routes/mcp.js +22 -0
  206. package/src/interfaces/cli/routes/memory.js +19 -0
  207. package/src/interfaces/cli/routes/messages.js +16 -0
  208. package/src/interfaces/cli/routes/model.js +11 -0
  209. package/src/interfaces/cli/routes/nudge.js +17 -0
  210. package/src/interfaces/cli/routes/obsidian.js +17 -0
  211. package/src/interfaces/cli/routes/org.js +34 -0
  212. package/src/interfaces/cli/routes/overlay.js +10 -0
  213. package/src/interfaces/cli/routes/pair.js +17 -0
  214. package/src/interfaces/cli/routes/panel.js +16 -0
  215. package/src/interfaces/cli/routes/permission.js +11 -0
  216. package/src/interfaces/cli/routes/plugins.js +21 -0
  217. package/src/interfaces/cli/routes/profile.js +27 -0
  218. package/src/interfaces/cli/routes/project.js +41 -0
  219. package/src/interfaces/cli/routes/restart.js +15 -0
  220. package/src/interfaces/cli/routes/routine.js +28 -0
  221. package/src/interfaces/cli/routes/run.js +11 -0
  222. package/src/interfaces/cli/routes/search.js +11 -0
  223. package/src/interfaces/cli/routes/send.js +11 -0
  224. package/src/interfaces/cli/routes/session.js +26 -0
  225. package/src/interfaces/cli/routes/sessions.js +15 -0
  226. package/src/interfaces/cli/routes/setup.js +18 -0
  227. package/src/interfaces/cli/routes/skills.js +20 -0
  228. package/src/interfaces/cli/routes/status.js +12 -0
  229. package/src/interfaces/cli/routes/task.js +26 -0
  230. package/src/interfaces/cli/routes/telegram.js +39 -0
  231. package/src/interfaces/cli/routes/update.js +24 -0
  232. package/src/interfaces/cli/routes/voice.js +17 -0
  233. package/src/interfaces/desktop/main.js +6 -7
  234. package/src/interfaces/desktop/renderer.js +0 -5
  235. package/src/interfaces/mcp-server/index.js +15 -14
  236. package/src/interfaces/tui/_shims/globals.d.ts +8 -0
  237. package/src/interfaces/tui/tsconfig.json +4 -1
  238. package/src/interfaces/web/README.md +6 -6
  239. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js +849 -0
  240. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js.map +1 -0
  241. package/src/interfaces/web/dist/assets/index-DzBBXFaO.css +1 -0
  242. package/src/interfaces/web/dist/index.html +2 -2
  243. package/src/interfaces/web/package-lock.json +11 -10
  244. package/src/interfaces/web/src/components/AddProjectDialog.tsx +1 -1
  245. package/src/interfaces/web/src/components/Section.tsx +18 -3
  246. package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +1 -1
  247. package/src/interfaces/web/src/components/chat/ChatList.tsx +2 -2
  248. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +13 -0
  249. package/src/interfaces/web/src/components/chat/SkillPicker.tsx +1 -1
  250. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +1 -1
  251. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +1 -1
  252. package/src/interfaces/web/src/components/cron/CronPicker.tsx +196 -0
  253. package/src/interfaces/web/src/components/desktop/DesktopStatusCard.tsx +1 -1
  254. package/src/interfaces/web/src/components/files/FileBrowser.tsx +2 -2
  255. package/src/interfaces/web/src/components/inbox/InboxList.tsx +145 -0
  256. package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +36 -6
  257. package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +1 -1
  258. package/src/interfaces/web/src/components/routines/RoutineDetail.tsx +16 -4
  259. package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +14 -4
  260. package/src/interfaces/web/src/components/routines/shared.ts +14 -5
  261. package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +1 -1
  262. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +1 -1
  263. package/src/interfaces/web/src/components/settings/NudgePanel.tsx +183 -0
  264. package/src/interfaces/web/src/components/settings/ProfilePanel.tsx +36 -12
  265. package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +1 -1
  266. package/src/interfaces/web/src/components/settings/SkillsManager.tsx +2 -2
  267. package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +1 -1
  268. package/src/interfaces/web/src/components/ui/filter-chips.tsx +47 -0
  269. package/src/interfaces/web/src/components/ui.tsx +1 -0
  270. package/src/interfaces/web/src/constants/index.ts +1 -0
  271. package/src/interfaces/web/src/hooks/useChat.ts +5 -1
  272. package/src/interfaces/web/src/hooks/useDaemonStatus.ts +1 -1
  273. package/src/interfaces/web/src/hooks/useDevices.ts +1 -1
  274. package/src/interfaces/web/src/hooks/useEngines.ts +1 -1
  275. package/src/interfaces/web/src/hooks/useGlobalConfig.ts +2 -2
  276. package/src/interfaces/web/src/hooks/useIdentity.ts +1 -1
  277. package/src/interfaces/web/src/hooks/useInbox.ts +1 -1
  278. package/src/interfaces/web/src/hooks/useNudges.ts +38 -0
  279. package/src/interfaces/web/src/hooks/useProfiles.ts +3 -3
  280. package/src/interfaces/web/src/hooks/useProjects.ts +1 -1
  281. package/src/interfaces/web/src/hooks/useTelegram.ts +3 -3
  282. package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +3 -3
  283. package/src/interfaces/web/src/i18n/en.ts +127 -0
  284. package/src/interfaces/web/src/i18n/es.ts +127 -0
  285. package/src/interfaces/web/src/lib/api/admin.ts +11 -11
  286. package/src/interfaces/web/src/lib/api/agents.ts +14 -14
  287. package/src/interfaces/web/src/lib/api/artifacts.ts +11 -11
  288. package/src/interfaces/web/src/lib/api/code.ts +1 -1
  289. package/src/interfaces/web/src/lib/api/commitments.ts +57 -0
  290. package/src/interfaces/web/src/lib/api/conversations.ts +8 -8
  291. package/src/interfaces/web/src/lib/api/deck.ts +3 -3
  292. package/src/interfaces/web/src/lib/api/desktop.ts +8 -8
  293. package/src/interfaces/web/src/lib/api/embeddings.ts +3 -3
  294. package/src/interfaces/web/src/lib/api/engines.ts +3 -3
  295. package/src/interfaces/web/src/lib/api/filesystem.ts +2 -2
  296. package/src/interfaces/web/src/lib/api/health.ts +1 -1
  297. package/src/interfaces/web/src/lib/api/identity.ts +2 -2
  298. package/src/interfaces/web/src/lib/api/inbox.ts +1 -1
  299. package/src/interfaces/web/src/lib/api/integrations.ts +8 -8
  300. package/src/interfaces/web/src/lib/api/mcps.ts +6 -6
  301. package/src/interfaces/web/src/lib/api/messages.ts +3 -3
  302. package/src/interfaces/web/src/lib/api/notebook.ts +23 -0
  303. package/src/interfaces/web/src/lib/api/nudges.ts +53 -0
  304. package/src/interfaces/web/src/lib/api/organization.ts +7 -7
  305. package/src/interfaces/web/src/lib/api/profiles.ts +8 -8
  306. package/src/interfaces/web/src/lib/api/projectFiles.ts +5 -5
  307. package/src/interfaces/web/src/lib/api/projects.ts +12 -12
  308. package/src/interfaces/web/src/lib/api/routines.ts +7 -7
  309. package/src/interfaces/web/src/lib/api/sessions.ts +2 -2
  310. package/src/interfaces/web/src/lib/api/skills.ts +11 -11
  311. package/src/interfaces/web/src/lib/api/super_agent.ts +3 -3
  312. package/src/interfaces/web/src/lib/api/tasks.ts +12 -12
  313. package/src/interfaces/web/src/lib/api/telegram.ts +14 -14
  314. package/src/interfaces/web/src/lib/api/tools.ts +1 -1
  315. package/src/interfaces/web/src/lib/api/vars.ts +4 -4
  316. package/src/interfaces/web/src/lib/api/voice.ts +6 -6
  317. package/src/interfaces/web/src/lib/cron.ts +196 -0
  318. package/src/interfaces/web/src/lib/when.ts +32 -0
  319. package/src/interfaces/web/src/screens/InboxScreen.tsx +107 -77
  320. package/src/interfaces/web/src/screens/ProjectScreen.tsx +5 -2
  321. package/src/interfaces/web/src/screens/SettingsScreen.tsx +17 -3
  322. package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +1 -1
  323. package/src/interfaces/web/src/screens/base/CommitmentsTab.tsx +239 -0
  324. package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +103 -20
  325. package/src/interfaces/web/src/screens/base/LogsTab.tsx +18 -3
  326. package/src/interfaces/web/src/screens/base/SessionsTab.tsx +1 -1
  327. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
  328. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +1 -1
  329. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -1
  330. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +1 -1
  331. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +8 -8
  332. package/src/interfaces/web/src/screens/project/AgentsTab.tsx +2 -2
  333. package/src/interfaces/web/src/screens/project/ChatTab.tsx +24 -6
  334. package/src/interfaces/web/src/screens/project/ConfigTab.tsx +1 -1
  335. package/src/interfaces/web/src/screens/project/McpsTab.tsx +3 -3
  336. package/src/interfaces/web/src/screens/project/Overview.tsx +6 -6
  337. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +15 -13
  338. package/src/interfaces/web/src/screens/project/StructureTab.tsx +1 -1
  339. package/src/interfaces/web/src/screens/project/TasksTab.tsx +1 -1
  340. package/src/interfaces/web/src/screens/project/VarsTab.tsx +1 -1
  341. package/src/interfaces/web/src/types/daemon.ts +10 -1
  342. package/src/interfaces/web/vite.config.ts +13 -18
  343. package/src/interfaces/web/dist/assets/index-BptlbKjU.js +0 -824
  344. package/src/interfaces/web/dist/assets/index-BptlbKjU.js.map +0 -1
  345. package/src/interfaces/web/dist/assets/index-D_EJEA1n.css +0 -1
  346. package/src/skills/apc-context/SKILL.md +0 -159
  347. /package/src/{host/daemon → core}/runtimes/_spawn.js +0 -0
  348. /package/src/{host/daemon → core}/runtimes/aider.js +0 -0
  349. /package/src/{host/daemon → core}/runtimes/antigravity.js +0 -0
  350. /package/src/{host/daemon → core}/runtimes/claude-code.js +0 -0
  351. /package/src/{host/daemon → core}/runtimes/codex.js +0 -0
  352. /package/src/{host/daemon → core}/runtimes/cursor-agent.js +0 -0
  353. /package/src/{host/daemon → core}/runtimes/gemini-cli.js +0 -0
  354. /package/src/{host/daemon → core}/runtimes/index.js +0 -0
  355. /package/src/{host/daemon → core}/runtimes/opencode.js +0 -0
  356. /package/src/{host/daemon → core}/runtimes/qwen-code.js +0 -0
@@ -51,13 +51,13 @@ export async function cmdArtifactCreate(args) {
51
51
  if (!name) throw new Error("apx artifact create: missing <name>");
52
52
  const pid = await resolveProjectId(args?.flags?.project);
53
53
  const content = args.flags.content && args.flags.content !== true ? String(args.flags.content) : "";
54
- const r = await http.post(`/projects/${pid}/artifacts`, { name, content });
54
+ const r = await http.post(`/api/projects/${pid}/artifacts`, { name, content });
55
55
  console.log(r.path);
56
56
  }
57
57
 
58
58
  export async function cmdArtifactList(args = {}) {
59
59
  const pid = await resolveProjectId(args?.flags?.project);
60
- const rows = await http.get(`/projects/${pid}/artifacts`);
60
+ const rows = await http.get(`/api/projects/${pid}/artifacts`);
61
61
  if (rows.length === 0) {
62
62
  console.log(`(no artifacts in project #${pid})`);
63
63
  return;
@@ -77,7 +77,7 @@ export async function cmdArtifactShow(args) {
77
77
  const name = args._[0];
78
78
  if (!name) throw new Error("apx artifact show: missing <name>");
79
79
  const pid = await resolveProjectId(args?.flags?.project);
80
- const r = await http.get(`/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
80
+ const r = await http.get(`/api/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
81
81
  process.stdout.write(r.content);
82
82
  }
83
83
 
@@ -85,7 +85,7 @@ export async function cmdArtifactRemove(args) {
85
85
  const name = args._[0];
86
86
  if (!name) throw new Error("apx artifact remove: missing <name>");
87
87
  const pid = await resolveProjectId(args?.flags?.project);
88
- await http.delete(`/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
88
+ await http.delete(`/api/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
89
89
  console.log(`removed artifact "${name}"`);
90
90
  }
91
91
 
@@ -106,7 +106,7 @@ export async function cmdArtifactRun(args) {
106
106
  // Pull the artifact record from the daemon for the absolute path.
107
107
  let entry;
108
108
  try {
109
- entry = await http.get(`/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
109
+ entry = await http.get(`/api/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
110
110
  } catch (e) {
111
111
  throw new Error(`artifact "${name}" not found in project #${pid}: ${e.message}`);
112
112
  }
@@ -170,7 +170,7 @@ export async function cmdArtifactPreview(args) {
170
170
  let view;
171
171
  try {
172
172
  view = await http.post(
173
- `/projects/${pid}/artifacts/${encodeURIComponent(name)}/preview`,
173
+ `/api/projects/${pid}/artifacts/${encodeURIComponent(name)}/preview`,
174
174
  { watch }
175
175
  );
176
176
  } catch (e) {
@@ -196,7 +196,7 @@ export async function cmdArtifactPreview(args) {
196
196
  async function sharePreview(previewId, { open: doOpen = false } = {}) {
197
197
  let tunnel;
198
198
  try {
199
- tunnel = await http.post(`/previews/${previewId}/tunnel`, {});
199
+ tunnel = await http.post(`/api/previews/${previewId}/tunnel`, {});
200
200
  } catch (e) {
201
201
  throw new Error(`could not create tunnel: ${e.message}`);
202
202
  }
@@ -217,7 +217,7 @@ export async function cmdArtifactShare(args) {
217
217
  const watch = args.flags["no-watch"] ? false : true;
218
218
 
219
219
  const view = await http.post(
220
- `/projects/${pid}/artifacts/${encodeURIComponent(name)}/preview`,
220
+ `/api/projects/${pid}/artifacts/${encodeURIComponent(name)}/preview`,
221
221
  { watch }
222
222
  );
223
223
  console.log(`sharing ${view.kind} artifact "${view.name}"`);
@@ -227,8 +227,8 @@ export async function cmdArtifactShare(args) {
227
227
  }
228
228
 
229
229
  // `apx artifact previews` — list running preview servers.
230
- export async function cmdArtifactPreviews(args = {}) {
231
- const rows = await http.get(`/previews`);
230
+ export async function cmdArtifactPreviews(_args = {}) {
231
+ const rows = await http.get(`/api/previews`);
232
232
  if (!rows.length) {
233
233
  console.log("(no running previews)");
234
234
  return;
@@ -247,13 +247,13 @@ export async function cmdArtifactPreviews(args = {}) {
247
247
  // `apx artifact stop <id> | --all` — stop preview server(s).
248
248
  export async function cmdArtifactStop(args) {
249
249
  if (args.flags.all) {
250
- const rows = await http.get(`/previews`);
251
- for (const r of rows) await http.delete(`/previews/${r.id}`);
250
+ const rows = await http.get(`/api/previews`);
251
+ for (const r of rows) await http.delete(`/api/previews/${r.id}`);
252
252
  console.log(`stopped ${rows.length} preview(s)`);
253
253
  return;
254
254
  }
255
255
  const id = args._[0];
256
256
  if (!id) throw new Error("apx artifact stop: missing <id> (or --all)");
257
- await http.delete(`/previews/${encodeURIComponent(id)}`);
257
+ await http.delete(`/api/previews/${encodeURIComponent(id)}`);
258
258
  console.log(`stopped preview ${id}`);
259
259
  }
@@ -42,7 +42,7 @@ export async function cmdChat(args) {
42
42
  if (convId) body.conversation_id = convId;
43
43
  if (overrideModel) body.model = overrideModel;
44
44
 
45
- const result = await http.post(`/projects/${pid}/agents/${slug}/chat`, body);
45
+ const result = await http.post(`/api/projects/${pid}/agents/${slug}/chat`, body);
46
46
  convId = result.conversation_id;
47
47
  process.stdout.write("\n" + result.text + "\n\n");
48
48
  } catch (e) {
@@ -61,7 +61,7 @@ export async function cmdConversationsList(args) {
61
61
  // No slug → the super-agent (default conversational agent).
62
62
  const slug = args._[0] || superAgentSlug();
63
63
  const pid = await resolveProjectId(args?.flags?.project);
64
- const rows = await http.get(`/projects/${pid}/agents/${slug}/conversations`);
64
+ const rows = await http.get(`/api/projects/${pid}/agents/${slug}/conversations`);
65
65
  if (rows.length === 0) {
66
66
  console.log(`(no conversations for ${slug})`);
67
67
  return;
@@ -85,7 +85,7 @@ export async function cmdConversationsGet(args) {
85
85
  else { slug = superAgentSlug(); id = args._[0]; }
86
86
  if (!id) throw new Error("apx conversations get: usage: apx conversations get [<agent>] <id>");
87
87
  const pid = await resolveProjectId(args?.flags?.project);
88
- const conv = await http.get(`/projects/${pid}/agents/${slug}/conversations/${id}`);
88
+ const conv = await http.get(`/api/projects/${pid}/agents/${slug}/conversations/${id}`);
89
89
  process.stdout.write(`# Conversation ${id} (${slug})\n`);
90
90
  for (const t of conv.turns) {
91
91
  process.stdout.write(`\n## ${t.role} — ${t.ts}\n${t.content}\n`);
@@ -18,7 +18,7 @@ async function resolveCommandRoot(args = {}) {
18
18
  const explicitProject = args?.flags?.project;
19
19
  if (explicitProject !== undefined && explicitProject !== null && explicitProject !== "") {
20
20
  const pid = await resolveProjectId(explicitProject);
21
- const projects = await http.get("/projects");
21
+ const projects = await http.get("/api/projects");
22
22
  const project = projects.find((p) => p.id === pid);
23
23
  if (!project) throw new Error(`project ${pid} not found`);
24
24
  return project.path;
@@ -28,7 +28,7 @@ async function resolveCommandRoot(args = {}) {
28
28
  if (root) return root;
29
29
 
30
30
  const pid = await resolveProjectId();
31
- const projects = await http.get("/projects");
31
+ const projects = await http.get("/api/projects");
32
32
  const project = projects.find((p) => p.id === pid);
33
33
  if (!project) throw new Error(`project ${pid} not found`);
34
34
  return project.path;
@@ -0,0 +1,154 @@
1
+ // apx commitment — what you promised, to whom, by when.
2
+ //
3
+ // apx commitment add "<what>" --to "<person>" [--due 2026-05-30] [--project X]
4
+ // [--channel telegram] [--ref <message id>]
5
+ // apx commitment list [--all | --project X] [--to X] [--state open|kept|missed|all]
6
+ // [--overdue] [--due-before ISO] [--limit N]
7
+ // apx commitment show <id> [--project X]
8
+ // apx commitment kept <id> [--project X] [--note "..."]
9
+ // apx commitment missed <id> [--project X] [--note "..."]
10
+ // apx commitment renegotiate <id> --due <ISO> [--project X] [--note "..."]
11
+ //
12
+ // A task is something to do; a commitment is something you promised a person.
13
+ // Kept separate on purpose — see core/stores/commitments.js.
14
+ import { http } from "../http.js";
15
+ import { resolveProjectId } from "./project.js";
16
+
17
+ export const COMMITMENT_USAGE = {
18
+ add: 'apx commitment add "<what>" --to "<person>" [--due ISO] [--project X] [--channel C] [--ref R]',
19
+ list: "apx commitment list [--all | --project X] [--to X] [--state open|kept|missed|all] [--overdue] [--due-before ISO] [--limit N]",
20
+ show: "apx commitment show <id> [--project X]",
21
+ kept: 'apx commitment kept <id> [--project X] [--note "..."]',
22
+ missed: 'apx commitment missed <id> [--project X] [--note "..."]',
23
+ renegotiate: 'apx commitment renegotiate <id> --due <ISO> [--project X] [--note "..."]',
24
+ };
25
+
26
+ function fail(sub, msg) {
27
+ console.error(`apx commitment ${sub}: ${msg}`);
28
+ console.error(`Usage: ${COMMITMENT_USAGE[sub]}`);
29
+ process.exit(1);
30
+ }
31
+
32
+ function shortDate(iso) {
33
+ if (!iso) return "";
34
+ return String(iso).replace(/T/, " ").replace(/Z$/, "").slice(0, 16);
35
+ }
36
+
37
+ /** Is this promise past its date and still open? */
38
+ function isOverdue(c) {
39
+ return c.state === "open" && c.due && c.due < new Date().toISOString();
40
+ }
41
+
42
+ function renderTable(rows, { showProject = false } = {}) {
43
+ if (!rows.length) {
44
+ console.log("(no commitments)");
45
+ return;
46
+ }
47
+ const idW = Math.max(...rows.map((r) => String(r.id).length), 2);
48
+ const whoW = Math.min(Math.max(...rows.map((r) => String(r.counterparty || "").length), 3), 20);
49
+ const projW = showProject
50
+ ? Math.min(Math.max(...rows.map((r) => String(r.project_name || "").length), 7), 18)
51
+ : 0;
52
+
53
+ for (const c of rows) {
54
+ const proj = showProject
55
+ ? String(c.project_name || "").slice(0, projW).padEnd(projW) + " "
56
+ : "";
57
+ // The flag carries the whole point of the type: a broken promise should be
58
+ // impossible to skim past.
59
+ const flag =
60
+ c.state === "kept" ? "✓" :
61
+ c.state === "missed" ? "✗" :
62
+ isOverdue(c) ? "!" : " ";
63
+ const moved = c.renegotiated_count ? ` (moved ×${c.renegotiated_count})` : "";
64
+ console.log(
65
+ `${flag} ${String(c.id).padEnd(idW)} ${proj}` +
66
+ `${String(c.counterparty || "").slice(0, whoW).padEnd(whoW)} ` +
67
+ `${(c.due ? shortDate(c.due).slice(0, 10) : "—").padEnd(10)} ` +
68
+ `${String(c.body || "").slice(0, 48)}${moved}`
69
+ );
70
+ }
71
+ const overdue = rows.filter(isOverdue).length;
72
+ if (overdue) console.log(`\n${overdue} past their date.`);
73
+ }
74
+
75
+ export async function cmdCommitmentAdd(args) {
76
+ const body = args?._?.[0];
77
+ const to = args?.flags?.to;
78
+ if (!body) fail("add", "what you promised is required");
79
+ if (!to) fail("add", "--to <person> is required — without a counterparty this is a task");
80
+
81
+ const pid = await resolveProjectId(args?.flags?.project);
82
+ const created = await http.post(`/api/projects/${pid}/commitments`, {
83
+ counterparty: to,
84
+ body,
85
+ due: args?.flags?.due || null,
86
+ origin_channel: args?.flags?.channel || "cli",
87
+ origin_message_ref: args?.flags?.ref || null,
88
+ });
89
+ console.log(`${created.id} → ${created.counterparty}${created.due ? ` by ${shortDate(created.due).slice(0, 10)}` : ""}`);
90
+ console.log(` ${created.body}`);
91
+ }
92
+
93
+ export async function cmdCommitmentList(args) {
94
+ const f = args?.flags || {};
95
+ const q = new URLSearchParams();
96
+ if (f.state) q.set("state", f.state);
97
+ if (f.to) q.set("counterparty", f.to);
98
+ if (f.overdue) q.set("overdue", "1");
99
+ if (f["due-before"]) q.set("due_before", f["due-before"]);
100
+ if (f.limit) q.set("limit", f.limit);
101
+
102
+ if (f.all) {
103
+ const { data } = await http.get(`/api/commitments?${q.toString()}`);
104
+ renderTable(data || [], { showProject: true });
105
+ return;
106
+ }
107
+ const pid = await resolveProjectId(args?.flags?.project);
108
+ const { data } = await http.get(`/api/projects/${pid}/commitments?${q.toString()}`);
109
+ renderTable(data || []);
110
+ }
111
+
112
+ export async function cmdCommitmentShow(args) {
113
+ const id = args?._?.[0];
114
+ if (!id) fail("show", "id required");
115
+ const pid = await resolveProjectId(args?.flags?.project);
116
+ const c = await http.get(`/api/projects/${pid}/commitments/${encodeURIComponent(id)}`);
117
+ console.log(`${c.id} [${c.state}${isOverdue(c) ? " · OVERDUE" : ""}]`);
118
+ console.log(` To: ${c.counterparty}`);
119
+ console.log(` What: ${c.body}`);
120
+ console.log(` Promised: ${shortDate(c.promised_at)}${c.origin_channel ? ` on ${c.origin_channel}` : ""}`);
121
+ console.log(` Due: ${c.due ? shortDate(c.due) : "(no date)"}`);
122
+ if (c.note) console.log(` Note: ${c.note}`);
123
+ // The history is the relationship record — print it, it is the reason the
124
+ // renegotiate event exists at all.
125
+ for (const h of c.history || []) {
126
+ console.log(` Moved: ${shortDate(h.due)?.slice(0, 10) || "?"} → (on ${shortDate(h.moved_at)})${h.note ? ` — ${h.note}` : ""}`);
127
+ }
128
+ }
129
+
130
+ async function close(args, sub) {
131
+ const id = args?._?.[0];
132
+ if (!id) fail(sub, "id required");
133
+ const pid = await resolveProjectId(args?.flags?.project);
134
+ const c = await http.post(`/api/projects/${pid}/commitments/${encodeURIComponent(id)}/${sub}`, {
135
+ note: args?.flags?.note || null,
136
+ });
137
+ console.log(`${c.id} → ${c.state}`);
138
+ }
139
+
140
+ export const cmdCommitmentKept = (args) => close(args, "kept");
141
+ export const cmdCommitmentMissed = (args) => close(args, "missed");
142
+
143
+ export async function cmdCommitmentRenegotiate(args) {
144
+ const id = args?._?.[0];
145
+ const due = args?.flags?.due;
146
+ if (!id) fail("renegotiate", "id required");
147
+ if (!due) fail("renegotiate", "--due <ISO> is required — a promise with no new date is a promise that vanished");
148
+ const pid = await resolveProjectId(args?.flags?.project);
149
+ const c = await http.post(`/api/projects/${pid}/commitments/${encodeURIComponent(id)}/renegotiate`, {
150
+ due,
151
+ note: args?.flags?.note || null,
152
+ });
153
+ console.log(`${c.id} → new date ${shortDate(c.due).slice(0, 10)} (moved ×${c.renegotiated_count})`);
154
+ }
@@ -1,7 +1,7 @@
1
1
  import { http } from "../http.js";
2
2
  import { resolveProjectId } from "./project.js";
3
3
  import { readConfig, writeConfig } from "#core/config/index.js";
4
- import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "#core/constants/permissions.js";
4
+ import { DEFAULT_PERMISSION_MODE } from "#core/constants/permissions.js";
5
5
 
6
6
  function parseValue(raw) {
7
7
  // best-effort: try JSON first (covers numbers, bools, objects, arrays, null,
@@ -15,7 +15,7 @@ function parseValue(raw) {
15
15
 
16
16
  export async function cmdConfigShow(args) {
17
17
  const pid = await resolveProjectId(args?.flags?.project);
18
- const data = await http.get(`/projects/${pid}/config`);
18
+ const data = await http.get(`/api/projects/${pid}/config`);
19
19
  if (args.flags.effective) {
20
20
  process.stdout.write(JSON.stringify(data.effective, null, 2) + "\n");
21
21
  return;
@@ -45,7 +45,7 @@ export async function cmdConfigSet(args) {
45
45
  }
46
46
  const pid = await resolveProjectId(args?.flags?.project);
47
47
  const value = parseValue(valueRaw);
48
- await http.patch(`/projects/${pid}/config`, { set: { [key]: value } });
48
+ await http.patch(`/api/projects/${pid}/config`, { set: { [key]: value } });
49
49
  console.log(`set ${key} = ${JSON.stringify(value)}`);
50
50
  }
51
51
 
@@ -53,7 +53,7 @@ export async function cmdConfigUnset(args) {
53
53
  const key = args._[0];
54
54
  if (!key) throw new Error("apx config unset: usage: apx config unset <key.path>");
55
55
  const pid = await resolveProjectId(args?.flags?.project);
56
- await http.patch(`/projects/${pid}/config`, { unset: [key] });
56
+ await http.patch(`/api/projects/${pid}/config`, { unset: [key] });
57
57
  console.log(`unset ${key}`);
58
58
  }
59
59
 
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
2
+ import { PID_PATH, LOG_PATH } from "#core/config/paths.js";
4
3
  import { ensureDaemon, http } from "../http.js";
4
+ import { installService, uninstallService, serviceStatus } from "#core/daemon/service.js";
5
5
 
6
6
  // Wait until nothing answers on the daemon port (old process fully exited and
7
7
  // released it), so a fresh start can bind it. Resolves true when down.
@@ -13,8 +13,8 @@ async function waitForPortReleased({ tries = 40, intervalMs = 150 } = {}) {
13
13
  return false;
14
14
  }
15
15
 
16
- const PID_PATH = path.join(os.homedir(), ".apx", "daemon.pid");
17
- const LOG_PATH = path.join(os.homedir(), ".apx", "daemon.log");
16
+
17
+
18
18
 
19
19
  // ── ANSI helpers ─────────────────────────────────────────────────────────────
20
20
  const c = {
@@ -56,8 +56,8 @@ function debugRaw(label, data) {
56
56
  export async function cmdDaemonStart(args = {}) {
57
57
  const debug = args.flags?.debug;
58
58
  await ensureDaemon();
59
- const status = await http.get("/health");
60
- if (debug) debugRaw("/health", status);
59
+ const status = await http.get("/api/health");
60
+ if (debug) debugRaw("/api/health", status);
61
61
  console.log(
62
62
  `\n ${fmt.green("●")} ${fmt.bold("apx daemon")} ${fmt.dim("v" + status.version)}` +
63
63
  ` ${fmt.gray("·")} ${fmt.cyan("port")} ${status.port ?? (process.env.APX_PORT || 7430)}` +
@@ -77,13 +77,13 @@ export async function cmdDaemonStatus(args = {}) {
77
77
  process.exit(1);
78
78
  }
79
79
 
80
- const h = await http.get("/health", { autoStart: false });
81
- const projects = await http.get("/projects", { autoStart: false });
80
+ const h = await http.get("/api/health", { autoStart: false });
81
+ const projects = await http.get("/api/projects", { autoStart: false });
82
82
  const pid = fs.existsSync(PID_PATH) ? fs.readFileSync(PID_PATH, "utf8").trim() : "?";
83
83
 
84
84
  if (debug) {
85
- debugRaw("/health", h);
86
- debugRaw("/projects", projects);
85
+ debugRaw("/api/health", h);
86
+ debugRaw("/api/projects", projects);
87
87
  }
88
88
 
89
89
  console.log(
@@ -113,8 +113,8 @@ export async function cmdDaemonReload(args = {}) {
113
113
  console.log(`\n ${fmt.yellow("○")} ${fmt.bold("apx daemon")} ${fmt.dim("not running — start first")}\n`);
114
114
  process.exit(1);
115
115
  }
116
- const res = await http.post("/admin/reload", undefined, { autoStart: false });
117
- if (debug) debugRaw("/admin/reload", res);
116
+ const res = await http.post("/api/admin/reload", undefined, { autoStart: false });
117
+ if (debug) debugRaw("/api/admin/reload", res);
118
118
  console.log(`\n ${fmt.green("↻")} ${fmt.bold("config reloaded")}`);
119
119
  if (res.super_agent_model) console.log(fmt.kv("model", fmt.cyan(res.super_agent_model)));
120
120
  if (res.fallback_order?.length) console.log(fmt.kv("fallback", fmt.cyan(res.fallback_order.join(" → "))));
@@ -129,8 +129,8 @@ export async function cmdDaemonRestart(args = {}) {
129
129
  // Ask the daemon to shut down cleanly; fall back to a PID kill if the
130
130
  // HTTP shutdown route is unreachable (same fallback as `stop`).
131
131
  try {
132
- const res = await http.post("/admin/shutdown", undefined, { autoStart: false });
133
- if (debug) debugRaw("/admin/shutdown", res);
132
+ const res = await http.post("/api/admin/shutdown", undefined, { autoStart: false });
133
+ if (debug) debugRaw("/api/admin/shutdown", res);
134
134
  } catch {
135
135
  if (fs.existsSync(PID_PATH)) {
136
136
  try {
@@ -154,8 +154,8 @@ export async function cmdDaemonRestart(args = {}) {
154
154
  }
155
155
 
156
156
  await ensureDaemon({ silent: true });
157
- const status = await http.get("/health", { autoStart: false });
158
- if (debug) debugRaw("/health", status);
157
+ const status = await http.get("/api/health", { autoStart: false });
158
+ if (debug) debugRaw("/api/health", status);
159
159
  console.log(
160
160
  `\n ${fmt.green("●")} ${fmt.bold("apx daemon")} ${fmt.dim("v" + status.version)}` +
161
161
  ` ${fmt.gray("·")} ${fmt.green(wasRunning ? "restarted" : "started")}` +
@@ -173,8 +173,8 @@ export async function cmdDaemonStop(args = {}) {
173
173
  }
174
174
 
175
175
  try {
176
- const res = await http.post("/admin/shutdown", undefined, { autoStart: false });
177
- if (debug) debugRaw("/admin/shutdown", res);
176
+ const res = await http.post("/api/admin/shutdown", undefined, { autoStart: false });
177
+ if (debug) debugRaw("/api/admin/shutdown", res);
178
178
  console.log(`\n ${fmt.red("○")} ${fmt.bold("apx daemon")} ${fmt.dim("stopped")}\n`);
179
179
  } catch {
180
180
  if (fs.existsSync(PID_PATH)) {
@@ -245,3 +245,59 @@ export async function cmdDaemonLogs(args) {
245
245
  return new Promise(() => {});
246
246
  }
247
247
  }
248
+
249
+ // ── Service (opt-in supervision) ─────────────────────────────────────────────
250
+
251
+ export async function cmdDaemonInstallService() {
252
+ const before = serviceStatus();
253
+ if (!before.supervised && before.platform !== "win32") {
254
+ console.error(`apx daemon install-service: ${before.note || "not supported here"}`);
255
+ process.exit(1);
256
+ }
257
+
258
+ const r = installService();
259
+ if (!r.ok) {
260
+ console.error(`apx daemon install-service: ${r.error}`);
261
+ process.exit(1);
262
+ }
263
+
264
+ console.log(`\n ${fmt.green("●")} ${fmt.bold("apx daemon")} is now a service`);
265
+ console.log(` ${fmt.gray("·")} ${fmt.cyan("unit")} ${r.path}`);
266
+ if (r.log) console.log(` ${fmt.gray("·")} ${fmt.cyan("log")} ${r.log}`);
267
+ console.log("");
268
+ if (r.supervised) {
269
+ console.log(" If it dies, it comes back. It survives a reboot.");
270
+ } else {
271
+ // Never let the headline promise more than the platform delivers.
272
+ console.log(` ${fmt.yellow("Note")}: ${r.note}`);
273
+ }
274
+ if (r.note && r.supervised) console.log(` ${fmt.yellow("Note")}: ${r.note}`);
275
+ console.log(`\n Undo any time: ${fmt.cyan("apx daemon uninstall-service")}\n`);
276
+ }
277
+
278
+ export async function cmdDaemonUninstallService() {
279
+ const r = uninstallService();
280
+ if (!r.ok) {
281
+ console.error(`apx daemon uninstall-service: ${r.error}`);
282
+ process.exit(1);
283
+ }
284
+ console.log(r.removed
285
+ ? `\n service removed — ${r.path}\n The daemon keeps working; it just is not supervised any more.\n`
286
+ : "\n no service was installed — nothing to remove\n");
287
+ }
288
+
289
+ export async function cmdDaemonServiceStatus() {
290
+ const s = serviceStatus();
291
+ if (!s.installed) {
292
+ console.log(`\n daemon service: ${fmt.dim("not installed")}`);
293
+ console.log(" The daemon starts when a command needs it, and nothing restarts it if it dies.");
294
+ console.log(` Install: ${fmt.cyan("apx daemon install-service")}\n`);
295
+ if (s.note) console.log(` ${fmt.yellow("Note")}: ${s.note}\n`);
296
+ return;
297
+ }
298
+ console.log(`\n daemon service: ${fmt.green("installed")} ${fmt.gray("·")} ${s.path}`);
299
+ console.log(s.supervised
300
+ ? " Supervised: it restarts on its own and survives a reboot."
301
+ : ` ${fmt.yellow("Not supervised")}: ${s.note}`);
302
+ console.log("");
303
+ }
@@ -1,8 +1,8 @@
1
1
  // apx desktop — launch/manage the floating voice desktop window (Electron).
2
2
  import fs from "node:fs";
3
- import os from "node:os";
4
3
  import path from "node:path";
5
- import { spawn, execFileSync } from "node:child_process";
4
+ import { DESKTOP_LOG_PATH, CONFIG_PATH } from "#core/config/paths.js";
5
+ import { spawn } from "node:child_process";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { http } from "../http.js";
8
8
  import {
@@ -11,18 +11,11 @@ import {
11
11
  autostartUninstall,
12
12
  getApxRunner as _getApxRunner,
13
13
  buildPlist as _buildPlist,
14
- MAC_PLIST_PATH,
15
- LINUX_DESKTOP_PATH,
16
- WIN_RUN_KEY,
17
- WIN_RUN_NAME,
18
14
  } from "#core/desktop/autostart.js";
19
15
  import {
20
16
  DESKTOP_MAIN,
21
- readPid, writePid, clearPid, pidAlive, isDesktopRunning,
22
- findElectron as _findElectron,
17
+ readPid, writePid, clearPid, pidAlive, findElectron as _findElectron,
23
18
  buildElectronSpawn as _buildElectronSpawn,
24
- startDesktopDetached,
25
- stopDesktop,
26
19
  } from "#core/desktop/process.js";
27
20
 
28
21
  // Re-exports — kept so existing tests (tests/desktop-autostart.test.js)
@@ -75,13 +68,13 @@ export async function cmdDesktopStart(args = {}) {
75
68
  // Get daemon port from running daemon or env
76
69
  let daemonPort = process.env.APX_PORT || "7430";
77
70
  try {
78
- const health = await http.get("/health").catch(() => null);
71
+ const health = await http.get("/api/health").catch(() => null);
79
72
  if (health?.port) daemonPort = String(health.port);
80
73
  } catch {}
81
74
 
82
75
  const { cmd, argv } = buildElectronSpawn(electronDescriptor, DESKTOP_MAIN, daemonPort);
83
76
 
84
- const logFile = path.join(os.homedir(), ".apx", "desktop.log");
77
+ const logFile = DESKTOP_LOG_PATH;
85
78
 
86
79
  if (debug) {
87
80
  // ── Debug mode: start desktop normally, then tail -f the log ─────────
@@ -191,7 +184,7 @@ export async function cmdDesktopStart(args = {}) {
191
184
  // Read configured shortcut (if any) for display
192
185
  let shortcutHint = "⌘G (macOS) / Ctrl+G (Win/Linux)";
193
186
  try {
194
- const cfg = JSON.parse(fs.readFileSync(path.join(os.homedir(), ".apx", "config.json"), "utf8"));
187
+ const cfg = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
195
188
  const sc = cfg?.desktop?.shortcut || cfg?.overlay?.shortcut;
196
189
  if (sc) shortcutHint = sc;
197
190
  } catch {}
@@ -254,7 +247,7 @@ export async function cmdDesktopStatus(_args = {}) {
254
247
 
255
248
  let daemonClients = 0;
256
249
  try {
257
- const s = await http.get("/desktop/status").catch(() => null);
250
+ const s = await http.get("/api/desktop/status").catch(() => null);
258
251
  daemonClients = s?.connected_clients ?? 0;
259
252
  } catch {}
260
253
 
@@ -171,7 +171,7 @@ export async function cmdExec(args) {
171
171
  let result;
172
172
  try {
173
173
  result = await http.streamPost(
174
- `/projects/${pid}/super-agent/chat/stream`,
174
+ `/api/projects/${pid}/super-agent/chat/stream`,
175
175
  { ...body, confirm: false },
176
176
  (event) => status.set(labelForEvent(event))
177
177
  );
@@ -187,7 +187,7 @@ export async function cmdExec(args) {
187
187
  return;
188
188
  }
189
189
 
190
- const result = await http.post(`/projects/${pid}/agents/${slug}/exec`, body);
190
+ const result = await http.post(`/api/projects/${pid}/agents/${slug}/exec`, body);
191
191
 
192
192
  process.stdout.write(result.text + "\n");
193
193
  if (process.stderr.isTTY || args.flags.verbose) {
@@ -10,11 +10,10 @@
10
10
  // here, regardless of which module produced it (telegram, whisper, super-agent…).
11
11
 
12
12
  import fs from "node:fs";
13
- import os from "node:os";
14
- import path from "node:path";
13
+ import { APX_LOG_PATH, ERROR_TRACE_PATH } from "#core/config/paths.js";
14
+
15
+
15
16
 
16
- const APX_LOG_PATH = path.join(os.homedir(), ".apx", "logs", "apx.log");
17
- const ERROR_TRACE_PATH = path.join(os.homedir(), ".apx", "logs", "errors.jsonl");
18
17
 
19
18
  const c = {
20
19
  reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
@@ -41,7 +41,7 @@ function sourceToScope(source) {
41
41
  export async function cmdMcpList(args = {}) {
42
42
  const scope = resolveListScope(args?.flags);
43
43
  const pid = await resolveProjectId(args?.flags?.project);
44
- const mcps = await http.get(`/projects/${pid}/mcps`);
44
+ const mcps = await http.get(`/api/projects/${pid}/mcps`);
45
45
  const filtered = scope === "all"
46
46
  ? mcps
47
47
  : mcps.filter((m) => sourceToScope(m.source) === scope);
@@ -90,7 +90,7 @@ export async function cmdMcpAdd(args) {
90
90
 
91
91
  const scope = resolveWriteScope(args.flags);
92
92
  const pid = await resolveProjectId(args?.flags?.project);
93
- const result = await http.post(`/projects/${pid}/mcps?scope=${encodeURIComponent(scope)}`, {
93
+ const result = await http.post(`/api/projects/${pid}/mcps?scope=${encodeURIComponent(scope)}`, {
94
94
  name,
95
95
  command,
96
96
  args: mcpArgs,
@@ -105,7 +105,7 @@ export async function cmdMcpRemove(args) {
105
105
  if (!name) throw new Error("apx mcp remove: missing <name>");
106
106
  const scope = resolveWriteScope(args.flags);
107
107
  const pid = await resolveProjectId(args?.flags?.project);
108
- await http.delete(`/projects/${pid}/mcps/${name}?scope=${encodeURIComponent(scope)}`);
108
+ await http.delete(`/api/projects/${pid}/mcps/${name}?scope=${encodeURIComponent(scope)}`);
109
109
  console.log(`Removed MCP "${name}" (scope: ${scope})`);
110
110
  }
111
111
 
@@ -120,7 +120,7 @@ async function toggleEnabled(args, enabled) {
120
120
  const name = args._[0];
121
121
  if (!name) throw new Error(`apx mcp ${enabled ? "enable" : "disable"}: missing <name>`);
122
122
  const pid = await resolveProjectId(args?.flags?.project);
123
- const all = await http.get(`/projects/${pid}/mcps`);
123
+ const all = await http.get(`/api/projects/${pid}/mcps`);
124
124
  const m = all.find((x) => x.name === name);
125
125
  if (!m) throw new Error(`MCP "${name}" not registered`);
126
126
  // Write back to the scope it lives in so we don't accidentally shadow it.
@@ -132,7 +132,7 @@ async function toggleEnabled(args, enabled) {
132
132
  `MCP "${name}" comes from foreign source "${m.source}" — toggle it in that IDE's config directly.`
133
133
  );
134
134
  }
135
- await http.post(`/projects/${pid}/mcps?scope=${encodeURIComponent(scope)}`, {
135
+ await http.post(`/api/projects/${pid}/mcps?scope=${encodeURIComponent(scope)}`, {
136
136
  name: m.name,
137
137
  command: m.command,
138
138
  args: m.args,
@@ -155,7 +155,7 @@ export async function cmdMcpRun(args) {
155
155
  }
156
156
  }
157
157
  const pid = await resolveProjectId(args?.flags?.project);
158
- const result = await http.post(`/projects/${pid}/mcps/${name}/call`, { tool, params });
158
+ const result = await http.post(`/api/projects/${pid}/mcps/${name}/call`, { tool, params });
159
159
  process.stdout.write(JSON.stringify(result.result, null, 2) + "\n");
160
160
  }
161
161
 
@@ -217,7 +217,7 @@ export async function cmdMcpTools(args) {
217
217
  if (!name) throw new Error("apx mcp tools: usage: apx mcp tools <name> [<tool>] [--json]");
218
218
  const toolFilter = args._[1];
219
219
  const pid = await resolveProjectId(args?.flags?.project);
220
- const data = await http.get(`/projects/${pid}/mcps/${name}/tools`);
220
+ const data = await http.get(`/api/projects/${pid}/mcps/${name}/tools`);
221
221
  const tools = data.tools || [];
222
222
 
223
223
  if (toolFilter) {
@@ -256,7 +256,7 @@ export async function cmdMcpLogs(args) {
256
256
  const name = args._[0];
257
257
  if (!name) throw new Error("apx mcp logs: missing <name>");
258
258
  const pid = await resolveProjectId(args?.flags?.project);
259
- const logs = await http.get(`/projects/${pid}/mcps/${name}/logs`);
259
+ const logs = await http.get(`/api/projects/${pid}/mcps/${name}/logs`);
260
260
  if (args?.flags?.json) {
261
261
  process.stdout.write(JSON.stringify(logs, null, 2) + "\n");
262
262
  return;
@@ -287,7 +287,7 @@ export async function cmdMcpLogs(args) {
287
287
 
288
288
  export async function cmdMcpCheck(args = {}) {
289
289
  const pid = await resolveProjectId(args?.flags?.project);
290
- const data = await http.get(`/projects/${pid}/mcps/check`);
290
+ const data = await http.get(`/api/projects/${pid}/mcps/check`);
291
291
 
292
292
  console.log("Source files:");
293
293
  for (const s of data.sources) {