quoxcli 0.0.0-stage → 0.3.1

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 (516) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +144 -2
  3. package/bin/quox.js +265 -0
  4. package/bin/quoxon.js +8 -0
  5. package/lib/api/endpoints/admin.js +151 -0
  6. package/lib/api/endpoints/aee.js +38 -0
  7. package/lib/api/endpoints/agent-attach.js +126 -0
  8. package/lib/api/endpoints/agent-schedules.js +26 -0
  9. package/lib/api/endpoints/agents.js +63 -0
  10. package/lib/api/endpoints/assistants.js +62 -0
  11. package/lib/api/endpoints/audit.js +14 -0
  12. package/lib/api/endpoints/auth.js +50 -0
  13. package/lib/api/endpoints/awareness.js +34 -0
  14. package/lib/api/endpoints/away.js +15 -0
  15. package/lib/api/endpoints/backup-schedules.js +20 -0
  16. package/lib/api/endpoints/backups.js +86 -0
  17. package/lib/api/endpoints/brain.js +140 -0
  18. package/lib/api/endpoints/budgets.js +62 -0
  19. package/lib/api/endpoints/calendarq.js +71 -0
  20. package/lib/api/endpoints/catalog.js +14 -0
  21. package/lib/api/endpoints/chat.js +61 -0
  22. package/lib/api/endpoints/consent.js +26 -0
  23. package/lib/api/endpoints/contactsq.js +68 -0
  24. package/lib/api/endpoints/conversations.js +46 -0
  25. package/lib/api/endpoints/custom-roles.js +92 -0
  26. package/lib/api/endpoints/divisions.js +56 -0
  27. package/lib/api/endpoints/domains.js +38 -0
  28. package/lib/api/endpoints/engagement.js +231 -0
  29. package/lib/api/endpoints/engine.js +11 -0
  30. package/lib/api/endpoints/entity.js +50 -0
  31. package/lib/api/endpoints/executions.js +45 -0
  32. package/lib/api/endpoints/fetchAllowlist.js +27 -0
  33. package/lib/api/endpoints/file.js +20 -0
  34. package/lib/api/endpoints/files.js +40 -0
  35. package/lib/api/endpoints/github.js +21 -0
  36. package/lib/api/endpoints/governance.js +43 -0
  37. package/lib/api/endpoints/hat.js +66 -0
  38. package/lib/api/endpoints/inbox.js +52 -0
  39. package/lib/api/endpoints/inboxq.js +120 -0
  40. package/lib/api/endpoints/index.js +242 -0
  41. package/lib/api/endpoints/invites.js +19 -0
  42. package/lib/api/endpoints/llm-policy.js +43 -0
  43. package/lib/api/endpoints/lucid.js +89 -0
  44. package/lib/api/endpoints/members.js +32 -0
  45. package/lib/api/endpoints/memory.js +94 -0
  46. package/lib/api/endpoints/mfa.js +32 -0
  47. package/lib/api/endpoints/mind.js +13 -0
  48. package/lib/api/endpoints/mon.js +184 -0
  49. package/lib/api/endpoints/notesq.js +43 -0
  50. package/lib/api/endpoints/objectives.js +68 -0
  51. package/lib/api/endpoints/orgs.js +38 -0
  52. package/lib/api/endpoints/plugins.js +152 -0
  53. package/lib/api/endpoints/powers.js +75 -0
  54. package/lib/api/endpoints/presence.js +33 -0
  55. package/lib/api/endpoints/prompts.js +69 -0
  56. package/lib/api/endpoints/qlarity.js +122 -0
  57. package/lib/api/endpoints/quoxbox.js +55 -0
  58. package/lib/api/endpoints/quoxchat.js +54 -0
  59. package/lib/api/endpoints/quoxflow.js +32 -0
  60. package/lib/api/endpoints/quoxplan.js +121 -0
  61. package/lib/api/endpoints/quoxseo.js +114 -0
  62. package/lib/api/endpoints/reach.js +47 -0
  63. package/lib/api/endpoints/router.js +17 -0
  64. package/lib/api/endpoints/runs.js +38 -0
  65. package/lib/api/endpoints/safety.js +53 -0
  66. package/lib/api/endpoints/scim.js +46 -0
  67. package/lib/api/endpoints/scopes.js +14 -0
  68. package/lib/api/endpoints/security.js +69 -0
  69. package/lib/api/endpoints/setup.js +90 -0
  70. package/lib/api/endpoints/skills.js +200 -0
  71. package/lib/api/endpoints/sso.js +67 -0
  72. package/lib/api/endpoints/stepup.js +20 -0
  73. package/lib/api/endpoints/tags.js +32 -0
  74. package/lib/api/endpoints/teams.js +56 -0
  75. package/lib/api/endpoints/telephony.js +46 -0
  76. package/lib/api/endpoints/tg.js +56 -0
  77. package/lib/api/endpoints/tools.js +50 -0
  78. package/lib/api/endpoints/traces.js +157 -0
  79. package/lib/api/endpoints/triggers.js +96 -0
  80. package/lib/api/endpoints/user-library.js +41 -0
  81. package/lib/api/endpoints/users.js +20 -0
  82. package/lib/api/endpoints/v1.js +585 -0
  83. package/lib/api/endpoints/vault.js +192 -0
  84. package/lib/api/endpoints/voice.js +22 -0
  85. package/lib/api/endpoints/volt.js +84 -0
  86. package/lib/api/endpoints/ward.js +47 -0
  87. package/lib/api/endpoints/webhooks.js +46 -0
  88. package/lib/api/endpoints/workflows.js +169 -0
  89. package/lib/api/endpoints/x.js +32 -0
  90. package/lib/api/endpoints.js +15 -0
  91. package/lib/commands/actions.js +61 -0
  92. package/lib/commands/admin.js +375 -0
  93. package/lib/commands/aee.js +99 -0
  94. package/lib/commands/agent.js +337 -0
  95. package/lib/commands/agentAttach.js +1528 -0
  96. package/lib/commands/approvals.js +119 -0
  97. package/lib/commands/aside.js +24 -0
  98. package/lib/commands/ask.js +144 -0
  99. package/lib/commands/assistant.js +169 -0
  100. package/lib/commands/auth.js +464 -0
  101. package/lib/commands/awareness.js +88 -0
  102. package/lib/commands/backup.js +377 -0
  103. package/lib/commands/brain.js +1085 -0
  104. package/lib/commands/budget.js +241 -0
  105. package/lib/commands/build.js +170 -0
  106. package/lib/commands/calendar.js +216 -0
  107. package/lib/commands/capabilities.js +34 -0
  108. package/lib/commands/chat.js +295 -0
  109. package/lib/commands/claude.js +380 -0
  110. package/lib/commands/config-cmd.js +106 -0
  111. package/lib/commands/consent.js +84 -0
  112. package/lib/commands/contacts.js +1016 -0
  113. package/lib/commands/contactsSelftest.js +132 -0
  114. package/lib/commands/conversation.js +105 -0
  115. package/lib/commands/dashboard.js +43 -0
  116. package/lib/commands/department.js +96 -0
  117. package/lib/commands/dev.js +620 -0
  118. package/lib/commands/devResident.js +326 -0
  119. package/lib/commands/devSupervisor.js +1062 -0
  120. package/lib/commands/discord.js +132 -0
  121. package/lib/commands/doctor.js +181 -0
  122. package/lib/commands/domain.js +114 -0
  123. package/lib/commands/done.js +347 -0
  124. package/lib/commands/engagement.js +597 -0
  125. package/lib/commands/entity.js +119 -0
  126. package/lib/commands/facebook.js +212 -0
  127. package/lib/commands/fetchAllowlist.js +71 -0
  128. package/lib/commands/file.js +165 -0
  129. package/lib/commands/fleet.js +1189 -0
  130. package/lib/commands/governance.js +647 -0
  131. package/lib/commands/gripe.js +355 -0
  132. package/lib/commands/hat.js +374 -0
  133. package/lib/commands/helpers.js +96 -0
  134. package/lib/commands/hyperion.js +58 -0
  135. package/lib/commands/inbox.js +173 -0
  136. package/lib/commands/instagram.js +264 -0
  137. package/lib/commands/integration.js +100 -0
  138. package/lib/commands/inventory.js +115 -0
  139. package/lib/commands/invites.js +45 -0
  140. package/lib/commands/licence.js +139 -0
  141. package/lib/commands/linkedin.js +178 -0
  142. package/lib/commands/llm-policy.js +117 -0
  143. package/lib/commands/llm.js +45 -0
  144. package/lib/commands/lucid.js +597 -0
  145. package/lib/commands/mail.js +531 -0
  146. package/lib/commands/memory.js +361 -0
  147. package/lib/commands/mind.js +71 -0
  148. package/lib/commands/mon.js +581 -0
  149. package/lib/commands/monitoring.js +86 -0
  150. package/lib/commands/motion.js +22 -0
  151. package/lib/commands/notes.js +1246 -0
  152. package/lib/commands/notesBatch.js +160 -0
  153. package/lib/commands/notesBridges.js +56 -0
  154. package/lib/commands/notesLinks.js +60 -0
  155. package/lib/commands/notification.js +57 -0
  156. package/lib/commands/nucleus.js +123 -0
  157. package/lib/commands/objective.js +240 -0
  158. package/lib/commands/offload.js +89 -0
  159. package/lib/commands/onboard.js +329 -0
  160. package/lib/commands/open.js +56 -0
  161. package/lib/commands/org.js +276 -0
  162. package/lib/commands/peek.js +28 -0
  163. package/lib/commands/plan.js +944 -0
  164. package/lib/commands/plugin-doctor.js +1091 -0
  165. package/lib/commands/plugin.js +649 -0
  166. package/lib/commands/policy.js +78 -0
  167. package/lib/commands/powers.js +296 -0
  168. package/lib/commands/presence.js +89 -0
  169. package/lib/commands/prompt.js +350 -0
  170. package/lib/commands/provider.js +159 -0
  171. package/lib/commands/qlarity.js +706 -0
  172. package/lib/commands/quoxbox.js +270 -0
  173. package/lib/commands/quoxchat.js +275 -0
  174. package/lib/commands/quoxseo.js +378 -0
  175. package/lib/commands/reach.js +159 -0
  176. package/lib/commands/role.js +286 -0
  177. package/lib/commands/router.js +77 -0
  178. package/lib/commands/rows.js +358 -0
  179. package/lib/commands/run.js +187 -0
  180. package/lib/commands/safety.js +107 -0
  181. package/lib/commands/scim.js +88 -0
  182. package/lib/commands/scope.js +58 -0
  183. package/lib/commands/security.js +157 -0
  184. package/lib/commands/server.js +292 -0
  185. package/lib/commands/service.js +89 -0
  186. package/lib/commands/settings.js +99 -0
  187. package/lib/commands/setup.js +569 -0
  188. package/lib/commands/skillManifest.js +113 -0
  189. package/lib/commands/skillRightsFormat.js +38 -0
  190. package/lib/commands/skills.js +687 -0
  191. package/lib/commands/slot.js +1451 -0
  192. package/lib/commands/sso.js +229 -0
  193. package/lib/commands/status.js +137 -0
  194. package/lib/commands/tag.js +91 -0
  195. package/lib/commands/team.js +142 -0
  196. package/lib/commands/tg.js +240 -0
  197. package/lib/commands/tiktok.js +218 -0
  198. package/lib/commands/tips.js +23 -0
  199. package/lib/commands/tool.js +304 -0
  200. package/lib/commands/trace.js +530 -0
  201. package/lib/commands/trigger.js +223 -0
  202. package/lib/commands/update.js +46 -0
  203. package/lib/commands/vault.js +1447 -0
  204. package/lib/commands/vm.js +237 -0
  205. package/lib/commands/voice.js +213 -0
  206. package/lib/commands/volt.js +570 -0
  207. package/lib/commands/walkthrough.js +32 -0
  208. package/lib/commands/watch.js +167 -0
  209. package/lib/commands/webhook.js +137 -0
  210. package/lib/commands/welcome.js +14 -0
  211. package/lib/commands/workflow.js +660 -0
  212. package/lib/commands/x.js +448 -0
  213. package/lib/core/actingAs.js +136 -0
  214. package/lib/core/activeSummary.js +186 -0
  215. package/lib/core/aee.js +53 -0
  216. package/lib/core/agentAlias.js +163 -0
  217. package/lib/core/approvalContext.js +61 -0
  218. package/lib/core/aside.js +54 -0
  219. package/lib/core/askDecision.js +60 -0
  220. package/lib/core/auth.js +501 -0
  221. package/lib/core/banner.js +143 -0
  222. package/lib/core/brainPrime.js +90 -0
  223. package/lib/core/callerSource.js +129 -0
  224. package/lib/core/claudeLs.js +94 -0
  225. package/lib/core/cliBuild.js +62 -0
  226. package/lib/core/client.js +693 -0
  227. package/lib/core/commandRegistrars.js +121 -0
  228. package/lib/core/completions.js +163 -0
  229. package/lib/core/config.js +669 -0
  230. package/lib/core/confirm.js +37 -0
  231. package/lib/core/customHats.js +168 -0
  232. package/lib/core/deeplinks.js +76 -0
  233. package/lib/core/doneEngineFetch.js +227 -0
  234. package/lib/core/errors.js +441 -0
  235. package/lib/core/global-flags.js +190 -0
  236. package/lib/core/governedSession.js +402 -0
  237. package/lib/core/hyperion.js +102 -0
  238. package/lib/core/inboxResolution.js +65 -0
  239. package/lib/core/lazyCommands.js +122 -0
  240. package/lib/core/liveSessionsReport.js +112 -0
  241. package/lib/core/offload.js +504 -0
  242. package/lib/core/org-override.js +52 -0
  243. package/lib/core/orgResolve.js +70 -0
  244. package/lib/core/output.js +239 -0
  245. package/lib/core/peek.js +126 -0
  246. package/lib/core/prodEnv.js +7 -0
  247. package/lib/core/profileUsers.js +51 -0
  248. package/lib/core/promptCancel.js +18 -0
  249. package/lib/core/quoxglyph.js +158 -0
  250. package/lib/core/repoPath.js +11 -0
  251. package/lib/core/residentAlarm.js +282 -0
  252. package/lib/core/residentAuth.js +243 -0
  253. package/lib/core/residentPump.js +229 -0
  254. package/lib/core/residentSeed.js +106 -0
  255. package/lib/core/rootOptions.js +39 -0
  256. package/lib/core/router.js +168 -0
  257. package/lib/core/selfUpdate.js +188 -0
  258. package/lib/core/sessionHeartbeat.js +192 -0
  259. package/lib/core/sessionsOverview.js +224 -0
  260. package/lib/core/slotKeeper.js +527 -0
  261. package/lib/core/slotWake.js +120 -0
  262. package/lib/core/supervisorEngine.js +933 -0
  263. package/lib/core/systemdInstaller.js +256 -0
  264. package/lib/core/version.js +87 -0
  265. package/lib/core/vm.js +361 -0
  266. package/lib/core/vmTui.js +86 -0
  267. package/lib/core/walkthrough.js +35 -0
  268. package/lib/hyperion/deck.js +29 -0
  269. package/lib/lucid/LucidCard.jsx +68 -0
  270. package/lib/lucid/LucidIntro.jsx +92 -0
  271. package/lib/lucid/blind.js +71 -0
  272. package/lib/lucid/cards.js +86 -0
  273. package/lib/lucid/deck.js +32 -0
  274. package/lib/lucid/fmt.js +139 -0
  275. package/lib/lucid/markdown.js +81 -0
  276. package/lib/lucid/motion.js +56 -0
  277. package/lib/lucid/sitrep.js +173 -0
  278. package/lib/plugins/loader.js +93 -0
  279. package/lib/quoxon/activity.js +116 -0
  280. package/lib/quoxon/aocl.js +117 -0
  281. package/lib/quoxon/api.js +952 -0
  282. package/lib/quoxon/app.js +4032 -0
  283. package/lib/quoxon/approvalFacts.js +94 -0
  284. package/lib/quoxon/approvalsView.js +325 -0
  285. package/lib/quoxon/asideView.js +40 -0
  286. package/lib/quoxon/ask.js +121 -0
  287. package/lib/quoxon/away.js +52 -0
  288. package/lib/quoxon/bang.js +224 -0
  289. package/lib/quoxon/bulkRisk.js +17 -0
  290. package/lib/quoxon/capabilities.js +82 -0
  291. package/lib/quoxon/cells.js +53 -0
  292. package/lib/quoxon/commandShort.js +112 -0
  293. package/lib/quoxon/commands.js +479 -0
  294. package/lib/quoxon/connect.js +654 -0
  295. package/lib/quoxon/connectCard.js +45 -0
  296. package/lib/quoxon/consent.js +86 -0
  297. package/lib/quoxon/context.js +68 -0
  298. package/lib/quoxon/copy.js +34 -0
  299. package/lib/quoxon/countdownLine.js +11 -0
  300. package/lib/quoxon/demo.autoplay.js +29 -0
  301. package/lib/quoxon/demo.fixtures.js +24 -0
  302. package/lib/quoxon/demo.js +76 -0
  303. package/lib/quoxon/devObserver.js +20 -0
  304. package/lib/quoxon/dividerAocl.js +191 -0
  305. package/lib/quoxon/dividerRule.js +57 -0
  306. package/lib/quoxon/dragonModes.js +150 -0
  307. package/lib/quoxon/dragonView.js +41 -0
  308. package/lib/quoxon/earlyInput.js +40 -0
  309. package/lib/quoxon/echo.js +15 -0
  310. package/lib/quoxon/egg.js +36 -0
  311. package/lib/quoxon/events.js +677 -0
  312. package/lib/quoxon/feedback.js +50 -0
  313. package/lib/quoxon/gallery.js +237 -0
  314. package/lib/quoxon/handoffOffer.js +78 -0
  315. package/lib/quoxon/hatSwitch.js +37 -0
  316. package/lib/quoxon/headless.js +161 -0
  317. package/lib/quoxon/hostUrls.js +41 -0
  318. package/lib/quoxon/indicator.js +197 -0
  319. package/lib/quoxon/intent.js +52 -0
  320. package/lib/quoxon/keys.js +164 -0
  321. package/lib/quoxon/launcher.js +103 -0
  322. package/lib/quoxon/main.js +176 -0
  323. package/lib/quoxon/markdown.js +341 -0
  324. package/lib/quoxon/markdownSettle.js +19 -0
  325. package/lib/quoxon/matrixDomains.json +46 -0
  326. package/lib/quoxon/mentions.js +90 -0
  327. package/lib/quoxon/mind.js +103 -0
  328. package/lib/quoxon/mode.js +46 -0
  329. package/lib/quoxon/model.js +311 -0
  330. package/lib/quoxon/modelPrefs.js +345 -0
  331. package/lib/quoxon/motion/clock.js +70 -0
  332. package/lib/quoxon/motion/color.js +35 -0
  333. package/lib/quoxon/motion/frame.js +14 -0
  334. package/lib/quoxon/motion/level.js +91 -0
  335. package/lib/quoxon/motion/reveal.js +54 -0
  336. package/lib/quoxon/motion/ring.js +79 -0
  337. package/lib/quoxon/mouse.js +211 -0
  338. package/lib/quoxon/noai.js +57 -0
  339. package/lib/quoxon/objects/agent.js +61 -0
  340. package/lib/quoxon/objects/away.js +72 -0
  341. package/lib/quoxon/objects/crystal.js +100 -0
  342. package/lib/quoxon/objects/fetch.js +182 -0
  343. package/lib/quoxon/objects/fleet.js +149 -0
  344. package/lib/quoxon/objects/health.js +186 -0
  345. package/lib/quoxon/objects/index.js +106 -0
  346. package/lib/quoxon/objects/ladder.js +71 -0
  347. package/lib/quoxon/objects/memory.js +44 -0
  348. package/lib/quoxon/objects/mirror.js +137 -0
  349. package/lib/quoxon/objects/model.js +118 -0
  350. package/lib/quoxon/objects/peek.js +17 -0
  351. package/lib/quoxon/objects/proof.js +110 -0
  352. package/lib/quoxon/objects/prove.js +130 -0
  353. package/lib/quoxon/objects/replay.js +125 -0
  354. package/lib/quoxon/objects/sitrep.js +58 -0
  355. package/lib/quoxon/objects/skill.js +105 -0
  356. package/lib/quoxon/objects/view.js +87 -0
  357. package/lib/quoxon/objects/workflow.js +119 -0
  358. package/lib/quoxon/observers.js +42 -0
  359. package/lib/quoxon/onboarding/copy.js +263 -0
  360. package/lib/quoxon/onboarding/credential.js +108 -0
  361. package/lib/quoxon/onboarding/detect.js +22 -0
  362. package/lib/quoxon/onboarding/dockerOffer.js +45 -0
  363. package/lib/quoxon/onboarding/failureCause.js +33 -0
  364. package/lib/quoxon/onboarding/finish.js +134 -0
  365. package/lib/quoxon/onboarding/index.js +94 -0
  366. package/lib/quoxon/onboarding/installRunner.js +284 -0
  367. package/lib/quoxon/onboarding/intent.js +53 -0
  368. package/lib/quoxon/onboarding/journey.js +73 -0
  369. package/lib/quoxon/onboarding/preflight.js +156 -0
  370. package/lib/quoxon/onboarding/probe.js +65 -0
  371. package/lib/quoxon/onboarding/runner.js +58 -0
  372. package/lib/quoxon/onboarding/view.js +682 -0
  373. package/lib/quoxon/orgLabel.js +10 -0
  374. package/lib/quoxon/orgSwitch.js +33 -0
  375. package/lib/quoxon/ownBroadcast.js +22 -0
  376. package/lib/quoxon/palette.js +43 -0
  377. package/lib/quoxon/print.js +98 -0
  378. package/lib/quoxon/profileSwitch.js +53 -0
  379. package/lib/quoxon/promptHistory.js +41 -0
  380. package/lib/quoxon/providers.js +48 -0
  381. package/lib/quoxon/qart.js +348 -0
  382. package/lib/quoxon/qdragon.js +410 -0
  383. package/lib/quoxon/qdragonFly.js +61 -0
  384. package/lib/quoxon/qdragonScenes.js +282 -0
  385. package/lib/quoxon/qeyes.js +213 -0
  386. package/lib/quoxon/qglyph.js +143 -0
  387. package/lib/quoxon/qhello.js +58 -0
  388. package/lib/quoxon/qmotion.js +132 -0
  389. package/lib/quoxon/render.js +1167 -0
  390. package/lib/quoxon/resend.js +72 -0
  391. package/lib/quoxon/roster.js +307 -0
  392. package/lib/quoxon/route.js +39 -0
  393. package/lib/quoxon/rows.js +711 -0
  394. package/lib/quoxon/rowsStore.js +80 -0
  395. package/lib/quoxon/rules.js +130 -0
  396. package/lib/quoxon/screenContext.js +150 -0
  397. package/lib/quoxon/screenFrames.js +254 -0
  398. package/lib/quoxon/session.js +238 -0
  399. package/lib/quoxon/sessionDead.js +17 -0
  400. package/lib/quoxon/signin.js +125 -0
  401. package/lib/quoxon/skills.js +30 -0
  402. package/lib/quoxon/slash/agents.js +112 -0
  403. package/lib/quoxon/slash/argSources.js +65 -0
  404. package/lib/quoxon/slash/args.js +65 -0
  405. package/lib/quoxon/slash/automate.js +89 -0
  406. package/lib/quoxon/slash/broadcast.js +140 -0
  407. package/lib/quoxon/slash/broadcastRun.js +108 -0
  408. package/lib/quoxon/slash/card.js +30 -0
  409. package/lib/quoxon/slash/challenge.js +114 -0
  410. package/lib/quoxon/slash/cliCommand.js +53 -0
  411. package/lib/quoxon/slash/cliLeaves.json +948 -0
  412. package/lib/quoxon/slash/compact.js +90 -0
  413. package/lib/quoxon/slash/confirm.js +30 -0
  414. package/lib/quoxon/slash/crystallize.js +69 -0
  415. package/lib/quoxon/slash/dashboard.js +136 -0
  416. package/lib/quoxon/slash/draft.js +54 -0
  417. package/lib/quoxon/slash/hatWalk.js +206 -0
  418. package/lib/quoxon/slash/inverses.js +77 -0
  419. package/lib/quoxon/slash/killer.js +24 -0
  420. package/lib/quoxon/slash/killerApi.js +16 -0
  421. package/lib/quoxon/slash/ledger.js +88 -0
  422. package/lib/quoxon/slash/listView.js +30 -0
  423. package/lib/quoxon/slash/modelCmd.js +261 -0
  424. package/lib/quoxon/slash/palette.js +118 -0
  425. package/lib/quoxon/slash/paletteController.js +169 -0
  426. package/lib/quoxon/slash/panels.js +116 -0
  427. package/lib/quoxon/slash/picker.js +56 -0
  428. package/lib/quoxon/slash/restart.js +202 -0
  429. package/lib/quoxon/slash/resume.js +59 -0
  430. package/lib/quoxon/slash/rowCmd.js +157 -0
  431. package/lib/quoxon/slash/rowWalk.js +175 -0
  432. package/lib/quoxon/slash/settings.js +60 -0
  433. package/lib/quoxon/slash/spawn.js +177 -0
  434. package/lib/quoxon/slash/stepUp.js +274 -0
  435. package/lib/quoxon/slash/stepUpCard.js +60 -0
  436. package/lib/quoxon/slash/stepUpCmds.js +78 -0
  437. package/lib/quoxon/slash/undoApi.js +38 -0
  438. package/lib/quoxon/slash/undoCmd.js +49 -0
  439. package/lib/quoxon/slash/undoWish.js +15 -0
  440. package/lib/quoxon/slash/watch.js +164 -0
  441. package/lib/quoxon/slash/wish.js +23 -0
  442. package/lib/quoxon/slash/wishCmd.js +32 -0
  443. package/lib/quoxon/slotChat.js +300 -0
  444. package/lib/quoxon/slotFormat.js +195 -0
  445. package/lib/quoxon/slotRun.js +131 -0
  446. package/lib/quoxon/sound/index.js +186 -0
  447. package/lib/quoxon/sound/mapping.js +120 -0
  448. package/lib/quoxon/sound/output.js +52 -0
  449. package/lib/quoxon/sound/synth.js +122 -0
  450. package/lib/quoxon/stray.js +22 -0
  451. package/lib/quoxon/suggestCommand.js +59 -0
  452. package/lib/quoxon/term.js +141 -0
  453. package/lib/quoxon/textBuffer.js +416 -0
  454. package/lib/quoxon/theme.js +286 -0
  455. package/lib/quoxon/tips.js +433 -0
  456. package/lib/quoxon/toolError.js +41 -0
  457. package/lib/quoxon/transcript.js +188 -0
  458. package/lib/quoxon/web/qface.js +89 -0
  459. package/lib/quoxon/welcome.js +52 -0
  460. package/lib/quoxon/whatsnew.js +136 -0
  461. package/lib/quoxon/whatsnew.jsonl +89 -0
  462. package/lib/quoxon/windowOrg.js +113 -0
  463. package/lib/server/diag.js +129 -0
  464. package/lib/server/doctor.js +71 -0
  465. package/lib/server/install.js +149 -0
  466. package/lib/server/pluginEnable.js +161 -0
  467. package/lib/server/skew.js +56 -0
  468. package/lib/server/snapshot.js +131 -0
  469. package/lib/server/status.js +128 -0
  470. package/lib/server/sys.js +65 -0
  471. package/lib/server/uninstall.js +63 -0
  472. package/lib/server/update.js +234 -0
  473. package/lib/toolOutputShape.js +315 -0
  474. package/lib/tui/app.jsx +289 -0
  475. package/lib/tui/components/Breadcrumb.jsx +42 -0
  476. package/lib/tui/components/ChatInput.jsx +68 -0
  477. package/lib/tui/components/Confirm.jsx +32 -0
  478. package/lib/tui/components/DataTable.jsx +270 -0
  479. package/lib/tui/components/DetailPanel.jsx +241 -0
  480. package/lib/tui/components/HelpOverlay.jsx +67 -0
  481. package/lib/tui/components/Layout.jsx +129 -0
  482. package/lib/tui/components/Palette.jsx +141 -0
  483. package/lib/tui/components/SearchInput.jsx +125 -0
  484. package/lib/tui/components/Sparkline.jsx +55 -0
  485. package/lib/tui/components/StatusBar.jsx +133 -0
  486. package/lib/tui/components/Toast.jsx +52 -0
  487. package/lib/tui/hooks/useApi.js +44 -0
  488. package/lib/tui/hooks/useHealth.js +90 -0
  489. package/lib/tui/hooks/usePolledApi.js +86 -0
  490. package/lib/tui/hooks/useTerminalSize.js +46 -0
  491. package/lib/tui/index.js +56 -0
  492. package/lib/tui/intro/Intro.jsx +332 -0
  493. package/lib/tui/jsx-loader.js +32 -0
  494. package/lib/tui/keys.js +87 -0
  495. package/lib/tui/registry.js +49 -0
  496. package/lib/tui/theme.js +103 -0
  497. package/lib/tui/views/AdminView.jsx +398 -0
  498. package/lib/tui/views/AgentView.jsx +204 -0
  499. package/lib/tui/views/BackupView.jsx +225 -0
  500. package/lib/tui/views/BrainView.jsx +223 -0
  501. package/lib/tui/views/ChatView.jsx +468 -0
  502. package/lib/tui/views/FilesView.jsx +218 -0
  503. package/lib/tui/views/FleetView.jsx +234 -0
  504. package/lib/tui/views/GovernanceView.jsx +453 -0
  505. package/lib/tui/views/HealthView.jsx +216 -0
  506. package/lib/tui/views/HyperionDeck.jsx +79 -0
  507. package/lib/tui/views/InboxView.jsx +228 -0
  508. package/lib/tui/views/LucidDeck.jsx +719 -0
  509. package/lib/tui/views/MainMenu.jsx +123 -0
  510. package/lib/tui/views/MemoryView.jsx +377 -0
  511. package/lib/tui/views/ObjectivesView.jsx +230 -0
  512. package/lib/tui/views/OrgView.jsx +229 -0
  513. package/lib/tui/views/ToolView.jsx +221 -0
  514. package/lib/tui/views/VaultView.jsx +422 -0
  515. package/lib/tui/views/WorkflowView.jsx +329 -0
  516. package/package.json +75 -4
