quoxcli 0.2.0 → 0.4.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 (442) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -0
  3. package/bin/quox.js +99 -126
  4. package/lib/api/endpoints/actions.js +15 -0
  5. package/lib/api/endpoints/admin.js +46 -0
  6. package/lib/api/endpoints/agent-schedules.js +26 -0
  7. package/lib/api/endpoints/agents.js +7 -0
  8. package/lib/api/endpoints/assistants.js +12 -0
  9. package/lib/api/endpoints/away.js +15 -0
  10. package/lib/api/endpoints/blog.js +16 -0
  11. package/lib/api/endpoints/chat.js +14 -0
  12. package/lib/api/endpoints/contactsq.js +6 -0
  13. package/lib/api/endpoints/conversations.js +14 -0
  14. package/lib/api/endpoints/devServerRegistry.js +25 -0
  15. package/lib/api/endpoints/engagement.js +26 -0
  16. package/lib/api/endpoints/evidence.js +20 -0
  17. package/lib/api/endpoints/executions.js +13 -0
  18. package/lib/api/endpoints/fetchAllowlist.js +27 -0
  19. package/lib/api/endpoints/files.js +14 -0
  20. package/lib/api/endpoints/hat.js +44 -0
  21. package/lib/api/endpoints/inbox.js +20 -0
  22. package/lib/api/endpoints/index.js +26 -0
  23. package/lib/api/endpoints/loops.js +26 -0
  24. package/lib/api/endpoints/memory.js +19 -0
  25. package/lib/api/endpoints/mind.js +13 -0
  26. package/lib/api/endpoints/mon.js +179 -0
  27. package/lib/api/endpoints/objectives.js +12 -0
  28. package/lib/api/endpoints/plugins.js +18 -0
  29. package/lib/api/endpoints/presence.js +33 -0
  30. package/lib/api/endpoints/quoxplan.js +7 -0
  31. package/lib/api/endpoints/quoxseo.js +12 -0
  32. package/lib/api/endpoints/router.js +17 -0
  33. package/lib/api/endpoints/safety.js +7 -0
  34. package/lib/api/endpoints/server.js +21 -0
  35. package/lib/api/endpoints/setup.js +6 -0
  36. package/lib/api/endpoints/skills.js +20 -0
  37. package/lib/api/endpoints/stepup.js +20 -0
  38. package/lib/api/endpoints/teams.js +2 -2
  39. package/lib/api/endpoints/triggers.js +26 -0
  40. package/lib/api/endpoints/v1.js +120 -0
  41. package/lib/api/endpoints/ward.js +7 -0
  42. package/lib/api/endpoints/workflows.js +7 -0
  43. package/lib/api/endpoints/x.js +8 -0
  44. package/lib/commands/actions.js +131 -0
  45. package/lib/commands/admin.js +16 -0
  46. package/lib/commands/agent.js +170 -27
  47. package/lib/commands/agentAttach.js +140 -13
  48. package/lib/commands/approvals.js +78 -1
  49. package/lib/commands/aside.js +24 -0
  50. package/lib/commands/ask.js +146 -0
  51. package/lib/commands/assistant.js +59 -4
  52. package/lib/commands/auth.js +8 -2
  53. package/lib/commands/backup.js +8 -4
  54. package/lib/commands/blog.js +110 -0
  55. package/lib/commands/brain.js +105 -14
  56. package/lib/commands/build.js +19 -5
  57. package/lib/commands/chat.js +180 -26
  58. package/lib/commands/chatCompanion.js +27 -0
  59. package/lib/commands/chatState.js +45 -0
  60. package/lib/commands/claude.js +35 -38
  61. package/lib/commands/contacts.js +134 -4
  62. package/lib/commands/contactsSendAt.js +21 -0
  63. package/lib/commands/conversation.js +35 -4
  64. package/lib/commands/dashboard.js +43 -0
  65. package/lib/commands/department.js +28 -7
  66. package/lib/commands/devResident.js +15 -3
  67. package/lib/commands/devSupervisor.js +13 -1
  68. package/lib/commands/discord.js +75 -39
  69. package/lib/commands/done.js +98 -28
  70. package/lib/commands/engagement.js +107 -6
  71. package/lib/commands/evidence.js +154 -0
  72. package/lib/commands/ext.js +23 -0
  73. package/lib/commands/fetchAllowlist.js +71 -0
  74. package/lib/commands/file.js +38 -1
  75. package/lib/commands/fleet.js +336 -4
  76. package/lib/commands/governance.js +12 -3
  77. package/lib/commands/gripe.js +44 -13
  78. package/lib/commands/hat.js +254 -9
  79. package/lib/commands/helpers.js +18 -1
  80. package/lib/commands/hyperion.js +58 -0
  81. package/lib/commands/image.js +68 -0
  82. package/lib/commands/inbox.js +74 -23
  83. package/lib/commands/kuma.js +1050 -0
  84. package/lib/commands/kumaSla.js +174 -0
  85. package/lib/commands/licence.js +52 -155
  86. package/lib/commands/loop.js +291 -0
  87. package/lib/commands/lucid.js +10 -4
  88. package/lib/commands/memory.js +85 -8
  89. package/lib/commands/mind.js +71 -0
  90. package/lib/commands/mon.js +69 -2
  91. package/lib/commands/monitoring.js +1 -1
  92. package/lib/commands/motion.js +2 -2
  93. package/lib/commands/nucleus.js +48 -17
  94. package/lib/commands/objective.js +59 -1
  95. package/lib/commands/offload.js +89 -0
  96. package/lib/commands/onboard.js +2 -1
  97. package/lib/commands/org.js +30 -3
  98. package/lib/commands/peek.js +28 -0
  99. package/lib/commands/plan.js +38 -0
  100. package/lib/commands/plugin.js +110 -0
  101. package/lib/commands/presence.js +89 -0
  102. package/lib/commands/provider.js +217 -0
  103. package/lib/commands/qlarity.js +19 -3
  104. package/lib/commands/quoxchat.js +13 -4
  105. package/lib/commands/quoxseo.js +69 -0
  106. package/lib/commands/reach.js +2 -1
  107. package/lib/commands/research.js +79 -0
  108. package/lib/commands/router.js +77 -0
  109. package/lib/commands/rows.js +367 -0
  110. package/lib/commands/run.js +82 -10
  111. package/lib/commands/scope.js +2 -2
  112. package/lib/commands/server.js +405 -0
  113. package/lib/commands/setup.js +33 -7
  114. package/lib/commands/shortcut.js +41 -0
  115. package/lib/commands/skills.js +68 -9
  116. package/lib/commands/slot.js +260 -7
  117. package/lib/commands/status.js +1 -1
  118. package/lib/commands/team.js +3 -1
  119. package/lib/commands/tips.js +23 -0
  120. package/lib/commands/tool.js +133 -45
  121. package/lib/commands/trace.js +12 -7
  122. package/lib/commands/trigger.js +95 -0
  123. package/lib/commands/update.js +46 -0
  124. package/lib/commands/vault.js +31 -8
  125. package/lib/commands/vm.js +237 -0
  126. package/lib/commands/voice.js +34 -3
  127. package/lib/commands/volt.js +45 -3
  128. package/lib/commands/walkthrough.js +32 -0
  129. package/lib/commands/watch.js +73 -21
  130. package/lib/commands/welcome.js +14 -0
  131. package/lib/commands/workflow.js +189 -32
  132. package/lib/commands/x.js +13 -4
  133. package/lib/commands/youtube.js +172 -0
  134. package/lib/core/actingAs.js +2 -2
  135. package/lib/core/actionItems.js +115 -0
  136. package/lib/core/activeSummary.js +186 -0
  137. package/lib/core/agentAlias.js +2 -0
  138. package/lib/core/agentFamilies.js +7 -0
  139. package/lib/core/aside.js +54 -0
  140. package/lib/core/askDecision.js +60 -0
  141. package/lib/core/auth.js +164 -13
  142. package/lib/core/callerSource.js +1 -1
  143. package/lib/core/cliBuild.js +62 -0
  144. package/lib/core/client.js +152 -35
  145. package/lib/core/commandRegistrars.js +130 -0
  146. package/lib/core/config.js +143 -25
  147. package/lib/core/customHats.js +168 -0
  148. package/lib/core/doneEngineFetch.js +8 -0
  149. package/lib/core/errors.js +329 -20
  150. package/lib/core/findingReconcile.js +90 -0
  151. package/lib/core/global-flags.js +105 -5
  152. package/lib/core/governedSession.js +1 -3
  153. package/lib/core/hyperion.js +102 -0
  154. package/lib/core/inboxResolution.js +65 -0
  155. package/lib/core/lazyCommands.js +4 -1
  156. package/lib/core/liveSessionsReport.js +1 -1
  157. package/lib/core/loopGlyph.js +97 -0
  158. package/lib/core/offload.js +533 -0
  159. package/lib/core/org-override.js +1 -1
  160. package/lib/core/orgResolve.js +70 -0
  161. package/lib/core/output.js +11 -11
  162. package/lib/core/peek.js +126 -0
  163. package/lib/core/profileUsers.js +51 -0
  164. package/lib/core/promptCancel.js +18 -0
  165. package/lib/core/repoPath.js +11 -0
  166. package/lib/core/residentSeed.js +3 -1
  167. package/lib/core/rootOptions.js +39 -0
  168. package/lib/core/router.js +168 -0
  169. package/lib/core/selfUpdate.js +188 -0
  170. package/lib/core/sessionHeartbeat.js +12 -8
  171. package/lib/core/sessionsOverview.js +64 -0
  172. package/lib/core/shortcut.js +83 -0
  173. package/lib/core/slotKeeper.js +248 -3
  174. package/lib/core/slotWake.js +120 -0
  175. package/lib/core/supervisorEngine.js +71 -2
  176. package/lib/core/version.js +1 -1
  177. package/lib/core/vm.js +366 -0
  178. package/lib/core/vmTui.js +86 -0
  179. package/lib/core/walkthrough.js +35 -0
  180. package/lib/dragon/companionView.js +34 -0
  181. package/lib/dragon/frames.js +118 -0
  182. package/lib/dragon/jobs.js +123 -0
  183. package/lib/dragon/renderWorker.js +15 -0
  184. package/lib/dragon/select.js +72 -0
  185. package/lib/evidence/bundleExport.js +47 -0
  186. package/lib/evidence/bundleVerify.js +77 -0
  187. package/lib/hyperion/deck.js +29 -0
  188. package/lib/lucid/LucidCard.jsx +68 -0
  189. package/lib/lucid/LucidIntro.jsx +45 -58
  190. package/lib/lucid/cards.js +86 -0
  191. package/lib/lucid/markdown.js +81 -0
  192. package/lib/lucid/motion.js +56 -0
  193. package/lib/quoxon/EXTENSIONS.md +23 -0
  194. package/lib/quoxon/activity.js +14 -2
  195. package/lib/quoxon/aocl.js +117 -0
  196. package/lib/quoxon/api.js +690 -25
  197. package/lib/quoxon/app.js +3586 -250
  198. package/lib/quoxon/approvalFacts.js +96 -0
  199. package/lib/quoxon/approvalSentence.js +44 -0
  200. package/lib/quoxon/approvalsView.js +384 -0
  201. package/lib/quoxon/asideView.js +40 -0
  202. package/lib/quoxon/ask.js +121 -0
  203. package/lib/quoxon/away.js +52 -0
  204. package/lib/quoxon/bang.js +224 -0
  205. package/lib/quoxon/bulkRisk.js +17 -0
  206. package/lib/quoxon/capabilities.js +13 -6
  207. package/lib/quoxon/cells.js +53 -0
  208. package/lib/quoxon/commandShort.js +124 -0
  209. package/lib/quoxon/commands.js +429 -39
  210. package/lib/quoxon/connect.js +210 -23
  211. package/lib/quoxon/connectCard.js +19 -2
  212. package/lib/quoxon/consent.js +86 -0
  213. package/lib/quoxon/copy.js +4 -3
  214. package/lib/quoxon/countdownLine.js +20 -0
  215. package/lib/quoxon/deck.js +30 -0
  216. package/lib/quoxon/demo.autoplay.js +29 -0
  217. package/lib/quoxon/demo.fixtures.js +1 -1
  218. package/lib/quoxon/demo.js +46 -3
  219. package/lib/quoxon/devObserver.js +20 -0
  220. package/lib/quoxon/dividerAocl.js +192 -0
  221. package/lib/quoxon/dividerRule.js +57 -0
  222. package/lib/quoxon/dragonCompanion.js +255 -0
  223. package/lib/quoxon/dragonModes.js +151 -0
  224. package/lib/quoxon/dragonMoments.js +82 -0
  225. package/lib/quoxon/dragonReads.js +68 -0
  226. package/lib/quoxon/dragonView.js +41 -0
  227. package/lib/quoxon/earlyInput.js +40 -0
  228. package/lib/quoxon/echo.js +15 -0
  229. package/lib/quoxon/egg.js +2 -2
  230. package/lib/quoxon/events.js +266 -26
  231. package/lib/quoxon/extensions.js +276 -0
  232. package/lib/quoxon/feedback.js +50 -0
  233. package/lib/quoxon/gallery.js +21 -7
  234. package/lib/quoxon/handoffOffer.js +78 -0
  235. package/lib/quoxon/hatSwitch.js +37 -0
  236. package/lib/quoxon/headless.js +161 -0
  237. package/lib/quoxon/hostUrls.js +41 -0
  238. package/lib/quoxon/inboxView.js +79 -0
  239. package/lib/quoxon/indicator.js +6 -2
  240. package/lib/quoxon/installCli.js +79 -0
  241. package/lib/quoxon/intent.js +12 -1
  242. package/lib/quoxon/keys.js +51 -8
  243. package/lib/quoxon/launcher.js +103 -0
  244. package/lib/quoxon/loopCard.js +456 -0
  245. package/lib/quoxon/loopCards.js +180 -0
  246. package/lib/quoxon/loopStream.js +87 -0
  247. package/lib/quoxon/main.js +101 -160
  248. package/lib/quoxon/markdown.js +45 -21
  249. package/lib/quoxon/markdownSettle.js +19 -0
  250. package/lib/quoxon/matrixDomains.json +46 -0
  251. package/lib/quoxon/mentions.js +90 -0
  252. package/lib/quoxon/mind.js +103 -0
  253. package/lib/quoxon/mode.js +46 -0
  254. package/lib/quoxon/model.js +132 -26
  255. package/lib/quoxon/modelPrefs.js +345 -0
  256. package/lib/quoxon/motion/clock.js +3 -2
  257. package/lib/quoxon/motion/level.js +91 -0
  258. package/lib/quoxon/mouse.js +222 -0
  259. package/lib/quoxon/noai.js +57 -0
  260. package/lib/quoxon/objectiveOffer.js +82 -0
  261. package/lib/quoxon/objects/away.js +72 -0
  262. package/lib/quoxon/objects/fetch.js +17 -5
  263. package/lib/quoxon/objects/fleet.js +149 -0
  264. package/lib/quoxon/objects/health.js +186 -0
  265. package/lib/quoxon/objects/index.js +46 -7
  266. package/lib/quoxon/objects/mirror.js +137 -0
  267. package/lib/quoxon/objects/model.js +33 -0
  268. package/lib/quoxon/objects/peek.js +17 -0
  269. package/lib/quoxon/objects/proof.js +12 -4
  270. package/lib/quoxon/objects/prove.js +130 -0
  271. package/lib/quoxon/objects/replay.js +125 -0
  272. package/lib/quoxon/objects/sitrep.js +58 -0
  273. package/lib/quoxon/objects/view.js +11 -0
  274. package/lib/quoxon/onboarding/copy.js +32 -4
  275. package/lib/quoxon/onboarding/credential.js +17 -4
  276. package/lib/quoxon/onboarding/dockerOffer.js +45 -0
  277. package/lib/quoxon/onboarding/failureCause.js +33 -0
  278. package/lib/quoxon/onboarding/finish.js +15 -0
  279. package/lib/quoxon/onboarding/index.js +34 -7
  280. package/lib/quoxon/onboarding/installRunner.js +64 -5
  281. package/lib/quoxon/onboarding/preflight.js +17 -3
  282. package/lib/quoxon/onboarding/probe.js +65 -0
  283. package/lib/quoxon/onboarding/shortcutOffer.js +28 -0
  284. package/lib/quoxon/onboarding/view.js +76 -19
  285. package/lib/quoxon/orgLabel.js +10 -0
  286. package/lib/quoxon/orgSwitch.js +59 -0
  287. package/lib/quoxon/ownBroadcast.js +22 -0
  288. package/lib/quoxon/palette.js +43 -0
  289. package/lib/quoxon/planSteps.js +51 -0
  290. package/lib/quoxon/print.js +12 -5
  291. package/lib/quoxon/profileSwitch.js +53 -0
  292. package/lib/quoxon/promptHistory.js +41 -0
  293. package/lib/quoxon/providers.js +48 -0
  294. package/lib/quoxon/qart.js +20 -51
  295. package/lib/quoxon/qdragon.js +410 -0
  296. package/lib/quoxon/qdragonFly.js +62 -0
  297. package/lib/quoxon/qdragonScenes.js +282 -0
  298. package/lib/quoxon/qeyes.js +213 -0
  299. package/lib/quoxon/qmotion.js +75 -37
  300. package/lib/quoxon/render.js +740 -124
  301. package/lib/quoxon/resend.js +72 -0
  302. package/lib/quoxon/roster.js +117 -17
  303. package/lib/quoxon/route.js +6 -5
  304. package/lib/quoxon/rows.js +954 -0
  305. package/lib/quoxon/rowsStore.js +80 -0
  306. package/lib/quoxon/screenContext.js +150 -0
  307. package/lib/quoxon/screenFrames.js +254 -0
  308. package/lib/quoxon/serverChip.js +42 -0
  309. package/lib/quoxon/session.js +182 -2
  310. package/lib/quoxon/sessionDead.js +17 -0
  311. package/lib/quoxon/signin.js +125 -0
  312. package/lib/quoxon/slash/agents.js +112 -0
  313. package/lib/quoxon/slash/argSources.js +65 -0
  314. package/lib/quoxon/slash/args.js +65 -0
  315. package/lib/quoxon/slash/automate.js +89 -0
  316. package/lib/quoxon/slash/broadcast.js +140 -0
  317. package/lib/quoxon/slash/broadcastRun.js +108 -0
  318. package/lib/quoxon/slash/card.js +30 -0
  319. package/lib/quoxon/slash/challenge.js +114 -0
  320. package/lib/quoxon/slash/cliCommand.js +94 -0
  321. package/lib/quoxon/slash/cliLeaves.json +1023 -0
  322. package/lib/quoxon/slash/compact.js +1 -1
  323. package/lib/quoxon/slash/confirm.js +30 -0
  324. package/lib/quoxon/slash/crystallize.js +69 -0
  325. package/lib/quoxon/slash/dashboard.js +136 -0
  326. package/lib/quoxon/slash/draft.js +54 -0
  327. package/lib/quoxon/slash/evidenceCmd.js +34 -0
  328. package/lib/quoxon/slash/hatWalk.js +206 -0
  329. package/lib/quoxon/slash/inverses.js +77 -0
  330. package/lib/quoxon/slash/killer.js +24 -0
  331. package/lib/quoxon/slash/killerApi.js +16 -0
  332. package/lib/quoxon/slash/ledger.js +13 -8
  333. package/lib/quoxon/slash/listView.js +3 -1
  334. package/lib/quoxon/slash/modelCmd.js +261 -0
  335. package/lib/quoxon/slash/palette.js +22 -8
  336. package/lib/quoxon/slash/paletteController.js +117 -7
  337. package/lib/quoxon/slash/panels.js +21 -6
  338. package/lib/quoxon/slash/picker.js +20 -2
  339. package/lib/quoxon/slash/qpeCmd.js +35 -0
  340. package/lib/quoxon/slash/restart.js +95 -13
  341. package/lib/quoxon/slash/resume.js +18 -1
  342. package/lib/quoxon/slash/rowCmd.js +157 -0
  343. package/lib/quoxon/slash/rowWalk.js +175 -0
  344. package/lib/quoxon/slash/settings.js +16 -6
  345. package/lib/quoxon/slash/spawn.js +177 -0
  346. package/lib/quoxon/slash/stepUp.js +274 -0
  347. package/lib/quoxon/slash/stepUpCard.js +60 -0
  348. package/lib/quoxon/slash/stepUpCmds.js +78 -0
  349. package/lib/quoxon/slash/todoCmd.js +122 -0
  350. package/lib/quoxon/slash/undoApi.js +38 -0
  351. package/lib/quoxon/slash/undoCmd.js +49 -0
  352. package/lib/quoxon/slash/undoWish.js +15 -0
  353. package/lib/quoxon/slash/watch.js +164 -0
  354. package/lib/quoxon/slash/wish.js +23 -0
  355. package/lib/quoxon/slash/wishCmd.js +32 -0
  356. package/lib/quoxon/slotChat.js +300 -0
  357. package/lib/quoxon/slotFormat.js +195 -0
  358. package/lib/quoxon/slotRun.js +131 -0
  359. package/lib/quoxon/sound/mapping.js +2 -1
  360. package/lib/quoxon/stray.js +22 -0
  361. package/lib/quoxon/suggestCommand.js +90 -0
  362. package/lib/quoxon/term.js +46 -11
  363. package/lib/quoxon/textBuffer.js +176 -2
  364. package/lib/quoxon/theme.js +58 -25
  365. package/lib/quoxon/tips.js +433 -0
  366. package/lib/quoxon/toolCards.js +30 -0
  367. package/lib/quoxon/toolError.js +41 -0
  368. package/lib/quoxon/transcript.js +42 -4
  369. package/lib/quoxon/web/qface.js +18 -10
  370. package/lib/quoxon/welcome.js +1 -1
  371. package/lib/quoxon/whatsnew.js +136 -0
  372. package/lib/quoxon/whatsnew.jsonl +134 -0
  373. package/lib/quoxon/windowOrg.js +123 -0
  374. package/lib/server/devServers.js +137 -0
  375. package/lib/server/diag.js +129 -0
  376. package/lib/server/doctor.js +71 -0
  377. package/lib/server/install.js +149 -0
  378. package/lib/server/pluginEnable.js +161 -0
  379. package/lib/server/skew.js +56 -0
  380. package/lib/server/snapshot.js +131 -0
  381. package/lib/server/status.js +128 -0
  382. package/lib/server/sys.js +65 -0
  383. package/lib/server/uninstall.js +63 -0
  384. package/lib/server/update.js +234 -0
  385. package/lib/toolOutputShape.js +315 -0
  386. package/lib/tui/views/HyperionDeck.jsx +79 -0
  387. package/lib/tui/views/LucidDeck.jsx +209 -217
  388. package/package.json +5 -1
  389. package/vendor/dragonlab/SOURCE +4 -0
  390. package/vendor/dragonlab/design/cipher/v1/cipher-master.svg +46 -0
  391. package/vendor/dragonlab/design/cipher/v2/character.mjs +49 -0
  392. package/vendor/dragonlab/design/cipher/v2/review.mjs +33 -0
  393. package/vendor/dragonlab/design/cipher/v2/watch.mjs +56 -0
  394. package/vendor/dragonlab/design/drago/v1/character.mjs +19 -0
  395. package/vendor/dragonlab/design/drago/v1/drago-master.svg +56 -0
  396. package/vendor/dragonlab/design/drago/v2/character.mjs +87 -0
  397. package/vendor/dragonlab/design/drago/v4/character.mjs +67 -0
  398. package/vendor/dragonlab/design/drago/v4/profile.mjs +35 -0
  399. package/vendor/dragonlab/design/drago/v4/review.mjs +33 -0
  400. package/vendor/dragonlab/design/drago/v4/watch.mjs +56 -0
  401. package/vendor/dragonlab/design/hyperion/v1/hyperion-master.svg +56 -0
  402. package/vendor/dragonlab/design/hyperion/v2/character.mjs +79 -0
  403. package/vendor/dragonlab/design/hyperion/v2/review.mjs +33 -0
  404. package/vendor/dragonlab/design/hyperion/v2/watch.mjs +56 -0
  405. package/vendor/dragonlab/design/lucid/recovery/arrival.mjs +22 -0
  406. package/vendor/dragonlab/design/lucid/recovery/character.mjs +106 -0
  407. package/vendor/dragonlab/design/lucid/recovery/layout.mjs +50 -0
  408. package/vendor/dragonlab/design/lucid/recovery/lucid-master.svg +78 -0
  409. package/vendor/dragonlab/design/lucid/recovery/motion.mjs +42 -0
  410. package/vendor/dragonlab/design/lucid/recovery/render.mjs +121 -0
  411. package/vendor/dragonlab/design/lucid/recovery/review.mjs +68 -0
  412. package/vendor/dragonlab/design/lucid/recovery/verify.mjs +84 -0
  413. package/vendor/dragonlab/design/lucid/recovery/watch.mjs +56 -0
  414. package/vendor/dragonlab/design/seer/v1/seer-master.svg +53 -0
  415. package/vendor/dragonlab/design/seer/v2/character.mjs +88 -0
  416. package/vendor/dragonlab/design/seer/v3/character.mjs +33 -0
  417. package/vendor/dragonlab/design/seer/v4/character.mjs +66 -0
  418. package/vendor/dragonlab/design/seer/v4/review.mjs +33 -0
  419. package/vendor/dragonlab/design/seer/v4/watch.mjs +56 -0
  420. package/vendor/dragonlab/design/stage3/companion-review.mjs +28 -0
  421. package/vendor/dragonlab/design/stage3/sentinel-art.js +39 -0
  422. package/vendor/dragonlab/design/stage3/sentinel-braille.js +216 -0
  423. package/vendor/dragonlab/design/stage3/sentinel-character.js +257 -0
  424. package/vendor/dragonlab/design/stage3/sentinel-icon.js +103 -0
  425. package/vendor/dragonlab/design/stage3/sentinel-warden-eyes.js +95 -0
  426. package/vendor/dragonlab/design/stage3/svg-coverage.js +148 -0
  427. package/vendor/dragonlab/factory/svg-rig.mjs +63 -0
  428. package/vendor/dragonlab/integration/companions-v1/characters.mjs +88 -0
  429. package/vendor/dragonlab/integration/companions-v1/cipher.mjs +2 -0
  430. package/vendor/dragonlab/integration/companions-v1/companion.mjs +63 -0
  431. package/vendor/dragonlab/integration/companions-v1/drago.mjs +2 -0
  432. package/vendor/dragonlab/integration/companions-v1/hyperion.mjs +2 -0
  433. package/vendor/dragonlab/integration/companions-v1/lucid.mjs +2 -0
  434. package/vendor/dragonlab/integration/companions-v1/seer.mjs +2 -0
  435. package/vendor/dragonlab/integration/companions-v1/sentinel.mjs +2 -0
  436. package/vendor/dragonlab/integration/companions-v1/view.mjs +28 -0
  437. package/vendor/dragonlab/integration/hyperion-v3/activity.mjs +76 -0
  438. package/vendor/dragonlab/lib/power-effects.js +294 -0
  439. package/vendor/dragonlab/lib/power-registry.js +74 -0
  440. package/vendor/dragonlab/vendor/quox-dragon/qart.js +348 -0
  441. package/vendor/dragonlab/vendor/quox-dragon/qeyes.js +213 -0
  442. package/lib/core/websiteSession.js +0 -148
