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
@@ -0,0 +1,367 @@
1
+ /**
2
+ * ROWS (owner 2026-10-17): `quox rows`, the CLI for the live lines above the quox window's status bar.
3
+ * CLI first: everything /row does in the window works here, from a shell or a script.
4
+ *
5
+ * quox rows list [--now] what you have, where each one lives, what it shows now
6
+ * quox rows add <id> --url|--quox|--plugin|--cmd one row (no --template = the status-row contract)
7
+ * quox rows edit <id> [...] change any field; --contract drops the template
8
+ * quox rows remove <id> (alias rm)
9
+ * quox rows test [id] | test --url ... fetch once and draw it here, nothing saved for an ad hoc test
10
+ * quox rows share [id...] a bundle others can import (command rows never leave)
11
+ * quox rows import <file|-> url, quox and plugin rows; command rows need --trust-commands
12
+ * AND a person answering y to each exact command
13
+ * quox rows contract the JSON shape a row endpoint returns
14
+ *
15
+ * Storage is the same as the window's (quoxon/rowsStore.js): synced rows in the user's server-side preferences,
16
+ * command rows in a 0600 local file. An open window picks changes up within a minute, or at once with /row reload.
17
+ */
18
+ import chalk from 'chalk';
19
+ import { readFileSync } from 'node:fs';
20
+ import { createInterface } from 'node:readline/promises';
21
+ import { resolveEndpoint } from '../api/endpoints.js';
22
+ import { resolveOrgId } from './helpers.js';
23
+ import {
24
+ checkRow, secretInRow, syncable, createRowEngine, runShell, rowParts, clipParts, labelWidth, MAX_ROWS,
25
+ shareBundle, readBundle, cleanText, STATUS_ROW_TONES, themeRole, ROLES, fieldHints, dataFrom
26
+ } from '../quoxon/rows.js';
27
+ import { createRowsStore } from '../quoxon/rowsStore.js';
28
+ import { c as colourOf } from '../quoxon/theme.js';
29
+
30
+ export const CONTRACT = {
31
+ kind: 'quox.status-row',
32
+ version: 1,
33
+ fields: {
34
+ text: 'required (unless value is given). One line, 160 characters at most.',
35
+ label: 'optional. Shown first, 16 characters. The row\'s own label wins.',
36
+ value: 'optional. A number (shown compactly: 1.2k) or a short string (24), drawn bold before the text.',
37
+ tone: `optional. One of ${STATUS_ROW_TONES.join(', ')}. Colours the text and adds ✔ ⚠ ✖ ◆.`,
38
+ trend: 'optional. up, down, flat, or a number (the change: 3 shows ▲3, -2 shows ▼2).',
39
+ link: 'optional. An http(s) URL to look at for more; shown by quox rows test and /row test.'
40
+ },
41
+ // TUI-QPE 2026-10-19: additive v1.1 fields, kept apart so the v1 field list stays exactly as published.
42
+ fields_v1_1: {
43
+ cells: 'optional. [{parts: [{text, tone, bold, keep, group}], keep}]: one row in several colours. A narrow window drops the lowest keep first (a whole part, its group, or a cell; no keep = never dropped), never half a word.',
44
+ grid: 'optional. A short name; rows with the same grid line their cells up in columns (quox-qpe-row uses "qpe").',
45
+ min_cols: 'optional. A whole number; the row hides when the drawn width (the terminal width less one) is under it.'
46
+ },
47
+ example: { label: 'Approvals', value: 3, text: 'waiting for you', tone: 'warn', trend: 1, link: 'https://quox.example/approvals' },
48
+ rules: [
49
+ 'Plain text output counts as {"text": "<the text>"}.',
50
+ 'One source can draw several lines: {"text": "<short line for older clients>", "grid": "...", "lines": [<status row>, ...]} (up to 4 lines in all above the status bar).',
51
+ 'Terminal escapes and control characters are removed before anything is drawn.',
52
+ 'A row definition never carries a secret: use a quox or plugin row (your session is used) or a local command that reads its own secret.',
53
+ 'Command rows run on the machine that holds them only. They are never synced, shared or imported without a person saying yes to the exact command.'
54
+ ]
55
+ };
56
+
57
+ /** A theme role -> a chalk painter in the window's own colours (hex, ansi256, or plain under NO_COLOR). No grey. */
58
+ export function tint(name) {
59
+ const v = colourOf(name);
60
+ if (!v) return chalk.reset;
61
+ const m = /^ansi256\((\d+)\)$/.exec(v);
62
+ if (m) return chalk.ansi256(Number(m[1]));
63
+ return /^#[0-9a-f]{6}$/i.test(v) ? chalk.hex(v) : chalk.reset;
64
+ }
65
+ const paint = (role, bold, t) => { const f = tint(themeRole(role)); return bold ? f.bold(t) : f(t); };
66
+ export const drawRow = (state, width = process.stdout.columns || 100, labelW = 0) =>
67
+ ' ' + clipParts(rowParts(state, { labelW }), Math.max(10, width - 2)).map(([t, role, bold]) => paint(role, bold, t)).join('');
68
+
69
+ const where = (r) => {
70
+ const s = r.source;
71
+ if (s.type === 'url') return s.url;
72
+ if (s.type === 'quox') return `${s.path}${s.service === 'collector' ? ' (collector)' : ''}`;
73
+ if (s.type === 'plugin') return `${s.plugin} ${s.path}`;
74
+ return s.cmd;
75
+ };
76
+
77
+ /** Options -> a source (or null when none of --url/--quox/--plugin/--cmd was given). */
78
+ export function sourceFromOptions(o) {
79
+ const given = ['url', 'quox', 'plugin', 'cmd'].filter((k) => o[k] !== undefined);
80
+ if (given.length > 1) return { error: `Give one of --url, --quox, --plugin or --cmd (you gave ${given.map((g) => `--${g}`).join(' and ')}).` };
81
+ if (!given.length) return { source: null };
82
+ const k = given[0];
83
+ if (k === 'url') return { source: { type: 'url', url: String(o.url) } };
84
+ if (k === 'cmd') return { source: { type: 'cmd', cmd: String(o.cmd) } };
85
+ if (k === 'quox') return { source: { type: 'quox', service: o.service || (String(o.quox).startsWith('/api/v1/') ? 'collector' : 'auth'), path: String(o.quox) } };
86
+ return { source: { type: 'plugin', plugin: String(o.plugin), path: o.path ? String(o.path) : '' } };
87
+ }
88
+
89
+ /**
90
+ * A plugin source with no --path -> the path its manifest's statusRows names. -> { source, label?, every? } | { error }.
91
+ * manifest.statusRows: [{ id, label, path, every?, description? }] (quox-plugin-sdk docs/status-rows.md).
92
+ */
93
+ export async function resolvePluginRow(client, orgId, source, rowId) {
94
+ if (source.path) return { source };
95
+ let manifest;
96
+ try {
97
+ const ep = resolveEndpoint('plugins.get', { orgId, pluginId: source.plugin });
98
+ const d = await client.get(ep.path, { service: ep.service, retries: 0 });
99
+ manifest = d?.plugin?.manifest ?? d?.manifest;
100
+ if (typeof manifest === 'string') manifest = JSON.parse(manifest);
101
+ } catch (e) { return { error: `Could not read the ${source.plugin} plugin (${e?.message || 'failed'}). Is it installed in this org?` }; }
102
+ const rows = Array.isArray(manifest?.statusRows) ? manifest.statusRows : [];
103
+ if (!rows.length) return { error: `The ${source.plugin} plugin ships no status rows (its manifest has no statusRows). Give --path to read one of its routes.` };
104
+ const pick = rowId ? rows.find((r) => r?.id === rowId) : rows[0];
105
+ if (!pick) return { error: `${source.plugin} has no status row "${rowId}". It has: ${rows.map((r) => r?.id).filter(Boolean).join(', ')}.` };
106
+ return { source: { ...source, path: String(pick.path || '') }, label: typeof pick.label === 'string' ? pick.label : undefined, every: typeof pick.every === 'string' ? pick.every : undefined };
107
+ }
108
+
109
+ export function registerRowsCommands(program, ctx) {
110
+ const { client, auth, config } = ctx;
111
+ const store = createRowsStore({ client, config, orgId: () => { try { return auth.getOrgId(program.opts().org); } catch { return null; } } });
112
+ const json = () => program.opts().output === 'json';
113
+ const fail = (msg, code = 1) => { console.error(tint('error')(msg)); process.exitCode = code; };
114
+ const say = (msg) => console.log(msg);
115
+
116
+ async function loadAll() {
117
+ const r = await store.rowPrefs();
118
+ return { synced: r.rows, cmd: store.localCmdRows(), offline: r.source !== 'server' ? (r.error || 'server unreachable') : null };
119
+ }
120
+ /** Write the synced list; -> true when the server took it. */
121
+ async function saveSynced(rows) {
122
+ const r = await store.setRowPrefs(rows);
123
+ if (!r.synced) fail(`Saved on this machine only: ${r.error || 'the server did not take it'}. Run the command again to retry.`);
124
+ return r.synced;
125
+ }
126
+ const engine = () => createRowEngine({ fetchUrl: (u) => store.rowFetch(u), fetchQuox: (s) => store.rowFetchQuox(s), runCmd: runShell });
127
+
128
+ /** Fetch once and print raw value, notes and the drawn row. -> the probe result. */
129
+ async function showOnce(row) {
130
+ const p = await engine().probe(row);
131
+ const raw = String(p.raw || '');
132
+ if (raw) {
133
+ say(tint('label')('Raw value:'));
134
+ const lines = raw.split('\n').filter((l) => l.trim()).slice(0, 6);
135
+ for (const l of lines) say(' ' + cleanText(l, (process.stdout.columns || 100) - 4));
136
+ if (raw.length > 600 || lines.length >= 6) say(' …');
137
+ }
138
+ if (!p.ok) {
139
+ fail(`⚠ ${row.label || row.id}: ${p.error}`);
140
+ const hints = raw ? fieldHints(dataFrom(raw)) : [];
141
+ if (hints.length && !row.template) {
142
+ say(tint('label')('It is JSON but not a status row. A template can read it; fields you can use:'));
143
+ say(' ' + hints.map((h) => tint('command')(`{{${h}}}`)).join(' '));
144
+ say(tint('label')(` e.g. --template '{{${hints.find((h) => !h.startsWith('count(')) || hints[0]}}} …'`));
145
+ }
146
+ return p;
147
+ }
148
+ for (const w of p.warnings || []) say(tint('warn')(`Note: ${w}`));
149
+ say(tint('label')(`Shape: ${row.template ? 'template' : 'status-row contract'}${p.seg?.link ? ` · link ${p.seg.link}` : ''}`));
150
+ say(tint('label')('The row:'));
151
+ say(drawRow({ row, status: 'ok', text: p.text, seg: p.seg, at: Date.now() }));
152
+ return p;
153
+ }
154
+
155
+ /** Build a row from add/edit options on top of `base`. -> { row } | { error }. */
156
+ async function buildRow(id, o, base = null) {
157
+ const s = sourceFromOptions(o);
158
+ if (s.error) return { error: s.error };
159
+ let source = s.source || (base ? { ...base.source } : null);
160
+ if (!source) return { error: 'Say where the row reads from: --url <url>, --quox <path>, --plugin <id>, or --cmd "<command>".' };
161
+ let label = o.label ?? base?.label;
162
+ let every = o.every ?? base?.source?.every; // a new source keeps the row's interval
163
+ if (source.type === 'plugin' && !source.path) {
164
+ auth.requireAuth();
165
+ const r = await resolvePluginRow(client, resolveOrgId(auth, program), source, o.row);
166
+ if (r.error) return { error: r.error };
167
+ source = r.source; label = label ?? r.label; every = every ?? r.every;
168
+ }
169
+ if (every) source.every = every;
170
+ const draft = { id, source };
171
+ if (label) draft.label = label;
172
+ const colour = o.colour ?? o.color ?? base?.colour;
173
+ if (colour) draft.colour = colour;
174
+ const template = o.contract ? '' : (o.template ?? base?.template ?? '');
175
+ if (template) draft.template = template;
176
+ const c = checkRow(draft);
177
+ if (c.error) return { error: c.error };
178
+ const secret = secretInRow(c.row);
179
+ if (secret) return { error: secret };
180
+ return { row: c.row };
181
+ }
182
+
183
+ async function put(row, { replace = null } = {}) {
184
+ const { synced, cmd } = await loadAll();
185
+ const all = [...synced, ...cmd];
186
+ const prev = replace ? all.find((r) => r.id === replace) : all.find((r) => r.id.toLowerCase() === row.id.toLowerCase());
187
+ if (!prev && all.length >= MAX_ROWS) return fail(`You have ${MAX_ROWS} rows already (the most the window has room for). quox rows remove <id> makes space.`);
188
+ const syncedNext = synced.filter((r) => r !== prev);
189
+ const cmdNext = cmd.filter((r) => r !== prev);
190
+ if (syncable(row)) syncedNext.push(row); else cmdNext.push(row);
191
+ if (prev && !syncable(prev) || !syncable(row)) if (!store.setLocalCmdRows(cmdNext)) return fail('Could not write the local rows file.');
192
+ if (prev && syncable(prev) || syncable(row)) if (!(await saveSynced(syncedNext))) return undefined;
193
+ if (json()) return say(JSON.stringify({ ok: true, row, where: syncable(row) ? 'synced' : 'this machine' }, null, 2));
194
+ say(tint('ok')(`✔ Row ${row.id} ${prev ? 'saved' : 'added'}. ${syncable(row) ? 'It follows your account to your other machines.' : 'It runs on THIS machine only and is never synced.'}`));
195
+ say(tint('label')(` Open windows show it within a minute (/row reload for now). quox rows test ${row.id} draws it here.`));
196
+ return undefined;
197
+ }
198
+
199
+ const rows = program.command('rows').description('Your live lines above the quox window\'s status bar: add, edit, remove, test, share, import (url, quox, plugin or local command rows)');
200
+
201
+ const sourceOpts = (cmd) => cmd
202
+ .option('--url <url>', 'read JSON or text from a URL (no credentials are sent to other hosts)')
203
+ .option('--quox <path>', 'read a path on your own Quox with your session, e.g. /api/orgs/{org}/inbox/stats')
204
+ .option('--service <name>', 'for --quox: auth (default) or collector')
205
+ .option('--plugin <id>', 'read a status row a plugin ships (its manifest statusRows)')
206
+ .option('--row <rowId>', 'for --plugin: which of its status rows (default: the first)')
207
+ .option('--path <path>', 'for --plugin: a route of the plugin backend instead of a declared row')
208
+ .option('--cmd <command>', 'run a local command (this machine only, never synced)')
209
+ .option('--template <template>', 'fill {{path}} from the data; leave out to read the status-row contract')
210
+ .option('--every <interval>', 'refresh interval: 30s, 5m, 1h (5 s to 1 day)')
211
+ .option('--label <text>', 'a short name drawn first')
212
+ .option('--colour <role>', `text colour: ${ROLES.join(', ')}`);
213
+
214
+ rows.command('list').alias('ls')
215
+ .description('List your rows: where each reads from, where it is stored, and (--now) what it shows now')
216
+ .option('--now', 'fetch each row once and draw it')
217
+ .action(async (o) => {
218
+ auth.requireAuth();
219
+ const { synced, cmd, offline } = await loadAll();
220
+ const all = [...synced, ...cmd];
221
+ if (offline) console.error(tint('warn')(`⚠ Server not reachable (${offline}); showing this machine's copy.`));
222
+ const states = [];
223
+ if (o.now) {
224
+ const e = engine();
225
+ for (const r of all) {
226
+ const p = await e.probe(r);
227
+ states.push(p.ok ? { row: r, status: 'ok', text: p.text, seg: p.seg, at: Date.now() } : { row: r, status: 'error', error: p.error, text: '', at: null });
228
+ }
229
+ }
230
+ if (json()) return say(JSON.stringify({ rows: all.map((r, i) => ({ ...r, stored: syncable(r) ? 'synced' : 'this machine', shape: r.template ? 'template' : 'contract', ...(states[i] ? { now: states[i].status === 'ok' ? states[i].text : null, error: states[i].error || undefined } : {}) })), max: MAX_ROWS }, null, 2));
231
+ if (!all.length) return say(`No rows yet. Try: quox rows add approvals --quox '/api/orgs/{org}/inbox/stats' --template '{{pending_approvals}} waiting' --label Approvals\n(or /row new in the quox window).`);
232
+ say(tint('heading').bold(`Rows (${all.length} of ${MAX_ROWS})`));
233
+ const lw = labelWidth(all.map((row) => ({ row })));
234
+ for (const [i, r] of all.entries()) {
235
+ say(` ${tint('id').bold(r.id.padEnd(12))} ${tint('label')(r.source.type.padEnd(6))} ${tint('text')(where(r))}`);
236
+ say(tint('label')(` ${''.padEnd(12)} every ${r.source.every} · ${syncable(r) ? 'synced to your account' : 'this machine only'} · ${r.template ? `template ${r.template}` : 'status-row contract'}${r.label ? ` · label ${r.label}` : ''}`));
237
+ if (states[i]) say(' ' + drawRow(states[i], (process.stdout.columns || 100) - 3, lw));
238
+ }
239
+ return undefined;
240
+ });
241
+
242
+ sourceOpts(rows.command('add <id>').description('Add a row. No --template = it reads the status-row contract (quox rows contract)')).option('--test', 'fetch it once and draw it after saving')
243
+ .action(async (id, o) => {
244
+ auth.requireAuth();
245
+ const b = await buildRow(id, o);
246
+ if (b.error) return fail(b.error);
247
+ const { synced, cmd } = await loadAll();
248
+ if ([...synced, ...cmd].some((r) => r.id.toLowerCase() === b.row.id.toLowerCase())) return fail(`A row called ${b.row.id} exists. quox rows edit ${b.row.id} changes it.`);
249
+ await put(b.row);
250
+ if (o.test && !process.exitCode) await showOnce(b.row);
251
+ return undefined;
252
+ });
253
+
254
+ sourceOpts(rows.command('edit <id>').description('Change a row: any of the add options; --contract drops the template')).option('--contract', 'read the status-row contract instead of a template')
255
+ .action(async (id, o) => {
256
+ auth.requireAuth();
257
+ const { synced, cmd } = await loadAll();
258
+ const cur = [...synced, ...cmd].find((r) => r.id.toLowerCase() === String(id).toLowerCase());
259
+ if (!cur) return fail(`No row "${id}". quox rows list shows them.`);
260
+ const b = await buildRow(cur.id, o, cur);
261
+ if (b.error) return fail(b.error);
262
+ if (JSON.stringify(b.row) === JSON.stringify(cur)) return say(`Nothing changed on ${cur.id}.`);
263
+ return put(b.row, { replace: cur.id });
264
+ });
265
+
266
+ rows.command('remove <id>').alias('rm').description('Remove a row')
267
+ .action(async (id) => {
268
+ auth.requireAuth();
269
+ const { synced, cmd } = await loadAll();
270
+ const cur = [...synced, ...cmd].find((r) => r.id.toLowerCase() === String(id).toLowerCase());
271
+ if (!cur) return fail(`No row "${id}". quox rows list shows them.`);
272
+ if (syncable(cur)) { if (!(await saveSynced(synced.filter((r) => r !== cur)))) return undefined; }
273
+ else if (!store.setLocalCmdRows(cmd.filter((r) => r !== cur))) return fail('Could not write the local rows file.');
274
+ return json() ? say(JSON.stringify({ ok: true, removed: cur.id })) : say(tint('ok')(`✔ Removed row ${cur.id}.`));
275
+ });
276
+
277
+ sourceOpts(rows.command('test [id]').description('Fetch a row once and draw it here. With --url/--quox/--plugin/--cmd instead of an id, try one before adding it (nothing is saved)'))
278
+ .action(async (id, o) => {
279
+ auth.requireAuth();
280
+ let row;
281
+ if (id) {
282
+ const { synced, cmd } = await loadAll();
283
+ row = [...synced, ...cmd].find((r) => r.id.toLowerCase() === String(id).toLowerCase());
284
+ if (!row) return fail(`No row "${id}". quox rows list shows them.`);
285
+ } else {
286
+ const b = await buildRow('test', o);
287
+ if (b.error) return fail(b.error);
288
+ row = b.row;
289
+ }
290
+ const p = await showOnce(row);
291
+ if (json()) say(JSON.stringify({ ok: p.ok, text: p.text ?? null, seg: p.seg ?? null, warnings: p.warnings ?? [], error: p.error ?? null }, null, 2));
292
+ return undefined;
293
+ });
294
+
295
+ rows.command('share [ids...]').description('Print a bundle of your rows for someone else to import. Command rows never leave this machine')
296
+ .action(async (ids) => {
297
+ auth.requireAuth();
298
+ const { synced, cmd } = await loadAll();
299
+ const all = [...synced, ...cmd];
300
+ const want = ids?.length ? ids.map((x) => String(x).toLowerCase()) : null;
301
+ const missing = want ? want.filter((x) => !all.some((r) => r.id.toLowerCase() === x)) : [];
302
+ if (missing.length) return fail(`No row ${missing.join(', ')}. quox rows list shows them.`);
303
+ const picked = want ? all.filter((r) => want.includes(r.id.toLowerCase())) : all;
304
+ const { bundle, skipped, refused } = shareBundle(picked);
305
+ for (const s of skipped) console.error(tint('warn')(`Not shared: ${s} is a command row (it runs a shell on this machine, so it never leaves it).`));
306
+ for (const r of refused) console.error(tint('warn')(`Not shared: ${r.id}: ${r.why}`));
307
+ if (!bundle.rows.length) return fail('Nothing to share.');
308
+ return say(JSON.stringify(bundle, null, 2));
309
+ });
310
+
311
+ rows.command('import <file>').description('Add rows from a share bundle (a file, or - for stdin). Command rows are never added without --trust-commands and a y for each exact command')
312
+ .option('--trust-commands', 'offer each command row for a yes/no, one by one (needs a person at the terminal)')
313
+ .option('--replace', 'replace rows that have the same id')
314
+ .action(async (file, o) => {
315
+ auth.requireAuth();
316
+ let text;
317
+ try { text = file === '-' ? readFileSync(0, 'utf8') : readFileSync(file, 'utf8'); } catch (e) { return fail(`Could not read ${file}: ${e.message}`); }
318
+ const b = readBundle(text);
319
+ for (const e of b.errors) console.error(tint('warn')(`Skipped ${e}`));
320
+ const take = [...b.rows];
321
+ if (b.commands.length) {
322
+ if (!o.trustCommands) {
323
+ for (const r of b.commands) console.error(tint('warn')(`Not added: ${r.id} would run \`${cleanText(r.source.cmd, 200)}\` on this machine. Command rows from a bundle need --trust-commands and your yes.`));
324
+ } else if (!process.stdin.isTTY || file === '-') {
325
+ return fail('A command row from a bundle needs a person at the terminal to say yes to it. Nothing was imported.');
326
+ } else {
327
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
328
+ try {
329
+ for (const r of b.commands) {
330
+ say(tint('warn').bold(`⚠ ${r.id} runs this on THIS machine every ${r.source.every}:`));
331
+ say(' ' + tint('command')(cleanText(r.source.cmd, 500)));
332
+ const a = (await rl.question(' Add it? y/N ')).trim();
333
+ if (/^y(es)?$/i.test(a)) take.push(r); else say(' Skipped.');
334
+ }
335
+ } finally { rl.close(); }
336
+ }
337
+ }
338
+ if (!take.length) return fail('Nothing to import.');
339
+ const { synced, cmd } = await loadAll();
340
+ const has = (id) => [...synced, ...cmd].find((r) => r.id.toLowerCase() === id.toLowerCase());
341
+ const clash = take.filter((r) => has(r.id));
342
+ if (clash.length && !o.replace) return fail(`You already have ${clash.map((r) => r.id).join(', ')}. --replace swaps them; nothing was imported.`);
343
+ const syncedNext = synced.filter((r) => !take.some((t) => t.id.toLowerCase() === r.id.toLowerCase()));
344
+ const cmdNext = cmd.filter((r) => !take.some((t) => t.id.toLowerCase() === r.id.toLowerCase()));
345
+ for (const r of take) (syncable(r) ? syncedNext : cmdNext).push(r);
346
+ if (syncedNext.length + cmdNext.length > MAX_ROWS) return fail(`That would make ${syncedNext.length + cmdNext.length} rows; the window has room for ${MAX_ROWS}. Remove some first. Nothing was imported.`);
347
+ if (take.some((r) => !syncable(r)) || cmdNext.length !== cmd.length) if (!store.setLocalCmdRows(cmdNext)) return fail('Could not write the local rows file.');
348
+ if (!(await saveSynced(syncedNext))) return undefined;
349
+ return say(tint('ok')(`✔ Imported ${take.map((r) => r.id).join(', ')}.`));
350
+ });
351
+
352
+ rows.command('contract').description('The JSON a row endpoint returns (the status-row contract), with an example')
353
+ .action(() => {
354
+ if (json()) return say(JSON.stringify(CONTRACT, null, 2));
355
+ say(tint('heading').bold('Status-row contract v1'));
356
+ for (const [k, v] of Object.entries(CONTRACT.fields)) say(` ${tint('id').bold(k.padEnd(6))} ${v}`);
357
+ say(tint('label')('v1.1 (colours and narrow windows):'));
358
+ for (const [k, v] of Object.entries(CONTRACT.fields_v1_1)) say(` ${tint('id').bold(k.padEnd(8))} ${v}`);
359
+ say(tint('label')('Example:'));
360
+ say(' ' + JSON.stringify(CONTRACT.example));
361
+ say(tint('label')('Draws as:'));
362
+ const ex = { row: { id: 'approvals', source: {} }, status: 'ok', at: Date.now(), seg: { label: 'Approvals', value: '3', text: 'waiting for you', tone: 'warn', trend: '▲1' }, text: '3 waiting for you ▲1' };
363
+ say(' ' + drawRow(ex));
364
+ for (const r of CONTRACT.rules) say(tint('label')(` · ${r}`));
365
+ return undefined;
366
+ });
367
+ }
@@ -31,9 +31,14 @@ export function registerRunCommands(program, ctx) {
31
31
 
32
32
  let rows, source;
33
33
  try {
34
+ // Journey #3 (2026-10-19): the engine path sent no query, so --workflow/--status/--limit were ignored and a
35
+ // filter for one workflow listed every workflow's runs. quoxflow's /api/executions takes all three.
34
36
  const ep = resolveEndpoint('executions.list');
35
- const data = await client.get(ep.path, { service: ep.service });
36
- const items = data.data || data.items || (Array.isArray(data) ? data : []);
37
+ const engineQuery = { ...(options.workflow ? { workflowId: options.workflow } : {}), ...(options.status ? { status: options.status } : {}), limit: options.limit };
38
+ const data = await client.get(ep.path, { service: ep.service, query: engineQuery });
39
+ let items = data.data || data.items || (Array.isArray(data) ? data : []);
40
+ // an older engine that ignores the filter must still never show another workflow's runs
41
+ if (options.workflow) items = items.filter((it) => (it.workflowId ?? it.workflow_id) === options.workflow);
37
42
  rows = items.map((item) => ({ ...item, source: 'engine' }));
38
43
  source = 'engine';
39
44
  } catch (err) {
@@ -85,7 +90,24 @@ export function registerRunCommands(program, ctx) {
85
90
  }
86
91
  }
87
92
 
88
- formatDetail({ ...data, source }, { format: program.opts().output });
93
+ // RUNS: surface WHICH step failed and the retry hint at the top level, so a failed
94
+ // run no longer reads as a bare "fetch failed" with no step.
95
+ const detail = { ...data, source };
96
+ const d = data.error && typeof data.error.details === 'object' && data.error.details ? data.error.details : null;
97
+ if (data.status === 'failed' && d && d.nodeId) {
98
+ detail.failedStep = d.nodeName && d.nodeName !== d.nodeId ? `${d.nodeName} (${d.nodeId})` : String(d.nodeId);
99
+ }
100
+ if (data.status === 'failed' && data.error && data.error.message) {
101
+ // formatDetail truncates long nested values; show the full code + network detail here.
102
+ detail.failure = `${data.error.code || 'ERROR'}: ${data.error.message}`;
103
+ }
104
+ if (data.metadata && data.metadata.retryable) {
105
+ detail.retry = data.metadata.retryExecutionId
106
+ ? `auto-retried as ${data.metadata.retryExecutionId}`
107
+ : `retryable: ${data.metadata.retryHint || `quox run create --workflow ${data.workflowId}`}`;
108
+ }
109
+
110
+ formatDetail(detail, { format: program.opts().output, full: true });
89
111
  });
