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,237 @@
1
+ /**
2
+ * `quox vm` : drive the HYPERION (agent id atlas) Proxmox tools from the CLI.
3
+ *
4
+ * quox vm list [--node n] [--type qemu|lxc]
5
+ * quox vm info <name|vmid>
6
+ * quox vm start|stop|reboot|shutdown <name|vmid> [--reason t]
7
+ * quox vm doctor <name|vmid> why is it slow or unhealthy? ranked findings with evidence (read only)
8
+ * quox vm snapshot <name|vmid> [--name s] [--reason t]
9
+ * quox vm rehearse <name|vmid> [--check url:..|unit:..|cmd:..] [--keep-twin] [--no-wait] -- <command>
10
+ * quox vm pool status|lease|done <vmid>|config
11
+ *
12
+ * Every action goes through the governed tool route (POST /api/v1/tools/execute).
13
+ * A held call prints its approval id and the `quox approvals result <id>` hint
14
+ * and is never re-submitted. Engine shared with the QUOXON /vm command: lib/core/vm.js.
15
+ */
16
+
17
+ import { Option } from 'commander';
18
+ import { resolveOrgId } from './helpers.js';
19
+ import { formatOutput, printInfo, printWarning } from '../core/output.js';
20
+ import {
21
+ VM_ACTIONS, VmPendingError, pendingHint, makeExec, makePoolRest,
22
+ listVms, vmListRows, vmInfo, vmDoctor, doctorLines, vmAction, vmSnapshot, vmRehearse, waitRehearsal, poolStatus, poolLease, poolDone, poolConfig, poolHostKeyLines,
23
+ readPubkey, textOf, payloadOf,
24
+ } from '../core/vm.js';
25
+
26
+ export function registerVmCommands(program, ctx) {
27
+ const { client, auth } = ctx;
28
+ const json = () => program.opts().output === 'json';
29
+
30
+ const vm = program
31
+ .command('vm')
32
+ .description('Proxmox VMs via the HYPERION agent tools (governed: held calls wait for approval)');
33
+
34
+ /** Run one verb: auth, exec, and turn a held call into the approval hint (exit 0, no re-submit). */
35
+ const run = (fn) => async (...args) => {
36
+ auth.requireAuth();
37
+ const exec = makeExec({ client, orgId: resolveOrgId(auth, program) });
38
+ try { await fn(exec, ...args); }
39
+ catch (e) {
40
+ if (!(e instanceof VmPendingError)) throw e;
41
+ if (json()) console.log(JSON.stringify({ pending: true, tool: e.tool, approvalId: e.approvalId, inbox_item_id: e.inboxItemId }));
42
+ else printWarning(pendingHint(e));
43
+ }
44
+ };
45
+ const emit = (data, human) => {
46
+ if (json()) console.log(JSON.stringify(data, null, 2));
47
+ else human();
48
+ };
49
+
50
+ // Read-only cluster views (HYPERION read tools; REST first, Bastion pvesh-read fallback).
51
+ vm.command('cluster')
52
+ .description('Proxmox cluster health: quorum and nodes (read only)')
53
+ .action(run(async (exec) => {
54
+ const p = payloadOf(await exec('proxmox_cluster_status', {}));
55
+ emit(p, () => console.log(textOf(p)));
56
+ }));
57
+
58
+ vm.command('ha')
59
+ .description('Proxmox HA status (read only)')
60
+ .action(run(async (exec) => {
61
+ const p = payloadOf(await exec('proxmox_ha_status', {}));
62
+ emit(p, () => console.log(textOf(p)));
63
+ }));
64
+
65
+ vm.command('templates')
66
+ .description('Golden templates you can clone from (read only)')
67
+ .action(run(async (exec) => { const p = payloadOf(await exec('proxmox_templates_list', {})); emit(p, () => console.log(textOf(p))); }));
68
+
69
+ vm.command('storage')
70
+ .description('Proxmox storage pools and usage (read only)')
71
+ .action(run(async (exec) => { const p = payloadOf(await exec('proxmox_storage', {})); emit(p, () => console.log(textOf(p))); }));
72
+
73
+ vm.command('node <name>')
74
+ .description('CPU, memory and storage of one Proxmox node (read only)')
75
+ .action(run(async (exec, name) => {
76
+ if (!/^[a-z0-9-]{1,32}$/.test(name)) throw new Error('node name must be lowercase letters, digits or -');
77
+ const p = payloadOf(await exec('proxmox_node_resources', { node: name })); emit(p, () => console.log(textOf(p)));
78
+ }));
79
+
80
+ vm.command('resolve <name>')
81
+ .description('Find which node and VMID a VM name lives on (read only)')
82
+ .action(run(async (exec, name) => {
83
+ const p = payloadOf(await exec('proxmox_vm_resolve', /^\d+$/.test(name) ? { vmid: Number(name) } : { name }));
84
+ emit(p, () => console.log(textOf(p)));
85
+ }));
86
+
87
+ vm.command('list')
88
+ .description('List VMs (--type lxc lists containers)')
89
+ .option('--node <name>', 'only this Proxmox node')
90
+ .option('--type <kind>', 'qemu | lxc', undefined)
91
+ .action(run(async (exec, options) => {
92
+ const vms = await listVms(exec, { node: options.node, type: options.type });
93
+ if (json()) { console.log(JSON.stringify(vms.map((v) => v.raw), null, 2)); return; }
94
+ formatOutput(
95
+ { columns: ['vmid', 'name', 'node', 'type', 'status', 'tags'], rows: vmListRows(vms) },
96
+ { emptyMessage: 'No VMs found.' },
97
+ );
98
+ }));
99
+
100
+ vm.command('agents')
101
+ .description('Which running VMs answer on the QEMU guest agent: counts and the VMs that do not (read only)')
102
+ .option('--node <name>', 'only this Proxmox node')
103
+ .option('--limit <n>', 'how many gaps to list (default 25, max 50)')
104
+ .action(run(async (exec, options) => {
105
+ const p = payloadOf(await exec('proxmox_vms', { guest: true, ...(options.node ? { node: options.node } : {}), ...(options.limit ? { limit: Number(options.limit) } : {}) }));
106
+ emit(p, () => {
107
+ printInfo(`Guest agent: ${p.agent_running} of ${p.probed} running VMs answer (${p.not_enabled} not enabled, ${p.not_running} not running, ${p.unknown} unknown; ${p.stopped} stopped VMs not checked)`);
108
+ formatOutput({ columns: ['vmid', 'name', 'node', 'agent'], rows: p.gaps || [] }, { emptyMessage: 'Every running VM answers on the guest agent.' });
109
+ if (p.gaps_truncated) printWarning(`${p.gaps_truncated} more not shown (--limit)`);
110
+ });
111
+ }));
112
+
113
+ vm.command('info <target>')
114
+ .description('Resources and state of one VM (name or vmid)')
115
+ .action(run(async (exec, target) => {
116
+ const { vm: v, resources } = await vmInfo(exec, target);
117
+ emit({ vm: v, resources }, () => {
118
+ printInfo(`${v.name || v.vmid} vmid ${v.vmid} node ${v.node} ${v.type}`);
119
+ console.log(textOf(resources));
120
+ });
121
+ }));
122
+
123
+ vm.command('doctor <target>')
124
+ .description('Why is this VM slow or unhealthy? Verdict and ranked findings with evidence and a suggested fix (read only; name or vmid)')
125
+ .action(run(async (exec, target) => {
126
+ const report = await vmDoctor(exec, target);
127
+ emit(report, () => { for (const l of doctorLines(report)) console.log(l); });
128
+ }));
129
+
130
+ for (const action of VM_ACTIONS) {
131
+ vm.command(`${action} <target>`)
132
+ .description(`${action[0].toUpperCase()}${action.slice(1)} a VM (name or vmid); write, may need approval`)
133
+ .option('--reason <text>', 'why (recorded as _reason on the call)')
134
+ .action(run(async (exec, target, options) => {
135
+ const { vm: v, result } = await vmAction(exec, action, target, { reason: options.reason });
136
+ emit({ vm: v, action, result }, () => {
137
+ printInfo(`${action} ${v.name || v.vmid} (vmid ${v.vmid} on ${v.node})`);
138
+ console.log(textOf(result));
139
+ });
140
+ }));
141
+ }
142
+
143
+ vm.command('snapshot <target>')
144
+ .description('Snapshot a VM (name or vmid); write, may need approval')
145
+ .option('--name <snapshot>', 'snapshot name')
146
+ .option('--reason <text>', 'why (recorded as _reason on the call)')
147
+ .action(run(async (exec, target, options) => {
148
+ const { vm: v, result } = await vmSnapshot(exec, target, { name: options.name, reason: options.reason });
149
+ emit({ vm: v, result }, () => { printInfo(`snapshot ${v.name || v.vmid} (vmid ${v.vmid})`); console.log(textOf(result)); });
150
+ }));
151
+
152
+ vm.command('rehearse <target> <command...>')
153
+ .description('Rehearse a command on a network-isolated twin of a VM (snapshot, full clone, health checks); slow, writes a snapshot and a VM. Put the command after --')
154
+ .option('--check <spec>', 'health check before/after: url:http://127.0.0.1:PORT/path | unit:NAME | cmd:COMMAND (repeatable)', (v, acc) => [...acc, v], [])
155
+ .option('--keep-twin', 'leave the stopped, isolated twin in place')
156
+ .addOption(new Option('--poll-ms <ms>', 'status poll interval in milliseconds (default 10000)').hideHelp())
157
+ .option('--no-wait', 'print the rehearsal id and return; do not wait for the verdict')
158
+ .action(run(async (exec, target, commandWords, options) => {
159
+ const { vm: v, result: started } = await vmRehearse(exec, target, commandWords.join(' '), { checks: options.check, keepTwin: !!options.keepTwin });
160
+ const id = started?.rehearsal_id;
161
+ if (options.wait === false || !id) {
162
+ emit({ vm: v, result: started }, () => { printInfo(`rehearsal of ${v.name || v.vmid} started${id ? `: ${id}` : ''}`); console.log(textOf(started)); });
163
+ return;
164
+ }
165
+ if (started?.state === 'awaiting_approval') {
166
+ if (!json()) printWarning(`${started.hint} The approval card lists every host command; approving it starts the run.`);
167
+ if (!json()) printInfo(`waiting for approval and the run (rehearsal ${id}); Ctrl-C is safe, check later with the proxmox_rehearsal_status tool`);
168
+ } else if (!json()) printInfo(`rehearsal ${id} of ${v.name || v.vmid} (vmid ${v.vmid} on ${v.node}) running; this takes minutes (a full disk copy)`);
169
+ const result = await waitRehearsal(exec, id, { intervalMs: Number(options.pollMs) || undefined });
170
+ emit({ vm: v, result }, () => {
171
+ printInfo(`rehearsal of ${v.name || v.vmid} (vmid ${v.vmid} on ${v.node}): ${String(result?.verdict || 'unknown').toUpperCase()} id ${id}`);
172
+ if (result?.error) printWarning(result.error);
173
+ console.log(textOf(result));
174
+ });
175
+ }));
176
+
177
+ const pool = vm.command('pool').description('VM pool: status, lease, done, config');
178
+
179
+ pool.command('status')
180
+ .description('Pool capacity and current leases')
181
+ .action(run(async (exec) => {
182
+ const p = await poolStatus(makePoolRest({ client }));
183
+ emit(p, () => {
184
+ console.log(textOf(p));
185
+ for (const v of (p && Array.isArray(p.vms) ? p.vms : [])) if (v.ip_stale) console.log(`vmid ${v.vmid}: ${v.ip} (ip may be stale)`);
186
+ });
187
+ }));
188
+
189
+ pool.command('lease')
190
+ .description('Lease a pool VM as you (needs your SSH public key); a warm VM returns at once, a bigger one is built to order (poll pool status)')
191
+ .option('--lessee <name>', 'who is leasing (default $QUOX_SESSION or hostname)')
192
+ .option('--pubkey-file <path>', 'SSH public key file (default ~/.ssh/id_ed25519.pub if present)')
193
+ .option('--cores <n>', 'vCPU cores')
194
+ .option('--memory-gb <n>', 'memory in GB')
195
+ .option('--disk-gb <n>', 'disk in GB')
196
+ .option('--ttl-h <n>', 'hours until the lease expires and the VM is destroyed (default and maximum: the pool lease_ttl_h)')
197
+ .option('--purpose <text>', 'what the VM is for')
198
+ .option('--avx', 'place only on an AVX2 lane (needed for local LLM chat); errors if no lane has avx2, never falls back')
199
+ .option('--node <name>', 'pin the VM to this pool lane (an active, non-denied lane host)')
200
+ .action(run(async (exec, options) => {
201
+ const pubkey = readPubkey(options.pubkeyFile);
202
+ const p = await poolLease(makePoolRest({ client }), {
203
+ lessee: options.lessee, pubkey, cores: options.cores, memoryGb: options.memoryGb,
204
+ diskGb: options.diskGb, ttlH: options.ttlH, purpose: options.purpose, avx: options.avx, node: options.node,
205
+ });
206
+ emit(p, () => { console.log(textOf(p)); for (const line of poolHostKeyLines(p)) console.log(line); });
207
+ }));
208
+
209
+ pool.command('done <vmid>')
210
+ .description('Release a leased pool VM as you; it stops being leasable at once, then is destroyed and the pool re-warms')
211
+ .action(run(async (exec, vmid, options) => {
212
+ const p = await poolDone(makePoolRest({ client }), vmid);
213
+ emit(p, () => console.log(poolDoneLine(p, vmid)));
214
+ }));
215
+
216
+ pool.command('config')
217
+ .description('Configure the pool (JSON settings via --set); org admins only, acting directly')
218
+ .option('--set <json>', 'settings as a JSON object, e.g. {"admin_keys":["ssh-ed25519 AAAA... owner"],"lanes":[{"node":"hyp-01","template_vmid":7630,"avx2":true}]} (omit to send an empty configure)')
219
+ .action(run(async (exec, options) => {
220
+ let input = {};
221
+ if (options.set) {
222
+ try { input = JSON.parse(options.set); } catch { throw new Error('--set must be a JSON object'); }
223
+ if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('--set must be a JSON object');
224
+ }
225
+ const p = await poolConfig(makePoolRest({ client }), input);
226
+ emit(p, () => console.log(textOf(p)));
227
+ }));
228
+ // `quox vm node reboot|shutdown` is intentionally not registered: the
229
+ // proxmox_node_power tool is not built yet, so the CLI must not advertise it.
230
+ }
231
+
232
+ /** One plain line for `vm pool done`: 202 {state:'recycling'} (current servers) or {released:{destroyed}} (older ones). */
233
+ export function poolDoneLine(p, vmid) {
234
+ if (p && p.state === 'recycling') return `VM ${p.vmid ?? vmid} is recycling: nobody can lease it now. Destroy and re-warm finish in the background; check with quox vm pool status.`;
235
+ if (p && p.released && p.released.destroyed) return `VM ${p.released.vmid ?? vmid} destroyed; the pool re-warms.`;
236
+ return textOf(p);
237
+ }
@@ -17,6 +17,13 @@ export function friendlyNumbersError(err) {
17
17
  return err;
18
18
  }