package/lib/core/vm.js ADDED
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Shared engine behind `quox vm` and the QUOXON `/vm` command: drives the
3
+ * HYPERION (agent id atlas) Proxmox tools through the governed tool route
4
+ * (POST /api/v1/tools/execute). Pure functions over an injected `exec(tool, input)`
5
+ * so the CLI group and the TUI share one implementation and one set of tests.
6
+ *
7
+ * Tool names/inputs are the contract with the server side:
8
+ * proxmox_vms, proxmox_containers, proxmox_vm_resources, proxmox_vm_action,
9
+ * proxmox_vm_snapshot, proxmox_vm_resolve {name|vmid}, proxmox_pool_status,
10
+ * proxmox_pool_lease, proxmox_pool_release, proxmox_pool_configure,
11
+ * proxmox_node_power, proxmox_rehearse {vmid, node, command, checks?, keep_twin?} (starts, returns {rehearsal_id, state:'running'}),
12
+ * proxmox_rehearsal_status {rehearsal_id}.
13
+ */
14
+
15
+ import os from 'node:os';
16
+ import fs from 'node:fs';
17
+ import path from 'node:path';
18
+ import { resolveEndpoint } from '../api/endpoints.js';
19
+
20
+ export const VM_ACTIONS = ['start', 'stop', 'reboot', 'shutdown'];
21
+ export const NODE_ACTIONS = ['reboot', 'shutdown'];
22
+
23
+ /** A write was held for a human. Callers print the hint and never re-submit. */
24
+ export class VmPendingError extends Error {
25
+ constructor(tool, data) {
26
+ super(`${tool} is awaiting approval`);
27
+ this.name = 'VmPendingError';
28
+ this.tool = tool;
29
+ this.approvalId = data?.approvalId || data?.approval_id || data?._approval?.id || null;
30
+ this.inboxItemId = data?.inbox_item_id || null;
31
+ this.rehearsalId = data?.rehearsal_id || null;
32
+ }
33
+ }
34
+
35
+ export function pendingHint(e) {
36
+ const id = e.approvalId || e.inboxItemId;
37
+ if (!id) return `${e.tool} is awaiting approval. Check: quox approvals`;
38
+ return `${e.tool} is awaiting approval (approval id ${id}). After a human approves, run: quox approvals result ${id}`;
39
+ }
40
+
41
+ /**
42
+ * DEVFLEET-READ: HYPERION (atlas) is granted ONE read tool, proxmox_read, not the per-view read
43
+ * tools. Callers keep using the old names; the wire call is proxmox_read with the matching view.
44
+ * Writes (proxmox_vm_action, _snapshot, _rehearse, _vm_doctor, pool lease/release/config) pass through.
45
+ */
46
+ export const PROXMOX_READ_VIEW_OF = {
47
+ proxmox_vms: 'vms', proxmox_containers: 'containers', proxmox_cluster_status: 'cluster',
48
+ proxmox_ha_status: 'ha', proxmox_templates_list: 'templates', proxmox_storage: 'storage',
49
+ proxmox_node_resources: 'node', proxmox_vm_resources: 'vm', proxmox_snapshots_list: 'snapshots',
50
+ proxmox_backup_status: 'backups', proxmox_vm_resolve: 'resolve', proxmox_pool_status: 'pool',
51
+ proxmox_rehearsal_status: 'rehearsal', proxmox_doctor_report: 'doctor_report',
52
+ };
53
+
54
+ // Tools that fan out many Proxmox reads server-side and outlive the client's 30s default.
55
+ // VM Doctor runs 12 reads in parallel; over Bastion pvesh on a slow node that is ~30s.
56
+ export const SLOW_TOOL_TIMEOUT_MS = { proxmox_vm_doctor: 120000, proxmox_snapshots_list: 90000 };
57
+
58
+ /** Build an exec(tool, input) over an API client. Throws VmPendingError on a held call. */
59
+ export function makeExec({ client, orgId }) {
60
+ return async function exec(tool, input = {}) {
61
+ const ep = resolveEndpoint('v1.tools.execute');
62
+ // HYPERION (atlas) owns these tools; without agent_id the call lands on the
63
+ // orchestrator, which is not granted them. An attach token's agent claim still wins server-side.
64
+ const view = PROXMOX_READ_VIEW_OF[tool];
65
+ const body = view ? { tool_name: 'proxmox_read', input: { view, ...input }, agent_id: 'atlas' } : { tool_name: tool, input, agent_id: 'atlas' };
66
+ if (orgId) body.org_id = orgId;
67
+ const timeout = SLOW_TOOL_TIMEOUT_MS[tool];
68
+ let data = await client.post(ep.path, { service: ep.service, body, ...(timeout ? { timeout } : {}) });
69
+ // The caller's own token identity wins over agent_id server-side (e.g. an attached session runs
70
+ // as itself, which holds the legacy reads like proxmox_vms but not proxmox_read). When proxmox_read
71
+ // is not granted to that identity, retry once with the legacy per-view tool it does hold.
72
+ if (view && data && data.success === false && /not granted/i.test(String(data.error || ''))) {
73
+ const legacy = { tool_name: tool, input, agent_id: 'atlas' };
74
+ if (orgId) legacy.org_id = orgId;
75
+ data = await client.post(ep.path, { service: ep.service, body: legacy });
76
+ }
77
+ if (data && data.pending) throw new VmPendingError(tool, data);
78
+ if (data && data.success === false) {
79
+ const err = new Error(`${tool} via POST ${ep.path} returned success:false: ${data.error || 'unknown error'}`);
80
+ err.toolError = data.error || 'unknown error';
81
+ err.tool = tool;
82
+ throw err;
83
+ }
84
+ return data;
85
+ };
86
+ }
87
+
88
+ /** The tool's payload, whatever envelope it came wrapped in. */
89
+ export function payloadOf(data) {
90
+ let p = data;
91
+ if (p && typeof p === 'object' && !Array.isArray(p)) {
92
+ for (const k of ['result', 'output', 'data']) {
93
+ if (p[k] !== undefined && p[k] !== null) { p = p[k]; break; }
94
+ }
95
+ }
96
+ if (typeof p === 'string') { try { return JSON.parse(p); } catch { return p; } }
97
+ return p;
98
+ }
99
+
100
+ function arrayOf(p, ...keys) {
101
+ if (Array.isArray(p)) return p;
102
+ for (const k of [...keys, 'items', 'results', 'resources']) if (Array.isArray(p?.[k])) return p[k];
103
+ return [];
104
+ }
105
+
106
+ const normVm = (v, fallbackType) => ({
107
+ vmid: Number(v.vmid ?? v.id),
108
+ name: v.name ?? v.hostname ?? '',
109
+ node: v.node ?? null,
110
+ type: v.type === 'lxc' || v.type === 'qemu' ? v.type : (fallbackType || 'qemu'),
111
+ status: v.status ?? v.state ?? '',
112
+ raw: v,
113
+ });
114
+
115
+ const isUnknownTool = (e) => e?.status === 404
116
+ || /unknown tool|tool .*not (found|available|registered)|no such tool|unsupported tool|not implemented/i.test(e?.toolError || e?.message || '');
117
+
118
+ export async function listVms(exec, { node, type } = {}) {
119
+ if (type && !['qemu', 'lxc'].includes(type)) throw new Error('--type must be qemu or lxc');
120
+ const out = [];
121
+ if (type !== 'lxc') {
122
+ const p = payloadOf(await exec('proxmox_vms', node ? { node } : {}));
123
+ for (const v of arrayOf(p, 'vms')) out.push(normVm(v, 'qemu'));
124
+ }
125
+ if (type === 'lxc') {
126
+ const p = payloadOf(await exec('proxmox_containers', node ? { node } : {}));
127
+ for (const v of arrayOf(p, 'containers', 'lxc')) out.push(normVm(v, 'lxc'));
128
+ }
129
+ return node ? out.filter((v) => !v.node || v.node === node) : out;
130
+ }
131
+
132
+ /**
133
+ * name|vmid -> { vmid, node, type, name }. Tool proxmox_vm_resolve first; if the server
134
+ * does not know it, fall back to client-side exact match over proxmox_vms + proxmox_containers.
135
+ * Ambiguity is an error listing candidates; never a guess.
136
+ */
137
+ export async function resolveTarget(exec, target) {
138
+ const t = String(target ?? '').trim();
139
+ if (!t) throw new Error('a VM name or vmid is required');
140
+ const numeric = /^\d+$/.test(t);
141
+ const input = numeric ? { vmid: Number(t) } : { name: t };
142
+ let matches = null;
143
+ try {
144
+ const p = payloadOf(await exec('proxmox_vm_resolve', input));
145
+ const list = Array.isArray(p) ? p : (p?.matches || p?.candidates || (p && (p.vmid !== undefined || p.id !== undefined) ? [p] : []));
146
+ matches = list.map((v) => normVm(v));
147
+ if (!matches.length) throw new Error(`no VM matches "${t}" (proxmox_vm_resolve)`);
148
+ } catch (e) {
149
+ if (e instanceof VmPendingError || !isUnknownTool(e)) throw e;
150
+ matches = null;
151
+ }
152
+ if (matches === null) {
153
+ const all = await listVms(exec, {});
154
+ try { all.push(...await listVms(exec, { type: 'lxc' })); } catch (e) { if (e instanceof VmPendingError) throw e; /* containers are optional */ }
155
+ matches = all.filter((v) => (numeric ? v.vmid === Number(t) : v.name === t));
156
+ if (!matches.length) throw new Error(`no VM matches "${t}" (checked proxmox_vms and proxmox_containers; exact name match only)`);
157
+ }
158
+ if (matches.length > 1) {
159
+ const lines = matches.map((v) => ` ${v.vmid} ${v.name} ${v.node || '?'} ${v.type}`).join('\n');
160
+ throw new Error(`"${t}" is ambiguous, ${matches.length} candidates (use the vmid):\n${lines}`);
161
+ }
162
+ const m = matches[0];
163
+ if (!m.node) throw new Error(`could not determine the node for "${t}" (vmid ${m.vmid})`);
164
+ return { vmid: m.vmid, node: m.node, type: m.type, name: m.name };
165
+ }
166
+
167
+ const withReason = (input, reason) => (reason ? { ...input, _reason: reason } : input);
168
+
169
+ export async function vmInfo(exec, target) {
170
+ const vm = await resolveTarget(exec, target);
171
+ const data = await exec('proxmox_vm_resources', { vmid: vm.vmid, node: vm.node, type: vm.type });
172
+ return { vm, resources: payloadOf(data) };
173
+ }
174
+
175
+ export async function vmAction(exec, action, target, { reason } = {}) {
176
+ if (!VM_ACTIONS.includes(action)) throw new Error(`action must be one of ${VM_ACTIONS.join(', ')}`);
177
+ const vm = await resolveTarget(exec, target);
178
+ const data = await exec('proxmox_vm_action', withReason({ vmid: vm.vmid, node: vm.node, action, type: vm.type }, reason));
179
+ return { vm, result: payloadOf(data) };
180
+ }
181
+
182
+ export async function vmSnapshot(exec, target, { name, reason } = {}) {
183
+ const vm = await resolveTarget(exec, target);
184
+ // proxmox_vm_snapshot requires snapname (Proxmox: letter first, then [A-Za-z0-9_-]).
185
+ const snapname = name || `quox-${new Date().toISOString().replace(/[-:]/g, '').replace('T', '-').slice(0, 15)}`;
186
+ if (!/^[A-Za-z][A-Za-z0-9_-]{0,39}$/.test(snapname)) throw new Error('snapshot name must start with a letter and use only letters, digits, _ or -');
187
+ const input = { vmid: vm.vmid, node: vm.node, snapname };
188
+ const data = await exec('proxmox_vm_snapshot', withReason(input, reason));
189
+ return { vm, result: payloadOf(data) };
190
+ }
191
+
192
+ /**
193
+ * `url:http://127.0.0.1:8080/health` | `unit:nginx` | `cmd:test -f /etc/x` -> a proxmox_rehearse check.
194
+ * Only the shape is checked here; the server validates the characters strictly.
195
+ */
196
+ export function parseCheck(spec) {
197
+ const m = /^(url|unit|cmd):(.+)$/.exec(String(spec ?? '').trim());
198
+ if (!m) throw new Error(`--check must look like url:http://127.0.0.1:PORT/path, unit:NAME or cmd:COMMAND (got "${spec}")`);
199
+ if (m[1] === 'url') return { type: 'url', url: m[2] };
200
+ if (m[1] === 'unit') return { type: 'unit', name: m[2] };
201
+ return { type: 'cmd', run: m[2] };
202
+ }
203
+
204
+ /** Start a rehearsal on a network-isolated twin of the VM (HYPERION proxmox_rehearse). Returns at once; the run takes minutes (a full disk copy). */
205
+ export async function vmRehearse(exec, target, command, { checks = [], keepTwin = false } = {}) {
206
+ const c = String(command ?? '').trim();
207
+ if (!c) throw new Error('a command is required after --');
208
+ if (c.includes("'")) throw new Error('the command may not contain single quotes (it runs inside bash -c, use double quotes)');
209
+ const vm = await resolveTarget(exec, target);
210
+ const input = { vmid: vm.vmid, node: vm.node, command: c };
211
+ if (checks.length) input.checks = checks.map(parseCheck);
212
+ if (keepTwin) input.keep_twin = true;
213
+ let data;
214
+ try { data = await exec('proxmox_rehearse', input); } catch (e) {
215
+ // The rehearsal needs ONE human approval (the card lists every host command). The proposal exists and
216
+ // the run starts by itself once approved, so hand back the id instead of failing.
217
+ if (e instanceof VmPendingError && e.rehearsalId) {
218
+ return { vm, result: { rehearsal_id: e.rehearsalId, state: 'awaiting_approval', approval_id: e.approvalId, inbox_item_id: e.inboxItemId, hint: pendingHint(e) } };
219
+ }
220
+ throw e;
221
+ }
222
+ return { vm, result: payloadOf(data) };
223
+ }
224
+
225
+ export const REHEARSAL_POLL_MS = 10000;
226
+ export const REHEARSAL_TIMEOUT_MS = 50 * 60 * 1000;
227
+
228
+ /** Poll proxmox_rehearsal_status until the rehearsal is done (awaiting_approval and running both keep waiting). Throws on timeout (the id stays valid for a later look). */
229
+ export async function waitRehearsal(exec, rehearsalId, { intervalMs = REHEARSAL_POLL_MS, timeoutMs = REHEARSAL_TIMEOUT_MS, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), onTick } = {}) {
230
+ if (!/^RH_[0-9A-F]{16}$/.test(String(rehearsalId || ''))) throw new Error('the server did not return a rehearsal id');
231
+ const t0 = Date.now();
232
+ for (;;) {
233
+ const rec = payloadOf(await exec('proxmox_rehearsal_status', { rehearsal_id: rehearsalId }));
234
+ const r = rec?.rehearsal || rec;
235
+ if (r && r.state === 'done') return r;
236
+ if (Date.now() - t0 > timeoutMs) throw new Error(`rehearsal ${rehearsalId} is still running after ${Math.round(timeoutMs / 60000)} minutes; check later with the proxmox_rehearsal_status tool`);
237
+ if (onTick) onTick(r);
238
+ await sleep(intervalMs);
239
+ }
240
+ }
241
+
242
+ /** Human REST surface for the pool. */
243
+ export function makePoolRest({ client }) {
244
+ const call = (name, params, body, timeout) => {
245
+ const ep = resolveEndpoint(name, params);
246
+ const m = ep.method.toLowerCase();
247
+ const t = timeout ? { timeout } : {};
248
+ return m === 'get' ? client.get(ep.path, { service: ep.service, ...t }) : client[m](ep.path, { service: ep.service, body: body || {}, ...t });
249
+ };
250
+ return {
251
+ status: () => call('v1.proxmox.pool'),
252
+ configure: (body) => call('v1.proxmox.pool.config.put', {}, body),
253
+ // A warm lease probes ssh, installs the key and refreshes the IP; a slow auth service stretches that past 30s.
254
+ lease: (body) => call('v1.proxmox.pool.lease', {}, body, 180000),
255
+ release: (vmid) => call('v1.proxmox.pool.release', { vmid }),
256
+ };
257
+ }
258
+
259
+ export async function poolStatus(rest) {
260
+ return rest.status();
261
+ }
262
+
263
+ export function defaultLessee(env = process.env) {
264
+ return env.QUOX_SESSION || os.hostname();
265
+ }
266
+
267
+ /** --pubkey-file, else ~/.ssh/id_ed25519.pub when present, else a clear error. Reads the PUBLIC key only. */
268
+ export function readPubkey(file, { home = os.homedir() } = {}) {
269
+ const f = file || path.join(home, '.ssh', 'id_ed25519.pub');
270
+ if (!file && !fs.existsSync(f)) throw new Error('no ssh public key: pass --pubkey-file <path> (default ~/.ssh/id_ed25519.pub was not found)');
271
+ let txt;
272
+ try { txt = fs.readFileSync(f, 'utf8').trim(); } catch (e) { throw new Error(`cannot read public key ${f}: ${e.message}`); }
273
+ if (!/^(ssh-|ecdsa-|sk-)/.test(txt)) throw new Error(`${f} does not look like an SSH public key (refusing to send it)`);
274
+ return txt;
275
+ }
276
+
277
+ export async function poolLease(rest, { lessee, pubkey, cores, memoryGb, diskGb, ttlH, purpose, avx, node } = {}) {
278
+ const input = { lessee: lessee || defaultLessee(), ssh_pubkey: pubkey };
279
+ if (cores != null) input.cores = Number(cores);
280
+ if (memoryGb != null) input.memory_gb = Number(memoryGb);
281
+ if (diskGb != null) input.disk_gb = Number(diskGb);
282
+ if (ttlH != null) input.ttl_h = Number(ttlH);
283
+ if (purpose) input.purpose = purpose;
284
+ if (avx) input.require_avx2 = true; // set when only some nodes have AVX2; the server errors if no lane can take it
285
+ if (node) input.node = node;
286
+ return rest.lease(input); // 201 {lease} when a warm VM fits, 202 {vmid,state:'building'} when built to order
287
+ }
288
+
289
+ /** Host key lines for a returned lease: the fingerprint to compare, and a ready known_hosts line. Never invents a key. */
290
+ export function poolHostKeyLines(p) {
291
+ const l = p && p.lease;
292
+ if (!l || typeof l !== 'object') return [];
293
+ if (l.host_key_line && l.host_key_fingerprint) {
294
+ return [`Host key ${l.host_key_fingerprint}`, 'known_hosts line:', `${l.ip} ${l.host_key_line}`];
295
+ }
296
+ return [`host key not read: ${l.host_key_error || 'the server returned none'}`];
297
+ }
298
+
299
+ export async function poolDone(rest, vmid) {
300
+ if (!/^\d+$/.test(String(vmid ?? ''))) throw new Error('vmid must be a number');
301
+ return rest.release(Number(vmid));
302
+ }
303
+
304
+ export async function poolConfig(rest, input = {}) {
305
+ return rest.configure(input);
306
+ }
307
+
308
+ export async function nodePower(exec, action, node, { reason } = {}) {
309
+ if (!NODE_ACTIONS.includes(action)) throw new Error(`node action must be one of ${NODE_ACTIONS.join(', ')}`);
310
+ if (!node) throw new Error('a node name is required');
311
+ // The proxmox_node_power tool is not built yet. Fail loudly instead of calling a missing tool.
312
+ void exec; void reason;
313
+ throw new Error('node power (reboot, shutdown) is not built yet; there is no proxmox_node_power tool');
314
+ }
315
+
316
+ /** Plain-text lines for a VM list. */
317
+ export function vmListLines(vms) {
318
+ if (!vms.length) return ['No VMs found.'];
319
+ return vms.map((v) => `${String(v.vmid).padEnd(6)} ${String(v.name).padEnd(28)} ${String(v.node || '?').padEnd(8)} ${String(v.type).padEnd(5)} ${v.status}`);
320
+ }
321
+
322
+ export function textOf(p) {
323
+ if (p === undefined || p === null) return 'done';
324
+ return typeof p === 'string' ? p : JSON.stringify(p, null, 2);
325
+ }
326
+
327
+ /**
328
+ * VM Doctor (DEVFLEET-DOCTOR): read-only diagnosis. The server resolves the name or vmid itself
329
+ * (proxmox_vm_doctor), so the target goes straight through. -> the report object.
330
+ */
331
+ export async function vmDoctor(exec, target) {
332
+ const t = String(target ?? '').trim();
333
+ if (!t) throw new Error('a VM name or vmid is required');
334
+ const p = payloadOf(await exec('proxmox_vm_doctor', { target: t }));
335
+ if (!p || typeof p !== 'object' || !Array.isArray(p.findings)) throw new Error('proxmox_vm_doctor returned no report');
336
+ return p;
337
+ }
338
+
339
+ const SEV_LABEL = { critical: 'CRITICAL', warning: 'WARNING', info: 'INFO' };
340
+ const showVal = (v) => (typeof v === 'number' ? String(Math.round(v * 1000) / 1000) : String(v));
341
+
342
+ /** Human lines for a doctor report: verdict, each finding (cause / evidence / fix), then gaps. */
343
+ export function doctorLines(report) {
344
+ const vm = report.vm || {};
345
+ const lines = [`VERDICT ${String(report.verdict || 'unknown').toUpperCase()} ${vm.name || vm.vmid} (vmid ${vm.vmid} on ${vm.node}, ${vm.status || 'unknown'})`];
346
+ if (!report.findings.length) lines.push('No problems found in what could be read.');
347
+ for (const f of report.findings) {
348
+ lines.push('', `${SEV_LABEL[f.severity] || String(f.severity).toUpperCase()} ${f.title}: ${f.cause}`);
349
+ for (const e of f.evidence || []) {
350
+ lines.push(` evidence: ${e.metric} = ${showVal(e.value)} (threshold ${showVal(e.threshold)}, ${e.window}, ${e.source})`);
351
+ }
352
+ lines.push(` fix: ${f.fix && f.fix.text}`);
353
+ if (f.fix && f.fix.action) lines.push(` gated action: ${f.fix.action.tool} ${JSON.stringify(f.fix.action.input)} (asks for approval; not run)`);
354
+ }
355
+ if (report.gaps && report.gaps.length) {
356
+ lines.push('', 'Could not check:');
357
+ for (const g of report.gaps) lines.push(` ${g}`);
358
+ }
359
+ if (report.report_id) lines.push('', `report ${report.report_id}`);
360
+ return lines;
361
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * QUOXON `/vm <verb> <target> [args]`: parse the words, call the shared engine
3
+ * (lib/core/vm.js, same as `quox vm`), return { lines, tone } for say().
4
+ * A held call becomes the approval hint (tone 'warn'); other failures throw.
5
+ */
6
+ import {
7
+ VM_ACTIONS, NODE_ACTIONS, VmPendingError, pendingHint, makeExec,
8
+ listVms, vmInfo, vmDoctor, doctorLines, vmAction, vmSnapshot, poolStatus, poolLease, poolDone, poolConfig,
9
+ nodePower, readPubkey, vmListLines, textOf,
10
+ } from './vm.js';
11
+
12
+ /** Split argv into positionals and --flag value pairs. */
13
+ export function splitFlags(argv) {
14
+ const pos = []; const flags = {};
15
+ for (let i = 0; i < argv.length; i++) {
16
+ const a = argv[i];
17
+ if (a.startsWith('--')) {
18
+ const eq = a.indexOf('=');
19
+ if (eq > 0) flags[a.slice(2, eq)] = a.slice(eq + 1);
20
+ else if (argv[i + 1] !== undefined && !argv[i + 1].startsWith('--')) flags[a.slice(2)] = argv[++i];
21
+ else flags[a.slice(2)] = true;
22
+ } else pos.push(a);
23
+ }
24
+ return { pos, flags };
25
+ }
26
+
27
+ const asLines = (t) => String(t).split('\n');
28
+ export const VM_USAGE = 'Usage: /vm <list|info|doctor|start|stop|reboot|shutdown|snapshot|pool|node> <target> [args]';
29
+
30
+ export async function runVmCommand({ client, orgId, argv, exec: injected }) {
31
+ const exec = injected || makeExec({ client, orgId });
32
+ const { pos, flags } = splitFlags(argv || []);
33
+ const [verb, ...rest] = pos;
34
+ const reason = typeof flags.reason === 'string' ? flags.reason : undefined;
35
+ const need = (v, what) => { if (!v) throw new Error(`${what} is required. ${VM_USAGE}`); return v; };
36
+ try {
37
+ if (!verb) return { lines: [VM_USAGE], tone: 'warn' };
38
+ if (verb === 'list') {
39
+ return { lines: vmListLines(await listVms(exec, { node: flags.node, type: flags.type })) };
40
+ }
41
+ if (verb === 'info') {
42
+ const { vm, resources } = await vmInfo(exec, need(rest[0], 'a VM name or vmid'));
43
+ return { lines: [`${vm.name || vm.vmid} vmid ${vm.vmid} node ${vm.node} ${vm.type}`, ...asLines(textOf(resources))] };
44
+ }
45
+ if (verb === 'doctor') {
46
+ const report = await vmDoctor(exec, need(rest[0], 'a VM name or vmid'));
47
+ return { lines: doctorLines(report), tone: report.verdict === 'healthy' ? undefined : 'warn' };
48
+ }
49
+ if (VM_ACTIONS.includes(verb)) {
50
+ const { vm, result } = await vmAction(exec, verb, need(rest[0], 'a VM name or vmid'), { reason });
51
+ return { lines: [`${verb} ${vm.name || vm.vmid} (vmid ${vm.vmid} on ${vm.node})`, ...asLines(textOf(result))] };
52
+ }
53
+ if (verb === 'snapshot') {
54
+ const { vm, result } = await vmSnapshot(exec, need(rest[0], 'a VM name or vmid'), { name: typeof flags.name === 'string' ? flags.name : undefined, reason });
55
+ return { lines: [`snapshot ${vm.name || vm.vmid} (vmid ${vm.vmid})`, ...asLines(textOf(result))] };
56
+ }
57
+ if (verb === 'pool') {
58
+ const sub = rest[0];
59
+ if (sub === 'status') return { lines: asLines(textOf(await poolStatus(exec))) };
60
+ if (sub === 'lease') {
61
+ const pubkey = readPubkey(typeof flags['pubkey-file'] === 'string' ? flags['pubkey-file'] : undefined);
62
+ const p = await poolLease(exec, {
63
+ lessee: flags.lessee, pubkey, cores: flags.cores, memoryGb: flags['memory-gb'],
64
+ diskGb: flags['disk-gb'], purpose: flags.purpose, reason,
65
+ });
66
+ return { lines: asLines(textOf(p)) };
67
+ }
68
+ if (sub === 'done') return { lines: asLines(textOf(await poolDone(exec, need(rest[1], 'a vmid'), { reason }))) };
69
+ if (sub === 'config') {
70
+ let input = {};
71
+ if (flags.set) { try { input = JSON.parse(flags.set); } catch { throw new Error('--set must be a JSON object'); } }
72
+ return { lines: asLines(textOf(await poolConfig(exec, input, { reason }))) };
73
+ }
74
+ return { lines: ['Usage: /vm pool <status|lease|done <vmid>|config>'], tone: 'warn' };
75
+ }
76
+ if (verb === 'node') {
77
+ const [action, name] = rest;
78
+ if (!NODE_ACTIONS.includes(action)) return { lines: ['Usage: /vm node <reboot|shutdown> <node>'], tone: 'warn' };
79
+ return { lines: asLines(textOf(await nodePower(exec, action, need(name, 'a node name'), { reason }))) };
80
+ }
81
+ return { lines: [`Unknown /vm verb "${verb}". ${VM_USAGE}`], tone: 'warn' };
82
+ } catch (e) {
83
+ if (e instanceof VmPendingError) return { lines: [pendingHint(e)], tone: 'warn' };
84
+ throw e;
85
+ }
86
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * WALKTHROUGH for a plain terminal (`quox approvals walkthrough`, `quox inbox walkthrough`): the waiting decisions one
3
+ * card at a time, using the same card and reply rules as the TUI (quoxon/approvalsView.js). Identity is the decision id.
4
+ * io = { readLine(prompt) -> Promise<string|null>, write(line), answer(item, key, note) -> Promise<void> }.
5
+ */
6
+ import { isDecision, walkCard, walkReply, walkNotYesNo, walkNoCatch, walkSummary } from '../quoxon/approvalsView.js';
7
+
8
+ export async function runWalkthrough(items, io) {
9
+ const decs = (items || []).filter(isDecision);
10
+ if (!decs.length) { io.write('No question is waiting for you.'); return { answered: [], skipped: [], left: [] }; }
11
+ const st = { answered: [], skipped: [], left: [] };
12
+ io.write(`${decs.length} question${decs.length === 1 ? '' : 's'} waiting for you. One at a time: a choice, then an optional note.`);
13
+ for (let i = 0; i < decs.length; i++) {
14
+ const it = decs[i]; const card = walkCard(it, i + 1, decs.length);
15
+ io.write(''); card.lines.forEach((l) => io.write(l));
16
+ for (;;) {
17
+ const line = await io.readLine('Your choice? ');
18
+ if (line == null) { for (let j = i; j < decs.length; j++) st.left.push(decs[j].title || decs[j].id); io.write(''); walkSummary(st).forEach((l) => io.write(l)); return st; }
19
+ const rep = walkReply(it, line);
20
+ if (!rep) { io.write(walkNoCatch(it)); continue; }
21
+ if (rep.notYesNo) { io.write(walkNotYesNo(it)); continue; }
22
+ if (rep.defer) { st.skipped.push(it.title || it.id); break; }
23
+ if (rep.ask) { io.write(` Asked by ${it.payload.session || 'a session'} (id ${it.id}). ${it.payload.why || 'No further reason was given.'}`); continue; }
24
+ if (it.payload.high_risk === true && rep.key !== 'n') { io.write(' High risk: a yes needs your PIN, which this plain terminal cannot take. Answer it in `quox` with /approvals walkthrough, or skip it here.'); continue; }
25
+ try { await io.answer(it, rep.key, rep.note || null); }
26
+ catch (e) { io.write(` Not answered (${e.message}). The question stays open.`); continue; }
27
+ const label = it.payload.options.find((o) => o.key === rep.key).label;
28
+ io.write(`✔ ${it.title || it.id}: ${label}`);
29
+ st.answered.push({ title: it.title || it.id, word: label });
30
+ break;
31
+ }
32
+ }
33
+ io.write(''); walkSummary(st).forEach((l) => io.write(l));
34
+ return st;
35
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Launcher for the HYPERION deck TUI (`quox hyperion deck`). Same shape as lucid/deck.js: alt screen via
3
+ * fullscreen-ink when the terminal supports raw mode; the terminal is restored on every exit path.
4
+ */
5
+ export async function launchHyperionDeck(api, { intervalMs = 10000 } = {}) {
6
+ if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== 'function' || !process.stdout.isTTY) {
7
+ console.error('quox hyperion deck needs an interactive terminal. Use `quox hyperion` for a one-shot view.');
8
+ process.exitCode = 1;
9
+ return;
10
+ }
11
+ // A long-running TUI must use React's production build (the development build grows the heap per render).
12
+ if (!process.env.NODE_ENV) process.env.NODE_ENV = 'production';
13
+ await import('../tui/index.js'); // registers the .jsx loader hook
14
+ const React = (await import('react')).default;
15
+ const { HyperionDeck } = await import('../tui/views/HyperionDeck.jsx');
16
+ const { withFullScreen } = await import('fullscreen-ink');
17
+ const app = withFullScreen(React.createElement(HyperionDeck, { api, intervalMs }), { exitOnCtrlC: true });
18
+ const restore = () => { try { process.stdout.write('\x1b[?1049l\x1b[?25h'); } catch { /* best effort */ } };
19
+ process.once('SIGTERM', () => { restore(); process.exit(143); });
20
+ process.once('SIGHUP', () => { restore(); process.exit(129); });
21
+ process.once('exit', restore);
22
+ process.once('uncaughtException', (e) => { restore(); console.error(e); process.exit(1); });
23
+ try {
24
+ await app.start();
25
+ await app.waitUntilExit();
26
+ } finally {
27
+ restore();
28
+ }
29
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * <LucidCard/>: draws a Quoxon object-card view ({ box, rows }, see cards.js) for the deck.
3
+ *
4
+ * Same geometry and roles as quoxon/objects/view.js, plus what a navigable list needs:
5
+ * box.sel 'focus' | 'dim' the title sits on a bar (and inverse under NO_COLOR) so the selected card is unmistakable
6
+ * role 'sel' the highlighted action bar
7
+ * t (seconds) drop-in reveal: rows decode through half-width katakana, then a short colour
8
+ * afterglow on the real characters. Infinity = settled (what idle always shows).
9
+ * Colour comes from the theme roles (light/dark, 256-colour, NO_COLOR); the characters never change.
10
+ */
11
+ import React from 'react';
12
+ import { Text } from 'ink';
13
+ import { c, mono } from '../quoxon/theme.js';
14
+ import { agentColor } from '../quoxon/theme.js';
15
+ import { decodeRow, padRow, fit, len } from '../quoxon/objects/model.js';
16
+ import { rowStart } from '../quoxon/objects/view.js';
17
+ import { hexRgb, mix, rgbStr } from '../quoxon/motion/color.js';
18
+
19
+ const h = React.createElement;
20
+ export const BAR = { focus: '#1E3A8A', dim: '#27272A' }; // selection bars (white text on both)
21
+
22
+ function roleColor(role) {
23
+ if (mono()) return undefined;
24
+ if (typeof role === 'string' && role.startsWith('agent:')) return agentColor(role.slice(6));
25
+ return c(role || 'value');
26
+ }
27
+ /** A role colour lifted towards white by the afterglow (hex colours only; anything else stays as is). */
28
+ function glowColor(role, g) {
29
+ const base = roleColor(role);
30
+ return typeof base === 'string' && base[0] === '#' ? rgbStr(mix(hexRgb(base), [255, 255, 255], 0.65 * g)) : base;
31
+ }
32
+
33
+ function runs(cells, key) {
34
+ const out = [];
35
+ let run = '', col, bold = false, bg, k = 0;
36
+ const flush = () => { if (run) out.push(h(Text, { key: `${key}-${k++}`, color: col, bold, backgroundColor: bg }, run)); run = ''; };
37
+ for (const cl of cells) {
38
+ let color, back;
39
+ if (cl.role === 'sel') { color = mono() ? undefined : '#FFFFFF'; back = mono() ? undefined : BAR.focus; }
40
+ else if (cl.glyph) color = roleColor('teal'); // the katakana flicker
41
+ else color = cl.glow > 0.05 ? glowColor(cl.role, cl.glow) : roleColor(cl.role);
42
+ const b = Boolean(cl.bold);
43
+ if (color !== col || b !== bold || back !== bg) { flush(); col = color; bold = b; bg = back; }
44
+ run += cl.ch;
45
+ }
46
+ flush();
47
+ return out;
48
+ }
49
+
50
+ export function LucidCard({ view, t = Infinity }) {
51
+ const b = view.box, w = b.width, inner = w - 4;
52
+ const rows = view.rows || [];
53
+ const bc = roleColor(b.border || 'faint');
54
+ const right = b.right ? ` ${b.right} ` : '';
55
+ const title = b.title ? ` ${fit(b.title, Math.max(4, w - 6 - (right ? len(right) + 1 : 0)))} ` : '';
56
+ const fill = Math.max(0, w - 3 - len(title) - len(right) - 1);
57
+ const bar = b.sel ? BAR[b.sel] : null;
58
+ const top = h(Text, { key: 'top', wrap: 'truncate' },
59
+ h(Text, { color: bc }, '╭─'),
60
+ h(Text, { color: bar && !mono() ? '#FFFFFF' : roleColor(b.titleRole || 'value'), backgroundColor: bar && !mono() ? bar : undefined, inverse: Boolean(bar) && mono(), bold: true }, title),
61
+ h(Text, { color: bc }, '─'.repeat(fill)),
62
+ h(Text, { color: roleColor(b.rightRole || 'muted') }, right),
63
+ h(Text, { color: bc }, '─╮'));
64
+ const body = rows.map((r, i) => h(Text, { key: `b${i}`, wrap: 'truncate' },
65
+ h(Text, { color: bc }, '│ '), ...runs(decodeRow(padRow(r, inner), t, rowStart(i, rows.length), 0.25, 70 + i), `b${i}`), h(Text, { color: bc }, ' │')));
66
+ const bottom = h(Text, { key: 'bot', color: bc }, '╰' + '─'.repeat(w - 2) + '╯');
67
+ return h(React.Fragment, null, top, ...body, bottom);
68
+ }