@@ -2,10 +2,30 @@
2
2
  * Tool commands: CRUD, catalog, install, publish
3
3
  */
4
4
 
5
+ import fs from 'node:fs';
5
6
  import { missingReasonWarning } from '../core/callerSource.js';
6
7
  import { formatOutput, formatDetail, printSuccess } from '../core/output.js';
7
8
  import { resolveOrgId, parseJSON } from './helpers.js';
8
9
  import { resolveEndpoint } from '../api/endpoints.js';
10
+ import { capInline, checkShape, shapeOutput, shapeRecord } from '../toolOutputShape.js';
11
+
12
+ /**
13
+ * QUOXON-LATENCY L2: the per-turn credential. QUOX_AUTH_TOKEN (cold spawn) or, on a warm
14
+ * `claude` spare, QUOX_AUTH_TOKEN_FILE: a file the collector writes for one turn and
15
+ * deletes when it ends. When the file variable is set it is the ONLY credential: a
16
+ * missing or empty file fails closed, never falling back to a stored session or the
17
+ * service key.
18
+ */
19
+ function readTurnToken(env) {
20
+ if (env.QUOX_AUTH_TOKEN) return env.QUOX_AUTH_TOKEN;
21
+ if (!env.QUOX_AUTH_TOKEN_FILE) return '';
22
+ let token = '';
23
+ try { token = fs.readFileSync(env.QUOX_AUTH_TOKEN_FILE, 'utf8').trim(); } catch { token = ''; }
24
+ if (!token) throw new Error('No turn credential: QUOX_AUTH_TOKEN_FILE holds no token (the turn has ended). Refusing to use any other credential.');
25
+ return token;
26
+ }
27
+
28
+ const failedOf = (data) => !!((data && data.success === false) || (data && data.result && data.result.success === false));
9
29
 