19
19
 
20
+ export function parseOnOff(value, flag) {
21
+ const v = String(value).trim().toLowerCase();
22
+ if (['on', 'true', 'yes', '1'].includes(v)) return true;
23
+ if (['off', 'false', 'no', '0'].includes(v)) return false;
24
+ throw new Error(`${flag} must be on or off`);
25
+ }
26
+
20
27
  export function registerVoiceCommands(program, ctx) {
21
28
  const { client, auth } = ctx;
22
29
 
@@ -48,13 +55,37 @@ export function registerVoiceCommands(program, ctx) {
48
55
  .command('set <number>')
49
56
  .description('Bind a number (E.164) to the active org, optionally with a label and policy')
50
57
  .option('--label <text>', 'human label for the number')
51
- .option('--policy <json>', 'policy as a JSON object')
58
+ .option('--policy <json>', 'policy as a JSON object (merged over the current policy)')
59
+ .option('--brand <text>', 'policy: brand name the line answers as')
60
+ .option('--opener-name <text>', 'policy: name used in the call opener')
61
+ .option('--ivr <on|off>', 'policy: IVR menu on or off')
62
+ .option('--voice <text>', 'policy: voice to speak with')
63
+ .option('--language <text>', 'policy: language')
64
+ .option('--persona <text>', 'policy: persona')
65
+ .option('--verbosity <quiet|verbose>', 'policy: how chatty the agent is')
52
66
  .action(async (number, options) => {
53
67
  auth.requireAuth();
54
- const body = { org_id: resolveOrgId(auth, program) };
68
+ const orgId = resolveOrgId(auth, program);
69
+ const body = { org_id: orgId };
55
70
  if (options.label !== undefined) body.label = options.label;
71
+ const flagPolicy = {};
72
+ if (options.brand !== undefined) flagPolicy.brand = options.brand;
73
+ if (options.openerName !== undefined) flagPolicy.opener_name = options.openerName;
74
+ if (options.ivr !== undefined) flagPolicy.ivr = parseOnOff(options.ivr, '--ivr');
75
+ if (options.voice !== undefined) flagPolicy.voice = options.voice;
76
+ if (options.language !== undefined) flagPolicy.language = options.language;
77
+ if (options.persona !== undefined) flagPolicy.persona = options.persona;
78
+ if (options.verbosity !== undefined) flagPolicy.verbosity = options.verbosity;
79
+ let jsonPolicy;
56
80
  if (options.policy !== undefined) {
57
- try { body.policy = JSON.parse(options.policy); } catch { throw new Error('--policy must be valid JSON'); }
81
+ try { jsonPolicy = JSON.parse(options.policy); } catch { throw new Error('--policy must be valid JSON'); }
82
+ }
83
+ if (jsonPolicy !== undefined || Object.keys(flagPolicy).length > 0) {
84
+ // Read-modify-write so one flag never clobbers the other policy keys.
85
+ const lep = resolveEndpoint('telephony.numbers.list');
86
+ const cur = await client.get(lep.path, { service: lep.service, query: { org_id: orgId } });
87
+ const existing = ((cur && cur.numbers) || []).find((n) => n.number === number);
88
+ body.policy = { ...((existing && existing.policy) || {}), ...(jsonPolicy || {}), ...flagPolicy };
58
89
  }
59
90
  const ep = resolveEndpoint('telephony.numbers.set', { number });
60
91
  let data;
@@ -27,6 +27,23 @@ function unwrapVoltBundle(data) {
27
27
  return bundle;
28
28
  }
29
29
 
30
+ /**
31
+ * quox#935: a bundle is ONE JSON object carrying `manifest` and/or
32
+ * `events_ndjson`. An events NDJSON file also starts with '{' on every line,
33
+ * so a first-character check took every NDJSON file for a bundle and the
34
+ * whole-file JSON.parse failed. Decide by parsing instead.
35
+ */
36
+ export function looksLikeBundle(raw) {
37
+ try {
38
+ const p = JSON.parse(raw);
39
+ return !!p && typeof p === 'object' && !Array.isArray(p) && ('manifest' in p || 'events_ndjson' in p);
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ const ndjsonEvents = (raw) => raw.trim().split('\n').filter((l) => l.trim()).map((l) => JSON.parse(l));
46
+
30
47
  export function registerVoltCommands(program, ctx = {}) {
31
48
  const { client, auth } = ctx;
32
49
 
@@ -47,7 +64,7 @@ export function registerVoltCommands(program, ctx = {}) {
47
64
  const raw = await readFile(file, 'utf8');
48
65
 
49
66
  // Detect if this is a bundle (JSON with manifest) or raw NDJSON
50
- const isBundle = raw.trimStart().startsWith('{');
67
+ const isBundle = looksLikeBundle(raw);
51
68
 
52
69
  if (isBundle) {
53
70
  // fleet #447: a persisted per-run bundle can be manifest_only (no
@@ -190,7 +207,7 @@ export function registerVoltCommands(program, ctx = {}) {
190
207
  .option('--manifest', 'show bundle manifest only')
191
208
  .action(async (file, options) => {
192
209
  const raw = await readFile(file, 'utf8');
193
- const isBundle = raw.trimStart().startsWith('{');
210
+ const isBundle = looksLikeBundle(raw);
194
211
 
195
212
  let events;
196
213
  let manifest = null;
@@ -432,9 +449,34 @@ export function registerVoltCommands(program, ctx = {}) {
432
449
  volt
433
450
  .command('summary')
434
451
  .description('Quick summary of a bundle (no verification)')
435
- .argument('<file>', 'path to bundle JSON file')
452
+ .argument('<file>', 'path to bundle JSON or events NDJSON file')
436
453
  .action(async (file) => {
437
454
  const raw = await readFile(file, 'utf8');
455
+ // quox#935: an events NDJSON file gets a chain summary instead of a crash.
456
+ if (!looksLikeBundle(raw)) {
457
+ let evs;
458
+ try { evs = ndjsonEvents(raw); } catch {
459
+ printWarning('File is neither a VOLT evidence bundle nor an events NDJSON file');
460
+ process.exitCode = 1;
461
+ return;
462
+ }
463
+ const counts = {};
464
+ for (const ev of evs) counts[ev.event_type] = (counts[ev.event_type] || 0) + 1;
465
+ console.log('');
466
+ console.log(chalk.bold(`VOLT NDJSON Chain Summary (${evs.length} events)`));
467
+ console.log(chalk.gray('─'.repeat(40)));
468
+ console.log(` Run ID: ${chalk.cyan(evs[0]?.run_id || 'N/A')}`);
469
+ console.log(` Events: ${evs.length} events`);
470
+ if (Object.keys(counts).length > 0) {
471
+ console.log('');
472
+ console.log(chalk.bold(' Event Types:'));
473
+ for (const [type, count] of Object.entries(counts).sort((a, b) => b[1] - a[1])) {
474
+ console.log(` ${String(count).padStart(4)} ${type}`);
475
+ }
476
+ }
477
+ console.log('');
478
+ return;
479
+ }
438
480
  const parsed = JSON.parse(raw);
439
481
 
440
482
  if (!parsed.manifest) {
@@ -0,0 +1,32 @@
1
+ /** `quox approvals walkthrough` and `quox inbox walkthrough`: the waiting questions, one card at a time. */
2
+ import { createInterface } from 'node:readline';
3
+ import { resolveEndpoint } from '../api/endpoints.js';
4
+ import { resolveOrgId } from './helpers.js';
5
+ import { runWalkthrough } from '../core/walkthrough.js';
6
+
7
+ export function attachWalkthrough(parent, { client, auth }, program) {
8
+ parent
9
+ .command('walkthrough')
10
+ .description('Walk through the questions waiting for you one at a time: what it does, why, my advice, then your choice')
11
+ .action(async () => {
12
+ auth.requireAuth();
13
+ const orgId = resolveOrgId(auth, program);
14
+ const lp = resolveEndpoint('inbox.list', { orgId });
15
+ const data = await client.get(lp.path, { service: lp.service, query: { status: 'open', limit: 200 } });
16
+ const items = (data && (data.items || data)) || [];
17
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
18
+ const lines = []; let waiting = null; let closed = false;
19
+ rl.on('line', (l) => { if (waiting) { const w = waiting; waiting = null; w(l); } else lines.push(l); });
20
+ rl.on('close', () => { closed = true; if (waiting) { const w = waiting; waiting = null; w(null); } });
21
+ const readLine = (prompt) => new Promise((res) => { if (lines.length) return res(lines.shift()); if (closed) return res(null); process.stdout.write(prompt); waiting = res; });
22
+ try {
23
+ await runWalkthrough(Array.isArray(items) ? items : [], {
24
+ readLine, write: (l) => console.log(l),
25
+ answer: async (it, key, note) => {
26
+ const ep = resolveEndpoint('inbox.respond', { orgId, itemId: it.id });
27
+ await client.post(ep.path, { service: ep.service, body: { action: 'answer', option: key, ...(note ? { comment: String(note).slice(0, 4000) } : {}) } });
28
+ },
29
+ });
30
+ } finally { rl.close(); }
31
+ });
32
+ }
@@ -7,6 +7,50 @@
7
7
 
8
8
  import chalk from 'chalk';
9
9
  import { resolveEndpoint } from '../api/endpoints.js';
10
+ import { joinServiceUrl } from '../core/config.js';
11
+
12
+ /** Seconds to a short "1d 1h" / "3h 12m" / "45s" label. */
13
+ function shortUptime(sec) {
14
+ if (typeof sec !== 'number' || !Number.isFinite(sec)) return '-';
15
+ const d = Math.floor(sec / 86400); const h = Math.floor((sec % 86400) / 3600); const m = Math.floor((sec % 3600) / 60);
16
+ if (d) return `${d}d ${h}h`;
17
+ if (h) return `${h}h ${m}m`;
18
+ return m ? `${m}m` : `${Math.floor(sec)}s`;
19
+ }
20
+
21
+ /**
22
+ * quox#946: one row per agent from GET /api/v1/agents (the same source as
23
+ * `quox fleet agents`). /api/v1/fleet/status carries totals only, which is why
24
+ * the old dashboard always said "No agents reporting".
25
+ */
26
+ export function fleetRows(data) {
27
+ const list = Array.isArray(data) ? data : (Array.isArray(data?.agents) ? data.agents : []);
28
+ return list.map((a) => ({
29
+ host_id: a.host_id || a.system?.hostname || '-',
30
+ status: typeof a.status === 'object' ? (a.status?.health ?? a.status?.connection ?? 'unknown') : (a.status || 'unknown'),
31
+ cpu_percent: Math.round(a.resources?.cpu?.usage_percent ?? a.cpu_percent ?? 0),
32
+ memory_percent: Math.round(a.resources?.memory?.usage_percent ?? a.memory_percent ?? 0),
33
+ uptime: shortUptime(a.system?.uptime_seconds ?? a.uptime_seconds),
34
+ version: a.agent?.version || a.version || '-',
35
+ last_seen_seconds: typeof a.status?.last_seen_seconds === 'number' ? a.status.last_seen_seconds : null,
36
+ }));
37
+ }
38
+
39
+ /**
40
+ * quox#946: a slow service is a TIMEOUT, a refused connection is DOWN, a non-2xx
41
+ * answer is DEGRADED. The old dashboard called every failure "down".
42
+ */
43
+ export async function probeService(url, fetchImpl = fetch, timeoutMs = 8000) {
44
+ const start = Date.now();
45
+ try {
46
+ const res = await fetchImpl(url, { signal: AbortSignal.timeout(timeoutMs) });
47
+ const latency_ms = Date.now() - start;
48
+ return res.ok ? { status: 'healthy', latency_ms, code: res.status } : { status: 'degraded', latency_ms, code: res.status };
49
+ } catch (err) {
50
+ if (err?.name === 'TimeoutError' || err?.name === 'AbortError') return { status: 'timeout', latency_ms: Date.now() - start };
51
+ return { status: 'down', reason: err?.cause?.code || err?.message || 'error' };
52
+ }
53
+ }
10
54
 
11
55
  export function registerWatchCommands(program, { client, auth, config }) {
12
56
  const watch = program
@@ -20,13 +64,22 @@ export function registerWatchCommands(program, { client, auth, config }) {
20
64
  .action(async (opts) => {
21
65
  auth.requireAuth();
22
66
  const interval = Math.max(2, parseInt(opts.interval, 10) || 5) * 1000;
67
+ const fetchAgents = async () => {
68
+ const ep = resolveEndpoint('v1.agents.list');
69
+ return fleetRows(await client.get(ep.path, { service: ep.service }));
70
+ };
71
+
72
+ // --json: one snapshot, then exit (a dashboard loop is not machine output).
73
+ if (program.opts().output === 'json') {
74
+ console.log(JSON.stringify({ agents: await fetchAgents(), timestamp: new Date().toISOString() }, null, 2));
75
+ return;
76
+ }
23
77
 
24
78
  console.log(chalk.cyan('Fleet Watch') + chalk.dim(' (Ctrl+C to stop)\n'));
25
79
 
26
80
  const tick = async () => {
27
81
  try {
28
- const data = await client.get(resolveEndpoint('v1.fleet.status.list').path, { service: resolveEndpoint('v1.fleet.status.list').service });
29
- const agents = data.agents || [];
82
+ const agents = await fetchAgents();
30
83
 
31
84
  // Clear screen and redraw
32
85
  process.stdout.write('\x1B[2J\x1B[H');
@@ -50,7 +103,7 @@ export function registerWatchCommands(program, { client, auth, config }) {
50
103
  `${statusColor(String(a.status || 'unknown').padEnd(10))} ` +
51
104
  `${cpuColor(String((a.cpu_percent || 0) + '%').padEnd(7))} ` +
52
105
  `${memColor(String((a.memory_percent || 0) + '%').padEnd(7))} ` +
53
- `${String(a.uptime || '-').padEnd(10)} ` +
106
+ `${String(a.uptime).padEnd(10)} ` +
54
107
  `${chalk.dim(a.version || '-')}`
55
108
  );
56
109
  }
@@ -82,29 +135,28 @@ export function registerWatchCommands(program, { client, auth, config }) {
82
135
  { name: 'orchestrator', port: 3100, path: '/health' },
83
136
  ];
84
137
 
138
+ const probeAll = async () => Promise.all(services.map(async (svc) => ({
139
+ name: svc.name,
140
+ ...(await probeService(joinServiceUrl(config.getServiceUrl(svc.name), svc.path).href)),
141
+ })));
142
+
143
+ // --json: one snapshot, then exit.
144
+ if (program.opts().output === 'json') {
145
+ console.log(JSON.stringify({ services: await probeAll(), timestamp: new Date().toISOString() }, null, 2));
146
+ return;
147
+ }
148
+
85
149
  const tick = async () => {
150
+ const results = await probeAll();
86
151
  process.stdout.write('\x1B[2J\x1B[H');
87
152
  console.log(chalk.cyan.bold('QuoxCLI Service Watch') + chalk.dim(` ${new Date().toLocaleTimeString()} (Ctrl+C to stop)`));
88
153
  console.log(chalk.dim('─'.repeat(50)));
89
-
90
- for (const svc of services) {
91
- try {
92
- const svcUrl = config.getServiceUrl(svc.name);
93
- const fullUrl = new URL(svc.path, svcUrl);
94
- const start = Date.now();
95
- const res = await fetch(fullUrl.href, { signal: AbortSignal.timeout(5000) });
96
- const latency = Date.now() - start;
97
-
98
- if (res.ok) {
99
- console.log(` ${chalk.green('●')} ${svc.name.padEnd(14)} ${chalk.green('healthy')} ${chalk.dim(latency + 'ms')}`);
100
- } else {
101
- console.log(` ${chalk.yellow('●')} ${svc.name.padEnd(14)} ${chalk.yellow(res.status)} ${chalk.dim(latency + 'ms')}`);
102
- }
103
- } catch {
104
- console.log(` ${chalk.red('●')} ${svc.name.padEnd(14)} ${chalk.red('down')}`);
105
- }
154
+ for (const r of results) {
155
+ if (r.status === 'healthy') console.log(` ${chalk.green('●')} ${r.name.padEnd(14)} ${chalk.green('healthy')} ${chalk.dim(r.latency_ms + 'ms')}`);
156
+ else if (r.status === 'degraded') console.log(` ${chalk.yellow('●')} ${r.name.padEnd(14)} ${chalk.yellow(r.code)} ${chalk.dim(r.latency_ms + 'ms')}`);
157
+ else if (r.status === 'timeout') console.log(` ${chalk.yellow('●')} ${r.name.padEnd(14)} ${chalk.yellow('slow (no answer in ' + Math.round(r.latency_ms / 1000) + 's)')}`);
158
+ else console.log(` ${chalk.red('●')} ${r.name.padEnd(14)} ${chalk.red('down')} ${chalk.dim(r.reason || '')}`);
106
159
  }
107
-
108
160
  console.log(chalk.dim('\n refreshing every ' + (interval / 1000) + 's'));
109
161
  };
110
162
 
@@ -0,0 +1,14 @@
1
+ /**
2
+ * quox welcome: print the greeting from the first screen again. Read only: no server, no login, and it never
3
+ * touches the first-run marker or any config.
4
+ */
5
+ export function registerWelcomeCommand(program) {
6
+ program
7
+ .command('welcome')
8
+ .description('Show the welcome screen again (read only; changes no setup or config)')
9
+ .action(async () => {
10
+ const { INTRO_HEAD, INTRO_LINES } = await import('../quoxon/render.js');
11
+ const { packageVersion } = await import('../quoxon/welcome.js');
12
+ console.log([INTRO_HEAD, ...INTRO_LINES, `${packageVersion()} · ${process.cwd()}`].join('\n'));
13
+ });
14
+ }