90
112
 
91
113
  run
@@ -93,12 +115,35 @@ export function registerRunCommands(program, ctx) {
93
115
  .description('Start a new workflow run')
94
116
  .requiredOption('--workflow <id>', 'workflow ID')
95
117
  .option('--input <json>', 'input data as JSON')
118
+ .option('--wait', 'engine workflows: wait for the run to finish (up to 10 minutes) instead of returning its id at once')
96
119
  .action(async (options) => {
97
120
  auth.requireAuth();
98
121
  const orgId = resolveOrgId(auth, program);
99
122
  const body = { workflow_id: options.workflow };
100
123
  if (options.input) body.context = parseJSON(options.input, '--input');
101
- const data = await client.post(resolveEndpoint('runs.create', { orgId }).path, { service: resolveEndpoint('runs.create', { orgId }).service, body });
124
+ let data;
125
+ try {
126
+ data = await client.post(resolveEndpoint('runs.create', { orgId }).path, { service: resolveEndpoint('runs.create', { orgId }).service, body });
127
+ } catch (err) {
128
+ if (err.status !== 404) throw err;
129
+ // #997: the auth runs model only knows auth-store workflows; an engine workflow runs on the engine
130
+ // (same fallback as `workflow run`, quoxflow#7).
131
+ const getEp = resolveEndpoint('workflows.applyGet', { workflowId: options.workflow });
132
+ try { await client.get(getEp.path, { service: getEp.service }); } catch (engineErr) { if (engineErr.status === 404) throw err; throw engineErr; }
133
+ const exEp = resolveEndpoint('workflows.applyExecute', { workflowId: options.workflow });
134
+ // #1029: start async by default and return the id at once; the engine ran synchronously before, so any
135
+ // run past the 30 s client timeout printed 'timed out' while it kept going. --wait keeps the old shape.
136
+ const ran = await client.post(exEp.path, {
137
+ service: exEp.service,
138
+ body: { triggerData: body.context || {}, async: !options.wait },
139
+ ...(options.wait ? { timeout: 600000 } : {}),
140
+ });
141
+ const runId = ran.executionId || ran.execution_id || ran.id || ran.execution?.id || ran.data?.executionId || 'ok';
142
+ const st = ran.status || ran.execution?.status || ran.data?.status;
143
+ printSuccess(`Run started (engine): ${runId}${st ? ` [${st}]` : ''}`);
144
+ if (!options.wait && runId !== 'ok') console.log(` Follow it: quox run get ${runId}`);
145
+ return;
146
+ }
102
147
  printSuccess(`Run started: ${data.run?.id || data.id || 'ok'}`);
103
148
  });
104
149
 
@@ -112,8 +157,12 @@ export function registerRunCommands(program, ctx) {
112
157
  const orgId = resolveOrgId(auth, program);
113
158
  const body = {};
114
159
  if (options.reason) body.reason = options.reason;
115
- await client.post(resolveEndpoint('runs.cancel', { orgId, runId }).path, { service: resolveEndpoint('runs.cancel', { orgId, runId }).service, body });
116
- printSuccess('Run cancelled.');
160
+ // #997: engine first (the runs `run list` shows), auth-store fallback on a 404 only
161
+ const source = await engineFirst(
162
+ () => { const ep = resolveEndpoint('executions.cancel', { executionId: runId }); return client.post(ep.path, { service: ep.service, body }); },
163
+ () => client.post(resolveEndpoint('runs.cancel', { orgId, runId }).path, { service: resolveEndpoint('runs.cancel', { orgId, runId }).service, body }),
164
+ );
165
+ printSuccess(`Run cancelled (${source}).`);
117
166
  });
