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
@@ -5,8 +5,8 @@
5
5
  export function registerMotionCommand(program) {
6
6
  program
7
7
  .command('motion')
8
- .description('Play the Quox motion gallery: the Q intro, portal, particle, the Dragon\'s Eye, the status Q, ring and reveals (no server needed)')
9
- .argument('[scenes...]', 'only these: orbit portal particle eye hello perpetual ring fx')
8
+ .description('Play the Quox motion gallery: the Q intro, portal, particle, the Dragon\'s Eye, the status Q, ring, reveals and the living dragon (no server needed)')
9
+ .argument('[scenes...]', 'only these: orbit portal particle eye hello perpetual ring fx dragon dragon-fire dragon-moods')
10
10
  .option('--narrow', 'use the 40-column layout (the compact Q)')
11
11
  .option('--slow', 'play three times slower')
12
12
  .option('--mono', 'no colour (also honoured: NO_COLOR)')
@@ -23,6 +23,36 @@ import { line as glyphLine, printEvent } from '../core/quoxglyph.js';
23
23
  import { resolveEndpoint } from '../api/endpoints.js';
24
24
  import { resolveOrgId } from './helpers.js';
25
25
 
26
+ // codebase_query's schema (collector agentTools.js): question (required),
27
+ // target, max_snippets. quox#836: the CLI used to send query/repo/limit,
28
+ // every call failed "Missing required parameter: question", and the failure
29
+ // printed as "0 hits".
30
+ export function buildNucleusInput(question, options = {}) {
31
+ const input = { question };
32
+ if (options.target) input.target = options.target;
33
+ const n = parseInt(options.limit, 10);
34
+ if (n > 0) input.max_snippets = n;
35
+ return input;
36
+ }
37
+
38
+ // The tool result shape varies by engine version, so normalise rather than
39
+ // assuming: an unexpected shape must still print SOMETHING true. An error
40
+ // body is an error, never an empty result.
41
+ export function readNucleusResult(data) {
42
+ if (data && data.success === false) return { error: data.error || data.message || 'tool call failed' };
43
+ // The collector's codebase_query returns agent-friendly text with
44
+ // file:line citations ({ success, output, resultCount }), not an array.
45
+ if (typeof data?.output === 'string') return { text: data.output, count: data.resultCount ?? null, result: data };
46
+ const result = data && (data.result || data.output || data);
47
+ if (result && result.success === false) return { error: result.error || result.message || 'tool call failed' };
48
+ if (typeof result?.output === 'string') return { text: result.output, count: result.resultCount ?? null, result };
49
+ const results = Array.isArray(result?.results) ? result.results
50
+ : Array.isArray(result?.snippets) ? result.snippets
51
+ : Array.isArray(result) ? result
52
+ : null;
53
+ return { results, result };
54
+ }
55
+
26
56
  export function registerNucleusCommands(program, { client, auth }) {
27
57
  const nucleus = program
28
58
  .command('nucleus')
@@ -43,33 +73,34 @@ export function registerNucleusCommands(program, { client, auth }) {
43
73
  const started = Date.now();
44
74
 
45
75
  const ep = resolveEndpoint('v1.tools.execute');
46
- const body = {
47
- tool_name: 'codebase_query',
48
- input: {
49
- query: question,
50
- ...(options.target ? { repo: options.target } : {}),
51
- ...(options.limit ? { limit: parseInt(options.limit, 10) || 8 } : {}),
52
- },
53
- };
76
+ const body = { tool_name: 'codebase_query', input: buildNucleusInput(question, options) };
54
77
  const orgId = resolveOrgId(auth, program);
55
78
  if (orgId) body.org_id = orgId;
56
79
 
57
80
  const data = await client.post(ep.path, { service: ep.service, body, timeout: 60000 });
58
81
  const ms = Date.now() - started;
59
-
60
- // The tool result shape varies by engine version, so normalise rather
61
- // than assuming: an unexpected shape must still print SOMETHING true.
62
- const result = data && (data.result || data.output || data);
63
- const results = Array.isArray(result?.results) ? result.results
64
- : Array.isArray(result?.snippets) ? result.snippets
65
- : Array.isArray(result) ? result
66
- : null;
82
+ const { error, results, result, text, count } = readNucleusResult(data);
67
83
 
68
84
  if (program.opts().output === 'json') {
69
85
  formatDetail(data, { format: 'json' });
70
86
  return;
71
87
  }
72
88
 
89
+ if (error) {
90
+ printEvent(glyphLine('repobrain', `query "${question}"`, { result: 'failed', ms }));
91
+ printWarning(`The query failed: ${error}`);
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+
96
+ if (typeof text === 'string') {
97
+ const hits = count === null ? 'answered' : `${count} hits`;
98
+ printEvent(glyphLine('repobrain', `query "${question}"`, { result: count === 0 ? '0 hits' : hits, ms }));
99
+ console.log('');
100
+ console.log(text.split('\n').map((l) => ` ${l}`).join('\n'));
101
+ return;
102
+ }
103
+
73
104
  if (!results || results.length === 0) {
74
105
  printEvent(glyphLine('repobrain', `query "${question}"`, { result: '0 hits', ms }));
75
106
  printWarning('Nothing matched. Try naming a concrete symbol, filename or behaviour rather than a general description.');
@@ -85,7 +116,7 @@ export function registerNucleusCommands(program, { client, auth }) {
85
116
  const line = r.line || r.start_line || null;
86
117
  console.log('');
87
118
  console.log(` ${loc}${line ? `:${line}` : ''}`);
88
- const snippet = r.snippet || r.content || r.text || '';
119
+ const snippet = r.snippet || r.code || r.content || r.text || '';
89
120
  for (const l of String(snippet).split('\n').slice(0, 20)) console.log(` ${l}`);
90
121
  }
91
122
  });
@@ -12,9 +12,10 @@
12
12
  * live loop gate: budget, risk, interval).
13
13
  */
14
14
 
15
- import { formatOutput, formatDetail, printSuccess } from '../core/output.js';
15
+ import { formatOutput, formatDetail, printSuccess, printWarning } from '../core/output.js';
16
16
  import { parseJSON } from './helpers.js';
17
17
  import { resolveEndpoint } from '../api/endpoints.js';
18
+ import { QuoxUserError } from '../core/errors.js';
18
19
 
19
20
  /**
20
21
  * Format a timestamp as a short relative-time string ("5m ago").
@@ -75,6 +76,13 @@ export function registerObjectiveCommands(program, ctx) {
75
76
  .option('--loop-backend <backend>', 'claude|codex, loop mode only (default claude)')
76
77
  .option('--capability-scope <tools>', 'comma-separated tool names the loop may call (repeatable); empty scope escalates every tool', collectScope)
77
78
  .option('--system-prompt <text>', 'per-objective system prompt, carried verbatim to the run')
79
+ .option('--observe-every <n>', 'loop mode: emit a status line every N turns while it runs (watch with `quox loop watch <id>`)')
80
+ .option('--observe-milestones', 'loop mode: lift MILESTONE: lines from the loop output into headline updates')
81
+ .option('--until <text>', 'loop mode: the done criterion in plain words. The loop runs turn after turn until the model ends a turn with "DONE: <evidence>" (or --max-iterations / --max-duration-ms stops it)')
82
+ .option('--every-seconds <n>', 'loop mode: the runner waits N seconds (30-3600) between turns; the model is never asked to sleep')
83
+ .option('--workspace <name>', 'loop mode: name the durable workspace dir, so a follow-up loop can work in the same files (default: one per loop)')
84
+ .option('--done-file <path>', 'loop mode: DONE only counts once this file exists in the loop workspace; the runner checks it itself (repeatable)', (v, prev = []) => prev.concat(v))
85
+ .option('--done-command <cmd>', 'loop mode: DONE only counts once this command exits 0 in the loop workspace; must be one the loop scope lets it run (repeatable)', (v, prev = []) => prev.concat(v))
78
86
  .action(async (goal, options) => {
79
87
  auth.requireAuth();
80
88
  const body = { title: goal };
@@ -98,6 +106,28 @@ export function registerObjectiveCommands(program, ctx) {
98
106
  if (scope.length > 0) body.capability_scope = scope;
99
107
  }
100
108
  if (options.systemPrompt) body.system_prompt = options.systemPrompt;
109
+ if (options.until) body.until = options.until;
110
+ if (options.everySeconds !== undefined) {
111
+ const n = Number(options.everySeconds);
112
+ if (!Number.isInteger(n) || n < 30 || n > 3600) throw new QuoxUserError('--every-seconds needs a whole number of seconds between 30 and 3600.');
113
+ body.every_seconds = n;
114
+ }
115
+ if (options.workspace) body.workspace = options.workspace;
116
+ const doneChecks = [
117
+ ...(options.doneFile || []).map((path) => ({ kind: 'file_exists', path })),
118
+ ...(options.doneCommand || []).map((command) => ({ kind: 'command_ok', command })),
119
+ ];
120
+ if (doneChecks.length) body.done_check = doneChecks;
121
+ if (options.observeEvery !== undefined || options.observeMilestones) {
122
+ const observe = {};
123
+ if (options.observeEvery !== undefined) {
124
+ const n = Number(options.observeEvery);
125
+ if (!Number.isInteger(n) || n < 1) throw new QuoxUserError('--observe-every needs a whole number of turns (1 or more).');
126
+ observe.every_turns = n;
127
+ }
128
+ if (options.observeMilestones) observe.milestones = true;
129
+ body.observe = observe;
130
+ }
101
131
 
102
132
  const data = await client.post(resolveEndpoint('objectives.create').path, { service: resolveEndpoint('objectives.create').service, body });
103
133
  printSuccess(`Objective created: ${data.id}`);
@@ -155,6 +185,17 @@ export function registerObjectiveCommands(program, ctx) {
155
185
  printSuccess(`Objective ${id}: ${data.status}${data.message ? ` — ${data.message}` : ''}`);
156
186
  });
157
187
 
188
+ objective
189
+ .command('retry')
190
+ .description('Retry a failed or partially completed objective: failed and skipped steps are reset and the plan comes back for approval')
191
+ .argument('<id>', 'objective ID')
192
+ .action(async (id) => {
193
+ auth.requireAuth();
194
+ const ep = resolveEndpoint('objectives.retry', { id });
195
+ const data = await client.post(ep.path, { service: ep.service });
196
+ printSuccess(`Objective ${id}: ${data.steps_reset ?? 0} step(s) reset, plan is back for approval. Run \`quox objective approve ${id}\` to run them again.`);
197
+ });
198
+
158
199
  objective
159
200
  .command('checkpoints')
160
201
  .description('List pending HITL checkpoints across the org (approval queue)')
@@ -191,6 +232,12 @@ export function registerObjectiveCommands(program, ctx) {
191
232
  if (options.notes) body.notes = options.notes;
192
233
  const params = { id: objectiveId, checkpointId };
193
234
  const data = await client.post(resolveEndpoint('objectives.checkpoint-approve', params).path, { service: resolveEndpoint('objectives.checkpoint-approve', params).service, body });
235
+ // LOOPQ-HALT: "approved" is not "running". When the server says the loop did not start, say so in words.
236
+ if (data.started === false) {
237
+ printWarning(`Checkpoint ${checkpointId}: ${data.status}, but the objective did NOT start (objective ${data.objective_status || 'pending'}).`);
238
+ if (data.hint) printWarning(data.hint);
239
+ return;
240
+ }
194
241
  printSuccess(`Checkpoint ${checkpointId}: ${data.status}${data.objective_status ? ` (objective ${data.objective_status})` : ''}`);
195
242
  });
196
243
 
@@ -209,6 +256,17 @@ export function registerObjectiveCommands(program, ctx) {
209
256
  printSuccess(`Checkpoint ${checkpointId}: ${data.status}${data.objective_status ? ` (objective ${data.objective_status})` : ''}`);
210
257
  });
211
258
 
259
+ objective
260
+ .command('cancel')
261
+ .description('Cancel an objective (pending ones included, e.g. proof leftovers that never started); its open tasks are cancelled too. For a RUNNING loop prefer `halt`, which also kills the live session.')
262
+ .argument('<objectiveId>', 'objective ID')
263
+ .action(async (objectiveId) => {
264
+ auth.requireAuth();
265
+ const ep = resolveEndpoint('objectives.cancel', { id: objectiveId });
266
+ const data = await client.delete(ep.path, { service: ep.service });
267
+ printSuccess(`Objective ${objectiveId}: ${data?.status || 'cancelled'}`);
268
+ });
269
+
212
270
  objective
213
271
  .command('halt')
214
272
  .description('Halt a running QLoop loop ON DEMAND (unconditional human stop; no pending gate needed). Fires the same signed-kill as a gate deny.')
@@ -0,0 +1,89 @@
1
+ /**
2
+ * `quox offload` : run a heavy command on a pool worker VM and behave like running it locally.
3
+ *
4
+ * quox offload [--dir <path>] [--fresh] [--keep] [--cores N --memory-gb N --disk-gb N] -- <command...>
5
+ * quox offload status
6
+ * quox offload release
7
+ *
8
+ * Engine: lib/core/offload.js. The worker is leased through the human pool REST API
9
+ * (same as `quox vm pool lease`) with a dedicated key at ~/.config/quox/offload_ed25519.
10
+ */
11
+
12
+ import path from 'node:path';
13
+ import { printInfo } from '../core/output.js';
14
+ import { makePoolRest } from '../core/vm.js';
15
+ import { OffloadError, realDeps, runOffload, releaseWorker, workerStatus, gitToplevel, WORKER_NAME_RE } from '../core/offload.js';
16
+
17
+ export function registerOffloadCommands(program, ctx, { deps: injected } = {}) {
18
+ const { client, auth } = ctx;
19
+ const json = () => program.opts().output === 'json';
20
+ const base = () => injected || realDeps({ rest: makePoolRest({ client }) });
21
+ // --worker <name> / QUOX_OFFLOAD_WORKER: your own worker (state, lease, size), not the box's shared one.
22
+ const mk = (options = {}) => {
23
+ const d = base();
24
+ const name = options.worker ?? d.env?.QUOX_OFFLOAD_WORKER;
25
+ if (name !== undefined && name !== '' && !WORKER_NAME_RE.test(String(name))) {
26
+ throw new OffloadError(`worker name must be lowercase letters, digits and dashes (up to 32), got ${JSON.stringify(name)}`);
27
+ }
28
+ return name ? Object.assign(Object.create(Object.getPrototypeOf(d)), d, { worker: String(name) }) : d;
29
+ };
30
+ const WORKER_HELP = 'use your own named worker (its own lease and size) instead of the shared one; or set QUOX_OFFLOAD_WORKER';
31
+
32
+ const off = program
33
+ .command('offload')
34
+ .description('Run a heavy command on a pool worker VM (synced dir, cached npm ci, live output, same exit code)')
35
+ .argument('[command...]', 'command to run remotely (put it after --)')
36
+ .option('--dir <path>', 'directory to sync (default: git toplevel of the current directory)')
37
+ .option('--fresh', 'release the current worker, lease a new one and reinstall dependencies')
38
+ .option('--keep', 'leave the worker leased for reuse (the default; kept for compatibility)')
39
+ .option('--cores <n>', 'vCPU cores for a newly leased worker')
40
+ .option('--memory-gb <n>', 'memory in GB for a newly leased worker')
41
+ .option('--disk-gb <n>', 'disk in GB for a newly leased worker')
42
+ .option('--worker <name>', WORKER_HELP)
43
+ .option('--docker', 'also give the worker Docker (user hyp in the docker group) and build the quox-sandbox images from the synced repo when they are missing or changed')
44
+ .allowExcessArguments(true)
45
+ .action(async (command, options) => {
46
+ auth.requireAuth();
47
+ const cwd = process.cwd();
48
+ const dir = path.resolve(options.dir || gitToplevel(cwd));
49
+ try {
50
+ const code = await runOffload(mk(options), {
51
+ dir, cwd, command, fresh: !!options.fresh, docker: !!options.docker,
52
+ cores: options.cores, memoryGb: options.memoryGb, diskGb: options.diskGb,
53
+ });
54
+ process.exitCode = code;
55
+ } catch (e) {
56
+ if (!(e instanceof OffloadError)) throw e;
57
+ process.stderr.write(`offload: ${e.message}\n`);
58
+ process.exitCode = 1;
59
+ }
60
+ });
61
+
62
+ off.command('status')
63
+ .description('Show your offload worker: vmid, ip, lease expiry, synced dirs')
64
+ .option('--worker <name>', WORKER_HELP)
65
+ .action(async (options) => {
66
+ auth.requireAuth();
67
+ const s = await workerStatus(mk(options));
68
+ if (json()) { console.log(JSON.stringify(s, null, 2)); return; }
69
+ if (!s.worker) { printInfo('no offload worker leased (it is leased on the next `quox offload -- <command>`)'); return; }
70
+ const w = s.worker;
71
+ console.log(`worker ${w.vmid} ${w.ip || '?'} ${w.state} lessee ${w.lessee} lease expires ${w.lease_expires_at || '?'}`);
72
+ for (const d of s.dirs) console.log(` synced ${d.local} -> ${d.remote} ${d.synced_at || ''}`);
73
+ });
74
+
75
+ off.command('release')
76
+ .description('Release (destroy) your offload worker via the pool')
77
+ .option('--worker <name>', WORKER_HELP)
78
+ .action(async (options) => {
79
+ auth.requireAuth();
80
+ try {
81
+ const vmid = await releaseWorker(mk(options));
82
+ if (json()) console.log(JSON.stringify({ released: vmid }));
83
+ else printInfo(vmid ? `released worker ${vmid}` : 'no offload worker to release');
84
+ } catch (e) {
85
+ process.stderr.write(`offload: release failed: ${e.message}\n`);
86
+ process.exitCode = 1;
87
+ }
88
+ });
89
+ }
@@ -64,7 +64,8 @@ export function inferTargetType(value) {
64
64
  /** curl install command for a host that is not yet a fleet-enrolled dev server. */
65
65
  export function buildOnboardInstallCommand(collectorBase, host) {
66
66
  const base = String(collectorBase || '').replace(/\/+$/, '');
67
- return `curl -sSL ${base}/install | sudo bash -s -- --host-id ${host}`;
67
+ // REL-423: fail loud, never `curl | sudo bash` (that pipe reports success when the download fails).
68
+ return `( f=$(mktemp) && trap 'rm -f "$f"' EXIT && curl -fsSL ${base}/install -o "$f" && sudo bash "$f" --host-id ${host} )`;
68
69
  }
69
70
 
70
71
  // ── the wizard ──────────────────────────────────────────────────────────────
@@ -3,9 +3,11 @@
3
3
  */
4
4
 
5
5
  import { QuoxError } from '../core/errors.js';
6
- import { formatOutput, formatDetail, printSuccess } from '../core/output.js';
6
+ import { formatOutput, formatDetail, printSuccess, printWarning } from '../core/output.js';
7
+ import { otherProfileUsers, listProcs } from '../core/profileUsers.js';
7
8
  import { resolveOrgId } from './helpers.js';
8
9
  import { resolveEndpoint } from '../api/endpoints.js';
10
+ import { numberedOrgLines, pickOrgByInput, sharedSwitchHint } from '../quoxon/orgSwitch.js';
9
11
 
10
12
  export function registerOrgCommands(program, ctx) {
11
13
  const { client, auth, config } = ctx;
@@ -89,9 +91,26 @@ export function registerOrgCommands(program, ctx) {
89
91
  org
90
92
  .command('switch')
91
93
  .description('Switch active organization context')
92
- .argument('<orgId>', 'organization ID or slug')
93
- .action(async (orgId) => {
94
+ .argument('[orgId]', 'organization ID, slug, or the number from the list (omit to pick from a numbered list)')
95
+ .action(async (orgIdArg) => {
94
96
  auth.requireAuth();
97
+ let orgId = orgIdArg;
98
+ if (!orgId || /^\d+$/.test(orgId)) {
99
+ const l = await client.get(resolveEndpoint('orgs.list').path, { service: resolveEndpoint('orgs.list').service });
100
+ const orgs = Array.isArray(l) ? l : (l?.organizations || l?.orgs || l?.data || []);
101
+ if (!orgs.length) throw new QuoxError('No organisations returned for this login.', 1);
102
+ if (!orgId) {
103
+ const curId = (() => { try { return auth.getOrgId(); } catch { return null; } })();
104
+ for (const line of numberedOrgLines(orgs, curId)) console.log(line);
105
+ if (!process.stdin.isTTY) throw new QuoxError('Pass a number, slug or ID: quox org switch <number>.', 1);
106
+ const rl = (await import('node:readline/promises')).createInterface({ input: process.stdin, output: process.stdout });
107
+ try { orgId = await rl.question('Switch to which? (number, slug, or Enter to cancel) '); } finally { rl.close(); }
108
+ if (!orgId.trim()) { console.log('Not switched.'); return; }
109
+ }
110
+ const pick = pickOrgByInput(orgId, orgs);
111
+ if (pick.org) orgId = pick.org.id;
112
+ else if (/^\d+$/.test(orgId)) throw new QuoxError(`No org numbered ${orgId}. Run \`quox org switch\` to see the list.`, 1);
113
+ }
95
114
  // The server matches memberships by org ID only. Accept a slug (or exact name) by
96
115
  // resolving it against the caller's org list first; a 26-char ULID passes through.
97
116
  let targetId = orgId;
@@ -120,6 +139,14 @@ export function registerOrgCommands(program, ctx) {
120
139
  config.setAuth(authData);
121
140
 
122
141
  printSuccess(`Switched to ${data.org?.name || orgId}`);
142
+ // #1034: this changed the SHARED profile. Name the other live sessions that will follow it.
143
+ try {
144
+ const name = config.getActiveProfileName?.();
145
+ const others = name ? otherProfileUsers({ profile: name, defaultProfile: config._data?.activeProfile, procs: listProcs(), selfPids: [process.pid, process.ppid] }) : [];
146
+ if (others.length) {
147
+ printWarning(sharedSwitchHint({ name, pids: others.map(p => p.pid) }));
148
+ }
149
+ } catch { /* best effort: never fail the switch over the warning */ }
123
150
  });
124
151
 
125
152
  // Members subcommands
@@ -0,0 +1,28 @@
1
+ /**
2
+ * quox peek <id>
3
+ *
4
+ * Look up an id without knowing what it is: an AEE envelope, inbox (approval) item, objective, workflow
5
+ * run, WARD receipt or agent. The id's shape picks what to ask; every lookup is an existing read
6
+ * endpoint; a miss names each kind tried and what it answered. The TUI's /peek prints the same card.
7
+ */
8
+ import { createPeekFetchers, peekId, peekLines } from '../core/peek.js';
9
+ import { resolveOrgId } from './helpers.js';
10
+
11
+ export function registerPeekCommand(program, ctx) {
12
+ const { client, auth } = ctx;
13
+ program
14
+ .command('peek')
15
+ .description('Look up an envelope, inbox item, objective, run, WARD receipt or agent by id (read only)')
16
+ .argument('<id>', 'the id, e.g. a ULID, a WARD entry UUID or an agent name')
17
+ .action(async (id) => {
18
+ auth.requireAuth();
19
+ const orgId = resolveOrgId(auth, program);
20
+ const r = await peekId(id, createPeekFetchers({ client, orgId: () => orgId }));
21
+ if (program.opts().output === 'json') {
22
+ console.log(JSON.stringify({ id: r.id, shape: r.shape, kind: r.hit?.kind || null, data: r.hit?.data || null, also: r.also, tried: r.tried }, null, 2));
23
+ } else {
24
+ for (const l of peekLines(r)) console.log(l.text);
25
+ }
26
+ if (!r.hit) process.exitCode = 5; // not found (the inbox verbs use 5 for not_found too)
27
+ });
28
+ }
@@ -137,6 +137,23 @@ function renderRoadmapsList(roadmaps) {
137
137
  return lines;
138
138
  }
139
139
 
140
+ function renderSearchResults(data) {
141
+ const results = data.results || [];
142
+ if (results.length === 0) return [chalk.dim(` No streams match "${data.q}".`)];
143
+ const lines = [''];
144
+ for (const r of results) {
145
+ const g = statusGlyph(r.status);
146
+ const where = [...new Set((r.matches || []).map((m) => m.where))].join(', ');
147
+ const first = (r.matches || []).find((m) => m.snippet) || {};
148
+ lines.push(
149
+ ` ${g.color(g.glyph)} ${pad(truncate(r.id, ID_WIDTH), ID_WIDTH)} ${pad(truncate(r.name, NAME_WIDTH), NAME_WIDTH)} `
150
+ + `${pad(r.status, 15)} ${pad(where, 18)} ${chalk.dim(truncate(first.snippet || '', 60))}`
151
+ );
152
+ }
153
+ lines.push('', chalk.dim(` ${results.length} stream${results.length === 1 ? '' : 's'} matched "${data.q}"`));
154
+ return lines;
155
+ }
156
+
140
157
  function streamLine(s, { indent = 0, showReasons = false } = {}) {
141
158
  const prefix = indent > 0 ? ' '.repeat(indent) + '└ ' : ' ';
142
159
  const g = statusGlyph(s.status);
@@ -546,6 +563,27 @@ export function registerPlanCommands(program, ctx) {
546
563
  printSuccess(`Created roadmap ${created.id || options.id} for org ${orgId}`);
547
564
  });
548
565
 
566
+ // ── search ───────────────────────────────────────────────────────────
567
+
568
+ plan
569
+ .command('search <text...>')
570
+ .description('Search streams by text (id, name, notes, plan, phase titles)')
571
+ .option('--roadmap <id>', 'limit to one roadmap')
572
+ .option('--status <status>', `limit to one status (${VALID_STATUSES.join(', ')})`)
573
+ .action(async (textParts, options) => {
574
+ auth.requireAuth();
575
+ if (options.status && !VALID_STATUSES.includes(options.status)) {
576
+ throw new Error(`invalid --status "${options.status}" (expected ${VALID_STATUSES.join(', ')})`);
577
+ }
578
+ const query = { q: textParts.join(' ') };
579
+ if (options.roadmap) query.roadmap = options.roadmap;
580
+ if (options.status) query.status = options.status;
581
+ const ep = resolveEndpoint('quoxplan.search');
582
+ const data = await client.get(ep.path, { service: ep.service, query });
583
+ if (isJsonOutput(program)) return printJson(data);
584
+ console.log(renderSearchResults(data).join('\n'));
585
+ });
586
+
549
587
  // ── list ─────────────────────────────────────────────────────────────
550
588
 
551
589
  plan
@@ -2,10 +2,12 @@
2
2
  * Plugin commands: org-scoped plugin install registry (PD-1..PD-8)
3
3
  *
4
4
  * quox plugin list|get|install|update|uninstall|mine
5
+ * quox plugin enable <id> (PLUGIN-IMG #1012: prebuilt private image, licence first)
5
6
  * quox plugin data list|get|set|delete
6
7
  * quox plugin secrets list|set|reveal|delete
7
8
  * quox plugin assign list|add|remove
8
9
  * quox plugin trusted-keys list|add|remove
10
+ * quox plugin revocations list|add|remove
9
11
  *
10
12
  * Backend: services/auth/routes/plugins.js, mounted org-scoped at
11
13
  * `/api/orgs/:orgId/...` (server.js). Install/update/uninstall and all
@@ -18,6 +20,9 @@ import { readFileSync } from 'node:fs';
18
20
  import { formatOutput, formatDetail, printSuccess, printWarning, printInfo } from '../core/output.js';
19
21
  import { resolveOrgId, parseJSON } from './helpers.js';
20
22
  import { resolveEndpoint } from '../api/endpoints.js';
23
+ import { enablePlugin, entitlementFrom } from '../server/pluginEnable.js';
24
+ import { resolveInstall, readEnvKeys } from '../server/install.js';
25
+ import { createSys } from '../server/sys.js';
21
26
 
22
27
  /** Read + JSON.parse a file, wrapping errors with the file path for context. */
23
28
  function readJsonFile(filePath, label) {
@@ -113,6 +118,56 @@ export function registerPluginCommands(program, ctx) {
113
118
  }, { title: `Licence: ${pluginId}` });
114
119
  });
115
120
 
121
+ // ── plugin enable (PLUGIN-IMG #1012) ────────────────────────────────────────
122
+ // Starts a licensed plugin from its prebuilt private image on this
123
+ // self-hosted install. Licence first: unlicensed means nothing runs.
124
+
125
+ plugin
126
+ .command('enable')
127
+ .description('Start a licensed plugin from its prebuilt image on this self-hosted install (QuoxPlan for now). Unlicensed: refused, nothing pulled.')
128
+ .argument('<pluginId>', 'plugin id, e.g. quoxplan')
129
+ .option('--dir <path>', 'the install directory (default: the one Getting started recorded)')
130
+ .action(async (pluginId, opts) => {
131
+ auth.requireAuth();
132
+ const deps = ctx.serverDeps || {};
133
+ const env = deps.env || process.env;
134
+ const sys = deps.sys || createSys();
135
+ const checkEntitlement = async (id) => {
136
+ const ep = resolveEndpoint('setup.licensing.entitlements.list');
137
+ const data = await client.get(ep.path, { service: ep.service });
138
+ return entitlementFrom(data, id);
139
+ };
140
+ // Licence before anything that touches the box: an unlicensed enable must not even look for the install.
141
+ let ent;
142
+ try { ent = await checkEntitlement(pluginId); } catch (err) { ent = err; }
143
+ const resolveFirst = !(ent instanceof Error) && ent && ent.entitled;
144
+ let install = null;
145
+ if (resolveFirst) {
146
+ const i = await resolveInstall({ dir: opts.dir, env, sys, journey: deps.journey });
147
+ if (i.error) { const e = new Error(i.error); e.exitCode = 2; throw e; }
148
+ install = i;
149
+ }
150
+ const tag = install ? (env.QUOX_IMAGE_TAG || readEnvKeys(install.repoDir, ['QUOX_IMAGE_TAG']).QUOX_IMAGE_TAG || '') : '';
151
+ const result = await enablePlugin({
152
+ pluginId,
153
+ checkEntitlement: async () => { if (ent instanceof Error) throw ent; return ent; },
154
+ install: install || { repoDir: '' },
155
+ sys,
156
+ tag,
157
+ ...(deps.getPullCredential ? { getPullCredential: deps.getPullCredential } : {}),
158
+ ...(deps.sleep ? { sleep: deps.sleep } : {}),
159
+ log: (l) => printInfo(l),
160
+ });
161
+ if (program.opts().output === 'json') {
162
+ console.log(JSON.stringify({ plugin: pluginId, ok: result.ok, stage: result.stage, message: result.message }, null, 2));
163
+ } else if (result.ok) {
164
+ printSuccess(result.message);
165
+ } else {
166
+ printWarning(result.message);
167
+ }
168
+ if (!result.ok) process.exitCode = result.stage === 'licence' ? 3 : 1;
169
+ });
170
+
116
171
  // ── plugin list ─────────────────────────────────────────────────────────────
117
172
 
118
173
  plugin
@@ -536,4 +591,59 @@ export function registerPluginCommands(program, ctx) {
536
591
  await client.delete(ep.path, reqOpts);
537
592
  printSuccess('Trusted key removed.');
538
593
  });
594
+
595
+ // ── plugin revocations (SDK-TEAM W5) ────────────────────────────────────────
596
+
597
+ const revocations = plugin
598
+ .command('revocations')
599
+ .description('Revoke a plugin signer key or one bundle digest for this org (admin only). A revoked plugin cannot be installed and stops loading on the next page load.');
600
+
601
+ revocations
602
+ .command('list')
603
+ .description('List revocations')
604
+ .action(async () => {
605
+ auth.requireAuth();
606
+ const orgId = resolveOrgId(auth, program);
607
+ const ep = resolveEndpoint('plugins.revocations.list', { orgId });
608
+ const result = await client.get(ep.path, { service: ep.service });
609
+ const rows = result.revocations || result || [];
610
+ formatOutput({
611
+ columns: ['id', 'kind', 'value', 'reason', 'revoked_by', 'revoked_at'],
612
+ headers: ['ID', 'Kind', 'Value', 'Reason', 'Revoked By', 'Revoked'],
613
+ rows,
614
+ }, { format: program.opts().output, emptyMessage: 'No revocations.' });
615
+ });
616
+
617
+ revocations
618
+ .command('add')
619
+ .description('Revoke a signer key or a bundle digest')
620
+ .option('--signer-key <hex>', 'raw Ed25519 public key (64 hex), as in signature.json public_key_hex')
621
+ .option('--bundle-digest <hex>', 'sha256 of plugin.esm.js (64 hex), as shown by quox plugin list')
622
+ .option('--reason <text>', 'why (kept in the audit trail)')
623
+ .action(async (options) => {
624
+ auth.requireAuth();
625
+ if (!!options.signerKey === !!options.bundleDigest) {
626
+ throw new Error('Specify exactly one of --signer-key or --bundle-digest.');
627
+ }
628
+ const orgId = resolveOrgId(auth, program);
629
+ const body = options.signerKey
630
+ ? { kind: 'signer_key', value: options.signerKey, reason: options.reason }
631
+ : { kind: 'bundle_digest', value: options.bundleDigest, reason: options.reason };
632
+ const ep = resolveEndpoint('plugins.revocations.create', { orgId });
633
+ const result = await client.post(ep.path, { service: ep.service, body });
634
+ printSuccess('Revoked.');
635
+ formatDetail(result.revocation || result, { format: program.opts().output });
636
+ });
637
+
638
+ revocations
639
+ .command('remove')
640
+ .description('Lift a revocation')
641
+ .argument('<revocationId>', 'revocation ID')
642
+ .action(async (revocationId) => {
643
+ auth.requireAuth();
644
+ const orgId = resolveOrgId(auth, program);
645
+ const ep = resolveEndpoint('plugins.revocations.delete', { orgId, revocationId });
646
+ await client.delete(ep.path, { service: ep.service });
647
+ printSuccess('Revocation lifted.');
648
+ });
539
649
  }