10
30
  export function registerToolCommands(program, ctx) {
11
31
  const { client, auth } = ctx;
@@ -58,7 +78,7 @@ export function registerToolCommands(program, ctx) {
58
78
  const orgId = resolveOrgId(auth, program);
59
79
  const body = {
60
80
  name: options.name,
61
- type: options.type,
81
+ tool_type: options.type, // #987: auth reads tool_type ('type' made every create 400)
62
82
  risk_level: options.riskLevel,
63
83
  };
64
84
  if (options.description) body.description = options.description;
@@ -132,67 +152,59 @@ export function registerToolCommands(program, ctx) {
132
152
 
133
153
  tool
134
154
  .command('exec')
135
- .description('Execute a governed tool via the collector (POST /api/v1/tools/execute)')
155
+ .description('Execute a governed tool via the collector (POST /api/v1/tools/execute). Large output: use --select/--summary, never a shell pipe.')
136
156
  .argument('<name>', 'tool name/id (the TOOL_DEFS key, e.g. "codebase_query")')
137
157
  .option('--args <json>', "tool arguments as a JSON string, e.g. '{\"query\":\"...\"}'", '{}')
138
158
  // #620: docs/muscle-memory reach for --input; accept it as an alias for
139
159
  // --args so the documented form works. --args still wins if both given.
140
160
  .option('--input <json>', 'alias for --args (tool arguments as a JSON string)')
141
161
  .option('--reason <text>', 'why you are running this; shown on the approval card if the call is held for a human')
162
+ // #970: governed shaping of a large result (the receipt records the shape). Grammar:
163
+ // a.b, a[0], list[field=value], list[field~text], list[*].field, list[*].{a,b}, list[field=value].name, count(list).
164
+ .option('--select <path>', 'return only this part of the result, e.g. agents[status=disconnected].name, streams[name~license] (contains, any case), layers[*].state (every item), containers[*].{host,name,status} (several fields in one call), count(urls), or several paths at once: total,count(urls)')
165
+ .option('--summary', 'honest summary of the main list: total, per-status counts, first N items ("showing N of M")')
166
+ .option('--limit <n>', 'cap the list to N items (with --summary: the sample size, default 10)')
142
167
  .action(async (name, options) => {
143
- auth.requireAuth();
144
168
  // Prefer whichever was explicitly given; --args carries the '{}' default
145
169
  // so it is always defined, hence check --input first.
146
170
  const usedInput = options.input !== undefined && (options.args === undefined || options.args === '{}');
147
171
  const raw = usedInput ? options.input : options.args;
148
172
  const input = parseJSON(raw, usedInput ? '--input' : '--args');
149
- const body = { tool_name: name, input };
150
- if (options.reason) body.reason = String(options.reason);
151
- { const w = missingReasonWarning(name, options.reason); if (w) console.error(w); }
152
-
153
- // Same org-resolution precedence as `fleet exec` (lib/commands/fleet.js):
154
- // the collector's /api/v1/tools/execute requires org_id in the body for
155
- // service-key authenticated callers (isServiceKeyAuth = !req.user,
156
- // services/collector/server.js ~8326) since there is no session to
157
- // derive org from. A human-logged-in caller has req.user and the
158
- // server derives org from the session instead — --org / QUOX_ORG_ID
159
- // still wins when set explicitly.
160
- const orgId = auth.getOrgId(program.opts().org || process.env.QUOX_ORG_ID);
161
- if (!orgId && process.env.QUOX_SERVICE_KEY) {
162
- throw new Error(
163
- 'QUOX_SERVICE_KEY is set but no org could be resolved. '
164
- + 'Set QUOX_ORG_ID=<org> for automation callers, or pass --org <id>.'
165
- );
173
+ const shape = { select: options.select, summary: !!options.summary, limit: options.limit };
174
+ // A malformed shape fails BEFORE the tool runs: nothing executes, nothing to receipt.
175
+ const shapeErr = checkShape(shape);
176
+ if (shapeErr) throw new Error(shapeErr);
177
+ const outputShape = shapeRecord(shape);
178
+ const data = await executeGovernedTool({ client, auth, program }, name, input, { reason: options.reason, outputShape });
179
+ // Shape only a completed, successful result: a held or failed call is shown whole.
180
+ let shown = data;
181
+ const held = data && (data.pending || data._approval || data.status === 'pending_approval' || data.status === 'held');
182
+ if (outputShape && data && !held && !failedOf(data)) {
183
+ const wrapped = data.result !== undefined;
184
+ const value = shapeOutput(wrapped ? data.result : data, shape).value;
185
+ shown = wrapped ? { ...data, result: value, output_shape: outputShape } : { result: value, output_shape: outputShape };
166
186
  }
167
- if (orgId) body.org_id = orgId;
168
-
169
- // QUOXHAT T0 (2026-10-01): automation callers (the collector's
170
- // CLI-tools chat path) carry the conversation + acting user in env,
171
- // exactly like QUOX_ORG_ID above. The collector's execute route keys
172
- // hat-binding resolution on body.session_id and user prefs on
173
- // body.user_id; without them every CLI-tools execution ran against the
174
- // org default hat and user-less. Interactive humans have neither set.
175
- if (process.env.QUOX_SESSION_ID) body.session_id = process.env.QUOX_SESSION_ID;
176
- if (process.env.QUOX_USER_ID) body.user_id = process.env.QUOX_USER_ID;
177
- // QUOXHAT #657 P-C.2: forward the ACTING AGENT so a specialist's CLI-tools
178
- // chat turn executes as that specialist, not the orchestrator. Without it
179
- // the spawned CLI is a service caller with no agent_id, which the
180
- // collector defaults to 'quox' — so under enforce a restricted specialist
181
- // would silently run tools against the orchestrator's broad grant (the
182
- // "CLI exec silently becomes quox" bypass). The collector sets this from
183
- // its own selected_agent; a service-key caller's known agent_id is
184
- // trusted, and the P-B gate then authorizes against the specialist's own
185
- // grant ceiling ∩ the hat mode.
186
- if (process.env.QUOX_AGENT_ID) body.agent_id = process.env.QUOX_AGENT_ID;
187
-
188
- const ep = resolveEndpoint('v1.tools.execute');
189
- const data = await client.post(ep.path, { service: ep.service, body, timeout: 120000 });
187
+ // BASH-FALLBACK: on the model-driven path (a per-turn credential is set) an oversized
188
+ // result would be saved to a file by the Bash tool and the model would reach for jq on
189
+ // it, which the guard denies. Keep it inline as the honest summary view instead. Never
190
+ // for a held or failed call (the approval/error is shown whole), never for a human.
191
+ let modelPath = false;
192
+ try { modelPath = !!readTurnToken(process.env); } catch { modelPath = false; }
193
+ if (modelPath && data && !held && !failedOf(data)) {
194
+ const wrapped = shown && shown.result !== undefined;
195
+ const capped = capInline(wrapped ? shown.result : shown);
196
+ if (capped.capped) shown = wrapped ? { ...shown, result: capped.value } : capped.value;
197
+ }
198
+ // quox#1031 (same class as #999): a refused or failed call must exit non-zero, or a script reads
199
+ // success on failure. A HELD (pending) call awaits a human, so it stays 0.
200
+ const failed = failedOf(data);
201
+ if (failed && !(data && data.pending)) process.exitCode = 1;
190
202
 
191
203
  if (program.opts().output === 'json') {
192
- console.log(JSON.stringify(data, null, 2));
204
+ console.log(JSON.stringify(shown, null, 2));
193
205
  return;
194
206
  }
195
- const result = data && (data.result !== undefined ? data.result : data);
207
+ const result = shown && (shown.result !== undefined ? shown.result : shown);
196
208
  if (typeof result === 'string') {
197
209
  console.log(result);
198
210
  } else {
@@ -214,3 +226,79 @@ export function registerToolCommands(program, ctx) {
214
226
  printSuccess('Tool installed.');
215
227
  });
216
228
  }
229
+
230
+ /**
231
+ * POST /api/v1/tools/execute for one governed tool, with the full identity
232
+ * contract `tool exec` established (per-turn QUOX_AUTH_TOKEN, org resolution,
233
+ * hat session/user/agent forwarding). Shared so a named verb that wraps a tool
234
+ * (e.g. `quox actions list`, #839) authenticates exactly like `tool exec`.
235
+ * Returns the raw response body.
236
+ */
237
+ // quox#1034: the HTTP budget for one governed tool call. 120 s for a person at a terminal. A caller that spawns
238
+ // the CLI with a longer budget of its own (the collector's CLI-tools chat path, where delegate_to_agent waits up to
239
+ // the specialist's delegation.wait_ms, Browser-Q 180 s) states it in QUOX_TOOL_EXEC_TIMEOUT_MS, so the client hop
240
+ // never gives up before the server's own deadline and detach do. Capped at 15 minutes.
241
+ export function toolExecTimeoutMs(env = process.env) {
242
+ const n = Number(env && env.QUOX_TOOL_EXEC_TIMEOUT_MS);
243
+ if (!Number.isFinite(n) || n < 1000) return 120000;
244
+ return Math.min(Math.floor(n), 900000);
245
+ }
246
+
247
+ export async function executeGovernedTool({ client, auth, program }, name, input, { reason, outputShape } = {}) {
248
+ // QUOXON-IDENTITY: QUOX_AUTH_TOKEN is the collector's per-turn scoped
249
+ // credential for the model-driven CLI-tools path. When set it is the ONLY
250
+ // credential: sent as the bearer, never the service key, no stored-session
251
+ // or refresh fallback, and org/user come from the token (not the body).
252
+ const turnToken = readTurnToken(process.env);
253
+ if (!turnToken) auth.requireAuth();
254
+ const body = { tool_name: name, input };
255
+ if (reason) body.reason = String(reason);
256
+ // #970: the collector records the view the caller was shown in the tool receipt.
257
+ if (outputShape) body.output_shape = outputShape;
258
+ { const w = missingReasonWarning(name, reason); if (w) console.error(w); }
259
+
260
+ // Same org-resolution precedence as `fleet exec` (lib/commands/fleet.js):
261
+ // the collector's /api/v1/tools/execute requires org_id in the body for
262
+ // service-key authenticated callers (isServiceKeyAuth = !req.user,
263
+ // services/collector/server.js ~8326) since there is no session to
264
+ // derive org from. A human-logged-in caller has req.user and the
265
+ // server derives org from the session instead — --org / QUOX_ORG_ID
266
+ // still wins when set explicitly.
267
+ const orgId = turnToken ? null : auth.getOrgId(program.opts().org || process.env.QUOX_ORG_ID);
268
+ if (!orgId && !turnToken && process.env.QUOX_SERVICE_KEY) {
269
+ throw new Error(
270
+ 'QUOX_SERVICE_KEY is set but no org could be resolved. '
271
+ + 'Set QUOX_ORG_ID=<org> for automation callers, or pass --org <id>.'
272
+ );
273
+ }
274
+ if (orgId) body.org_id = orgId;
275
+
276
+ // QUOXHAT T0 (2026-10-01): automation callers (the collector's
277
+ // CLI-tools chat path) carry the conversation + acting user in env,
278
+ // exactly like QUOX_ORG_ID above. The collector's execute route keys
279
+ // hat-binding resolution on body.session_id and user prefs on
280
+ // body.user_id; without them every CLI-tools execution ran against the
281
+ // org default hat and user-less. Interactive humans have neither set.
282
+ if (process.env.QUOX_SESSION_ID) body.session_id = process.env.QUOX_SESSION_ID;
283
+ if (process.env.QUOX_USER_ID && !turnToken) body.user_id = process.env.QUOX_USER_ID;
284
+ // QUOXHAT #657 P-C.2: forward the ACTING AGENT so a specialist's CLI-tools
285
+ // chat turn executes as that specialist, not the orchestrator. Without it
286
+ // the spawned CLI is a service caller with no agent_id, which the
287
+ // collector defaults to 'quox' — so under enforce a restricted specialist
288
+ // would silently run tools against the orchestrator's broad grant (the
289
+ // "CLI exec silently becomes quox" bypass). The collector sets this from
290
+ // its own selected_agent; a service-key caller's known agent_id is
291
+ // trusted, and the P-B gate then authorizes against the specialist's own
292
+ // grant ceiling ∩ the hat mode.
293
+ if (process.env.QUOX_AGENT_ID) body.agent_id = process.env.QUOX_AGENT_ID;
294
+
295
+ const ep = resolveEndpoint('v1.tools.execute');
296
+ // noAuth: client.js adds no service key / stored session / refresh retry.
297
+ const data = await client.post(ep.path, {
298
+ service: ep.service,
299
+ body,
300
+ timeout: toolExecTimeoutMs(process.env),
301
+ ...(turnToken ? { noAuth: true, noActAs: true, headers: { Authorization: `Bearer ${turnToken}` } } : {}),
302
+ });
303
+ return data;
304
+ }
@@ -20,7 +20,7 @@
20
20
 
21
21
  import { randomUUID } from 'node:crypto';
22
22
  import { formatOutput, formatDetail, printSuccess, printWarning, printInfo } from '../core/output.js';
23
- import { parseJSON } from './helpers.js';
23
+ import { parseJSON, withTimeoutHint } from './helpers.js';
24
24
  import { resolveEndpoint } from '../api/endpoints.js';
25
25
 
26
26
  /** Flatten the nested `actor` object into a display string for table rows. */
@@ -71,10 +71,14 @@ export function registerTraceCommands(program, ctx) {
71
71
  if (options.denied) query.has_denial = 'true';
72
72
  if (options.hitl) query.has_hitl = 'true';
73
73
 
74
- const data = await client.get(resolveEndpoint('traces.list').path, {
75
- service: resolveEndpoint('traces.list').service,
76
- query,
77
- });
74
+ const data = await withTimeoutHint(
75
+ 'quox trace list',
76
+ 'Try a smaller --limit or a narrower window (--after <iso>, --before <iso>).',
77
+ () => client.get(resolveEndpoint('traces.list').path, {
78
+ service: resolveEndpoint('traces.list').service,
79
+ query,
80
+ }),
81
+ );
78
82
  const traces = (data.traces || data || []).map(normalizeTraceSummary);
79
83
 
80
84
  formatOutput({
@@ -83,8 +87,9 @@ export function registerTraceCommands(program, ctx) {
83
87
  rows: traces,
84
88
  }, { format: program.opts().output, emptyMessage: 'No traces found.' });
85
89
 
86
- if (program.opts().output !== 'json' && program.opts().output !== 'quiet' && typeof data.total === 'number') {
87
- printInfo(`Showing ${traces.length} of ${data.total} trace(s).`);
90
+ if (program.opts().output !== 'json' && program.opts().output !== 'quiet') {
91
+ if (typeof data.total === 'number') printInfo(`Showing ${traces.length} of ${data.total} trace(s).`);
92
+ else if (data.has_more) printInfo(`Showing ${traces.length} trace(s); more available (--offset ${Number(options.offset || 0) + Number(options.limit || 30)}).`);
88
93
  }
89
94
  });
90
95
 
@@ -5,6 +5,8 @@
5
5
  *
6
6
  * quox trigger webhooks|schedules|events
7
7
  * quox trigger workflow-activate|workflow-deactivate <workflow-id>
8
+ * quox trigger pause|resume <workflow-id> [node-id]
9
+ * quox trigger webhook-fire <path> [--body <json|@file>]
8
10
  *
9
11
  * Backend: quoxflow (NOT the auth service, NOT proxied by collector) — see
10
12
  * cli/lib/api/endpoints/triggers.js header for the full access-path
@@ -21,7 +23,48 @@
21
23
  import { formatOutput, formatDetail, printInfo } from '../core/output.js';
22
24
  import { line as glyphLine, printEvent } from '../core/quoxglyph.js';
23
25
  import { resolveOrgId } from './helpers.js';
26
+ import { readFileSync, realpathSync, statSync } from 'node:fs';
27
+ import { resolve as resolvePath, sep, basename } from 'node:path';
24
28
  import { resolveEndpoint } from '../api/endpoints.js';
29
+ import { QuoxApiError } from '../core/errors.js';
30
+
31
+
32
+ // A webhook path is plain segments under /webhooks: no .., no query or fragment, no encoding tricks, no spaces.
33
+ const WEBHOOK_SEGMENT = /^[A-Za-z0-9._~-]+$/;
34
+ export function normalizeWebhookPath(raw) {
35
+ let p = String(raw || '').trim().replace(/^\/+/, '');
36
+ if (p.startsWith('webhooks/')) p = p.slice('webhooks/'.length);
37
+ const segs = p.split('/');
38
+ if (!p || segs.some((x) => !WEBHOOK_SEGMENT.test(x) || x === '.' || x === '..')) {
39
+ throw new Error(`webhook path "${raw}" is not a plain path under /webhooks (letters, digits, . _ ~ - and /; no .., query, fragment or spaces)`);
40
+ }
41
+ return segs.join('/');
42
+ }
43
+ // HYPERION #23: --body @file is an ALLOWLIST. The file must be a regular file whose REAL path (symlinks resolved) is
44
+ // inside the working directory, and never a credential-shaped name: ~/.config/quox/config.json is JSON and holds the
45
+ // user's tokens, and the fired run's trigger data is org-readable and can be forwarded by the workflow.
46
+ const CREDENTIAL_NAME = /^(?:\.env(?:\..*)?|.*\.pem|.*\.key|id_[a-z0-9_]+(?:\.pub)?|credentials(?:\..*)?)$/i;
47
+ export function parseWebhookBody(raw, { cwd = process.cwd() } = {}) {
48
+ if (raw === undefined) return {};
49
+ let text = String(raw);
50
+ if (text.startsWith('@')) {
51
+ const asked = text.slice(1);
52
+ const rule = '--body @file reads only a regular file inside the current directory';
53
+ let real, root;
54
+ try { root = realpathSync(cwd); real = realpathSync(resolvePath(cwd, asked)); }
55
+ catch { throw new Error(`--body @${asked}: no such file (${rule})`); }
56
+ if (real !== root && !real.startsWith(root + sep)) throw new Error(`--body @${asked} is not inside the current directory (${rule}; symlinks are followed)`);
57
+ const rel = real.slice(root.length + 1);
58
+ if (CREDENTIAL_NAME.test(basename(real)) || /(^|[\\/])\.config[\\/].*config\.json$/i.test(rel)) throw new Error(`--body @${asked} looks like a credential file; refused (${rule}, never a key, token or config file)`);
59
+ if (!statSync(real).isFile()) throw new Error(`--body @${asked} is not a regular file (${rule})`);
60
+ text = readFileSync(real, 'utf8');
61
+ }
62
+ try { return JSON.parse(text); } catch { throw new Error('--body is not JSON (pass a JSON string or @file with JSON in it)'); }
63
+ }
64
+ const apiFail = (method, path, err) => {
65
+ const status = err instanceof QuoxApiError ? err.status : 'network error';
66
+ return new Error(`${method} ${path} failed (${status}): ${err.message}`);
67
+ };
25
68
 
26
69
  export function registerTriggerCommands(program, ctx) {
27
70
  const { client, auth } = ctx;
@@ -125,4 +168,56 @@ export function registerTriggerCommands(program, ctx) {
125
168
  printEvent(glyphLine('quox', 'workflow deactivated', { result: id, status: 'ok' }));
126
169
  formatDetail(result, { format: program.opts().output });
127
170
  });
171
+
172
+ // ── trigger pause | resume ─────────────────────────────────────────────────
173
+ // SITE-CLI 2026-10-19: pausing a schedule meant re-applying the workflow JSON with paused:true pasted in by hand.
174
+ // The node is found from the workflow when it has exactly one schedule; with several, name it (never guess).
175
+ const scheduleNode = async (workflowId, nodeId) => {
176
+ if (nodeId) return nodeId;
177
+ const ep = resolveEndpoint('triggers.schedule-list');
178
+ const data = await client.get(ep.path, { service: ep.service });
179
+ const rows = (Array.isArray(data?.data) ? data.data : Array.isArray(data) ? data : []).filter((r) => r.workflowId === workflowId);
180
+ if (rows.length === 0) throw new Error(`no schedule found for workflow ${workflowId} (is it activated? quox trigger schedules)`);
181
+ if (rows.length > 1) throw new Error(`workflow ${workflowId} has ${rows.length} schedule nodes (${rows.map((r) => r.nodeId).join(', ')}): name one, quox trigger pause ${workflowId} <node>`);
182
+ return rows[0].nodeId;
183
+ };
184
+ for (const [verb, key, done] of [['pause', 'triggers.schedule-pause', 'paused'], ['resume', 'triggers.schedule-resume', 'resumed']]) {
185
+ trigger
186
+ .command(verb)
187
+ .description(verb === 'pause'
188
+ ? 'Pause a workflow schedule (stays paused across restarts) without re-applying the workflow'
189
+ : 'Resume a paused workflow schedule')
190
+ .argument('<workflowId>', 'workflow ID')
191
+ .argument('[nodeId]', 'schedule node ID (needed only when the workflow has more than one schedule)')
192
+ .action(async (workflowId, nodeIdArg) => {
193
+ auth.requireAuth();
194
+ const nodeId = await scheduleNode(workflowId, nodeIdArg);
195
+ const ep = resolveEndpoint(key, { workflowId, nodeId });
196
+ let data;
197
+ try { data = await client.post(ep.path, { service: ep.service }); } catch (err) { throw apiFail('POST', ep.path, err); }
198
+ const result = data?.data || data;
199
+ printEvent(glyphLine('quox', `schedule ${done}`, { result: `${workflowId}:${nodeId}`, status: 'ok' }));
200
+ formatDetail(result, { format: program.opts().output });
201
+ });
202
+ }
203
+
204
+ // ── trigger webhook-fire ───────────────────────────────────────────────────
205
+ // Test-fire a workflow's inbound webhook through the real public ingress. The route copies every request header
206
+ // into the run's trigger data, so this sends NO bearer and NO service key: the run is a system:webhook:<path> run,
207
+ // exactly like an outside caller's.
208
+ trigger
209
+ .command('webhook-fire')
210
+ .description('Fire a workflow inbound webhook (POST /webhooks/<path>) as an outside caller would, with no credentials')
211
+ .argument('<path>', 'webhook path, e.g. orders/new (a leading /webhooks/ is fine)')
212
+ .option('--body <json|@file>', 'JSON body (a JSON string, or @file); default {}')
213
+ .action(async (rawPath, options) => {
214
+ const path = normalizeWebhookPath(rawPath);
215
+ const body = parseWebhookBody(options.body, { cwd: (ctx.cwd || (() => process.cwd()))() });
216
+ const ep = resolveEndpoint('triggers.webhook-fire', { path });
217
+ let data;
218
+ try { data = await client.post(ep.path, { service: ep.service, body, noAuth: true, noActAs: true, anonymous: true }); } catch (err) { throw apiFail('POST', ep.path, err); }
219
+ const result = data?.data || data;
220
+ printEvent(glyphLine('quox', 'webhook fired', { result: `/webhooks/${path}${result?.executionId ? ` · run ${result.executionId}` : ''}`, status: 'ok' }));
221
+ formatDetail(result, { format: program.opts().output });
222
+ });
128
223
  }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `quox update` (and `quox --update`, rewritten to this in bin/quox.js): update this quox the way it was
3
+ * installed. The same updater as /update in the conversation client (lib/core/selfUpdate.js).
4
+ *
5
+ * quox update check, show what's new, ask y/N (a TTY), then run the update with the terminal
6
+ * quox update --check check and show, change nothing (exit 10 when an update is available)
7
+ * quox update --yes no question (needed when not a TTY)
8
+ */
9
+ import chalk from 'chalk';
10
+ import { createInterface } from 'node:readline/promises';
11
+ import { detectInstall, checkUpdate, runUpdate, updateLines, updateAsk } from '../core/selfUpdate.js';
12
+
13
+ async function askYes(line) {
14
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
15
+ try { return /^y(es)?$/i.test((await rl.question(`${line} `)).trim()); } finally { rl.close(); }
16
+ }
17
+
18
+ export function registerUpdateCommand(program, ctx = {}) {
19
+ const d = ctx.updateDeps || {};
20
+ const out = d.log || ((l) => console.log(l));
21
+ program.command('update')
22
+ .description('Update quox to the latest version (npm, git checkout, or this box\'s shared CLI); also quox --update')
23
+ .option('--check', 'show what an update would do and change nothing')
24
+ .option('-y, --yes', 'update without asking (needed in a non-interactive shell)')
25
+ .action(async (o) => {
26
+ const info = (d.detect || detectInstall)({});
27
+ const plan = await (d.check || checkUpdate)(info);
28
+ for (const l of updateLines(plan)) out(l);
29
+ if (!plan.ok) { process.exitCode = 2; return; }
30
+ if (plan.upToDate) return;
31
+ if (plan.refused) { process.exitCode = 1; return; }
32
+ if (o.check) { out(`Update with: quox update (runs: ${plan.command.display})`); process.exitCode = 10; return; }
33
+ const yes = o.yes || program.opts().yes;
34
+ if (!yes) {
35
+ const tty = d.isTTY ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
36
+ if (!tty) { out('Not updated: this needs a yes. Re-run with quox update --yes in a non-interactive shell.'); process.exitCode = 1; return; }
37
+ if (!(await (d.ask || askYes)(updateAsk(plan, { restart: false })))) { out('Not updated. Nothing was changed.'); return; }
38
+ }
39
+ out(chalk.dim(`Running: ${plan.command.display}`));
40
+ const r = (d.run || runUpdate)(plan);
41
+ if (r.ok) { out(chalk.green(`✔ Updated (exit 0). The next quox you start is the new one.`)); return; }
42
+ process.exitCode = r.code || 1;
43
+ out(chalk.red(`✖ Update failed: ${plan.command.display} ${r.error ? `could not start (${r.error})` : `exited ${r.code}`}. Nothing else was changed.`));
44
+ out(`Run it yourself to see why: ${plan.manual}`);
45
+ });
46
+ }
@@ -943,6 +943,8 @@ export function registerVaultCommands(program, ctx) {
943
943
  const credentials = data.credentials || data;
944
944
 
945
945
  if (credentials.length === 0) {
946
+ // quox#920: --json always prints valid JSON, an honest empty included.
947
+ if (program.opts().output === 'json') { console.log('[]'); return; }
946
948
  printInfo(`No credentials expiring within ${days} days.`);
947
949
  return;
948
950
  }
@@ -1373,15 +1375,36 @@ export function registerVaultCommands(program, ctx) {
1373
1375
 
1374
1376
  vault
1375
1377
  .command('health')
1376
- .description('Lightweight vault health summary (requires QUOX_SERVICE_KEY, not a user session)')
1377
- .option('--org-id <orgId>', 'org ID to scope the summary to (server default: "default")')
1378
+ .description('Vault health summary: total, healthy, failed, stale, expiring credentials')
1379
+ .option('--org-id <orgId>', 'with QUOX_SERVICE_KEY only: org ID to scope the summary to (server default: "default")')
1378
1380
  .action(async (options) => {
1379
- const ep = resolveEndpoint('vault.health', {});
1380
- const data = await client.get(ep.path, {
1381
- service: ep.service,
1382
- query: options.orgId ? { org_id: options.orgId } : undefined,
1383
- });
1384
- formatDetail(data, { format: program.opts().output, title: 'QuoxVault Health' });
1381
+ // #914: the internal route accepts only the service key, so an owner's `vault health` was a 401
1382
+ // "invalid service key". Automation (QUOX_SERVICE_KEY) keeps that route; a signed-in person reads
1383
+ // the same numbers from their org's vault stats.
1384
+ if (process.env.QUOX_SERVICE_KEY) {
1385
+ const ep = resolveEndpoint('vault.health', {});
1386
+ const data = await client.get(ep.path, {
1387
+ service: ep.service,
1388
+ query: options.orgId ? { org_id: options.orgId } : undefined,
1389
+ });
1390
+ formatDetail(data, { format: program.opts().output, title: 'QuoxVault Health' });
1391
+ return;
1392
+ }
1393
+ auth.requireAuth();
1394
+ const orgId = resolveOrgId(auth, program);
1395
+ const ep = resolveEndpoint('vault.vault-stats.list', { orgId });
1396
+ const st = await client.get(ep.path, { service: ep.service });
1397
+ const summary = {
1398
+ total: st.total ?? 0,
1399
+ healthy: st.healthy ?? 0,
1400
+ failed: st.failed ?? 0,
1401
+ untested: st.untested ?? 0,
1402
+ stale: st.stale ?? 0,
1403
+ expiring_soon: st.expiring ?? 0,
1404
+ rotation_overdue: st.rotationOverdue ?? 0,
1405
+ source: 'org vault stats',
1406
+ };
1407
+ formatDetail(summary, { format: program.opts().output, title: 'QuoxVault Health' });
1385
1408
  });
1386
1409
 
1387
1410
  // ── vault export (removed — WP0.2) ──────────────────────────────────────────