118
167
 
119
168
  run
@@ -121,12 +170,35 @@ export function registerRunCommands(program, ctx) {
121
170
  .description('Resume a paused run')
122
171
  .argument('<runId>', 'run ID')
123
172
  .option('--response <json>', 'human response payload as JSON')
173
+ .option('--wait', 'engine runs: wait for the resumed run to finish (up to 10 minutes) instead of answering at once')
124
174
  .action(async (runId, options) => {
125
175
  auth.requireAuth();
126
176
  const orgId = resolveOrgId(auth, program);
127
- const body = {};
128
- if (options.response) body.response = parseJSON(options.response, '--response');
129
- await client.post(resolveEndpoint('runs.resume', { orgId, runId }).path, { service: resolveEndpoint('runs.resume', { orgId, runId }).service, body });
130
- printSuccess('Run resumed.');
177
+ const response = options.response ? parseJSON(options.response, '--response') : undefined;
178
+ const body = response === undefined ? {} : { response };
179
+ // #997: the engine's resume body IS the decision ({approved, approver, reason?}; approver must be you);
180
+ // the auth model wraps it as { response }.
181
+ const source = await engineFirst(
182
+ // #1037: async by default; the engine checks the approver synchronously, then runs the rest in the background
183
+ () => {
184
+ const ep = resolveEndpoint('executions.resume', { executionId: runId });
185
+ return client.post(ep.path, {
186
+ service: ep.service,
187
+ body: { ...(response || {}), async: !options.wait },
188
+ ...(options.wait ? { timeout: 600000 } : {}),
189
+ });
190
+ },
191
+ () => client.post(resolveEndpoint('runs.resume', { orgId, runId }).path, { service: resolveEndpoint('runs.resume', { orgId, runId }).service, body }),
192
+ );
193
+ printSuccess(`Run resumed (${source}).`);
194
+ if (source === 'engine' && !options.wait) console.log(` Follow it: quox run get ${runId}`);
131
195
  });
132
196
  }
197
+
198
+ // #997: try the engine (quoxflow) store, fall back to the auth-service store only when the engine says 404.
199
+ // Any other engine answer (409 already terminal, 400 not paused) is the real outcome and is thrown.
200
+ async function engineFirst(engineCall, authCall) {
201
+ try { await engineCall(); return 'engine'; } catch (err) { if (err.status !== 404) throw err; }
202
+ await authCall();
203
+ return 'auth';
204
+ }
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Scopes are read-only projections of the org/division/department/team
7
7
  * hierarchy — there is no backend write route for scopes. To create,
8
- * update, or delete a scope, use `quox org`, `quox division`,
8
+ * update, or delete a scope, use `quox org`, `quox org divisions`,
9
9
  * `quox department`, or `quox team` instead.
10
10
  */
11
11
 
@@ -18,7 +18,7 @@ export function registerScopeCommands(program, ctx) {
18
18
 
19
19
  const scope = program
20
20
  .command('scope')
21
- .description('View scopes (read-only — use `quox org`/`quox division`/`quox team` to manage)');
21
+ .description('View scopes (read-only — use `quox org`/`quox org divisions`/`quox department`/`quox team` to manage)');
22
22
 
23
23
  scope
24
24
  .command('list')