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
@@ -0,0 +1,402 @@
1
+ /**
2
+ * Governed sessions: run any interactive program (claude, codex, the Quox TUI)
3
+ * inside a tmux session `quox-...` that survives sleep and wifi drops and
4
+ * reattaches with no context reload. argv arrays only; user input never reaches
5
+ * a shell string.
6
+ *
7
+ * Session naming: claude keeps the legacy untagged form `quox-NAME`; other
8
+ * programs carry a tag: `quox-codex-NAME`, `quox-tui-NAME`. `quox sessions ls`
9
+ * derives the PROGRAM column from that tag.
10
+ */
11
+ import os from 'node:os';
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import { spawnSync as realSpawnSync } from 'node:child_process';
15
+ import { qArt, colorOn } from './banner.js';
16
+ import { startHeartbeat as realStartHeartbeat } from './sessionHeartbeat.js';
17
+
18
+ const NAME_RE = /^[A-Za-z0-9._-]{1,48}$/;
19
+ export const PREFIX = 'quox-';
20
+ export const HELP_KEY = 'h';
21
+ export const HELP_COMMAND = 'quox sessions help --wait';
22
+
23
+ export const validateName = (n) => typeof n === 'string' && NAME_RE.test(n);
24
+
25
+ export function autoName(now = new Date()) {
26
+ const p = (n) => String(n).padStart(2, '0');
27
+ return `s-${p(now.getMonth() + 1)}${p(now.getDate())}-${p(now.getHours())}${p(now.getMinutes())}`;
28
+ }
29
+
30
+ /**
31
+ * Program registry. One entry per known CLI; adding the next one is one entry.
32
+ * tag '' means legacy untagged (claude). resumeHint(bare, full) is the plain
33
+ * words command that gets you back after a reboot. Unknown programs (quox wrap)
34
+ * use GENERIC: the session carries the tag `run-<program>-` and the hint is
35
+ * `quox sessions attach FULLNAME`.
36
+ */
37
+ export const PROGRAMS = {
38
+ claude: { tag: '', bin: 'claude', resumeHint: (n) => `quox claude --resume ${n}` },
39
+ codex: { tag: 'codex-', bin: 'codex', resumeHint: (n) => `quox codex resume ${n}` },
40
+ // gemini resumes by index or "latest" (--resume latest | --resume N), not by name.
41
+ gemini: { tag: 'gemini-', bin: 'gemini', resumeHint: () => 'quox gemini --resume latest' },
42
+ tui: { tag: 'tui-', bin: 'quox', resumeHint: (n) => `quox tui --governed --name ${n}` },
43
+ };
44
+
45
+ export const GENERIC_TAG = 'run-';
46
+ const PROGRAM_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,47}$/;
47
+ export const validateProgram = (p) => typeof p === 'string' && PROGRAM_RE.test(p);
48
+ /** Tag segment for a generic program: no hyphens so the name parses back. */
49
+ export const programSeg = (bin) => String(bin).replace(/-/g, '_');
50
+
51
+ const TAGGED = Object.entries(PROGRAMS).filter(([, v]) => v.tag);
52
+
53
+ /** Resume hint for any program name (registry entry or generic). */
54
+ export function resumeHintFor(program, bare, full) {
55
+ const e = PROGRAMS[program];
56
+ return e ? e.resumeHint(bare) : `quox sessions attach ${full || bare}`;
57
+ }
58
+
59
+ /** Find an executable on PATH without a shell. Returns path or null. */
60
+ export function whichSync(bin, envPath = process.env.PATH || '') {
61
+ if (!validateProgram(bin)) return null;
62
+ for (const dir of String(envPath).split(path.delimiter)) {
63
+ if (!dir) continue;
64
+ const f = path.join(dir, bin);
65
+ try {
66
+ if (!fs.statSync(f).isFile()) continue;
67
+ fs.accessSync(f, fs.constants.X_OK);
68
+ return f;
69
+ } catch { /* keep looking */ }
70
+ }
71
+ return null;
72
+ }
73
+
74
+ /** Full session name (without the quox- prefix) for a program + user name. */
75
+ export const taggedName = (program, name, bin) =>
76
+ (program === 'run' ? `${GENERIC_TAG}${programSeg(bin)}-` : PROGRAMS[program]?.tag || '') + name;
77
+
78
+ /** Program for a session name (without prefix). Untagged means claude. */
79
+ export function programOf(name) {
80
+ const n = String(name);
81
+ for (const [p, v] of TAGGED) if (n.startsWith(v.tag)) return p;
82
+ const m = /^run-([^-]+)-/.exec(n);
83
+ if (m) return m[1];
84
+ return 'claude';
85
+ }
86
+
87
+ /** Name the user chose, with the program tag stripped. */
88
+ export const bareName = (name) => {
89
+ const n = String(name);
90
+ const p = programOf(n);
91
+ if (PROGRAMS[p]) return PROGRAMS[p].tag ? n.slice(PROGRAMS[p].tag.length) : n;
92
+ return n.replace(/^run-[^-]+-/, '');
93
+ };
94
+
95
+ /** argv for the program inside the session. */
96
+ export function programCmd(program, args, bin) {
97
+ if (program === 'run') return [bin, ...args];
98
+ const p = PROGRAMS[program];
99
+ if (program === 'tui') return [p.bin, 'tui', ...args];
100
+ return [p.bin, ...args];
101
+ }
102
+
103
+ /** Env vars (QUOX_PREATTACH_*) the SessionStart hook reads to attach/check out a slot. Empty when none requested. */
104
+ export function preattachEnv(pre) {
105
+ const out = {};
106
+ if (pre?.agent) out.QUOX_PREATTACH_AGENT = pre.agent;
107
+ if (pre?.slot) out.QUOX_PREATTACH_SLOT = pre.slot;
108
+ if ((pre?.agent || pre?.slot) && pre.session) out.QUOX_PREATTACH_SESSION = pre.session;
109
+ return out;
110
+ }
111
+
112
+ const tmuxEnvArgs = (penv) => Object.entries(penv).flatMap(([k, v]) => ['-e', `${k}=${v}`]);
113
+
114
+ export function planLaunch({ name, args = [], env = {}, alive = false, resume = false, noTmux = false, program = 'claude', bin, preattach }) {
115
+ const penv = preattachEnv(preattach);
116
+ const tmuxSession = PREFIX + taggedName(program, name, bin);
117
+ const cmdArgv = programCmd(program, args, bin);
118
+ if (env.TMUX || noTmux) {
119
+ return { mode: resume ? 'RESUME' : 'NEW', tmuxSession, cmd: cmdArgv, ...(Object.keys(penv).length ? { env: penv } : {}) };
120
+ }
121
+ if (alive) {
122
+ return { mode: 'REATTACH', tmuxSession, cmd: ['tmux', 'attach-session', '-t', tmuxSession] };
123
+ }
124
+ return {
125
+ mode: resume ? 'RESUME' : 'NEW',
126
+ tmuxSession,
127
+ cmd: ['tmux', 'new-session', ...tmuxEnvArgs(penv), '-s', tmuxSession, '--', ...cmdArgv],
128
+ ...(Object.keys(penv).length ? { env: penv } : {}),
129
+ ...(resume ? { note: 'A resume reloads the conversation context (costs tokens). Reattach is free.' } : {}),
130
+ };
131
+ }
132
+
133
+ export function parseSessions(out) {
134
+ return String(out || '')
135
+ .split('\n')
136
+ .map((l) => l.split('\t'))
137
+ .filter((c) => c.length >= 4 && c[0].startsWith(PREFIX))
138
+ .map((c) => {
139
+ const name = c[0].slice(PREFIX.length);
140
+ return {
141
+ name,
142
+ program: programOf(name),
143
+ created: new Date(Number(c[1]) * 1000),
144
+ attached: Number(c[2]) > 0,
145
+ windows: Number(c[3]),
146
+ };
147
+ });
148
+ }
149
+
150
+ /** The plain-words cheat sheet, shared by the intro and the Ctrl-b h popup. */
151
+ export function howItWorksLines({ name, program = 'claude', bare }) {
152
+ const b = bare || bareName(name);
153
+ const full = name;
154
+ return [
155
+ ['Leave it running:', 'press Ctrl-b, then d'],
156
+ ['Come back later:', `quox sessions attach ${name}`],
157
+ ['See your sessions:', 'quox sessions ls'],
158
+ ['Scroll back:', 'press Ctrl-b, then [ (q to stop)'],
159
+ ['After a reboot:', resumeHintFor(program, b, full)],
160
+ ['End it:', `quox sessions kill ${name}`],
161
+ ['Help any time:', 'press Ctrl-b, then h'],
162
+ ];
163
+ }
164
+
165
+ export function renderIntro({ name, host, mode, tmuxSession, note, program = 'claude', preattach }, { color = false } = {}) {
166
+ const b = (s) => (color ? `\x1b[1m${s}\x1b[0m` : s);
167
+ const d = (s) => (color ? `\x1b[2m${s}\x1b[0m` : s);
168
+ const art = qArt(color);
169
+ const modeText = {
170
+ NEW: 'NEW',
171
+ REATTACH: 'REATTACH (same process, no context reload)',
172
+ RESUME: 'RESUME',
173
+ }[mode] || mode;
174
+ const text = [
175
+ '',
176
+ b('QUOX GOVERNED SESSION'),
177
+ '',
178
+ `${d('session ')} ${name}`,
179
+ `${d('program ')} ${program}`,
180
+ `${d('host ')} ${host}`,
181
+ `${d('mode ')} ${modeText}`,
182
+ `${d('tmux session')} ${tmuxSession}`,
183
+ '',
184
+ b('How this works'),
185
+ ...howItWorksLines({ name, program }).map(([k, v]) => `${d(k.padEnd(19))}${v}`),
186
+ ];
187
+ const w = art[0] ? 14 : 0;
188
+ const lines = [];
189
+ for (let i = 0; i < Math.max(art.length, text.length); i++) {
190
+ lines.push(` ${art[i] || ' '.repeat(w)} ${text[i] || ''}`);
191
+ }
192
+ let out = '\n' + lines.join('\n') + '\n';
193
+ if (note) out += ` ${d(note)}\n`;
194
+ if (mode !== 'REATTACH' && (preattach?.agent || preattach?.slot)) {
195
+ const { agent, slot } = preattach;
196
+ const what = agent ? `Attaching as ${agent}${slot ? ` \u00b7 slot ${slot}` : ''}` : `Holding slot ${slot}`;
197
+ out += ` ${b(`${what} at session start`)}\n`;
198
+ if (slot) out += ` ${d(`Talk to it from Discord in #${slot}`)}\n`;
199
+ }
200
+ return out + '\n';
201
+ }
202
+
203
+ /** Plain text for `quox sessions help` (no colour, no art). */
204
+ export function renderHelp({ name, program } = {}) {
205
+ const n = name || '<name>';
206
+ const p = program || (name ? programOf(name) : 'claude');
207
+ const lines = howItWorksLines({ name: n, program: p, bare: name ? bareName(name) : '<name>' });
208
+ return ['QUOX GOVERNED SESSION: How this works', '', ...lines.map(([k, v]) => `${k.padEnd(19)}${v}`), ''].join('\n');
209
+ }
210
+
211
+ function defaults(over = {}) {
212
+ return {
213
+ spawnSync: realSpawnSync,
214
+ env: process.env,
215
+ hostname: () => os.hostname(),
216
+ now: () => new Date(),
217
+ color: colorOn(),
218
+ write: (s) => process.stdout.write(s),
219
+ // QUOXON J1: register + heartbeat each tmux session the wrapper starts. An
220
+ // injected spawnSync (tests) means no real child unless startHeartbeat is injected too.
221
+ startHeartbeat: over.spawnSync ? () => {} : realStartHeartbeat,
222
+ ...over,
223
+ };
224
+ }
225
+
226
+ const tmuxMissing = (r) => r && r.error && r.error.code === 'ENOENT';
227
+
228
+ /** tmux -V output to [major, minor], or null if unknown. */
229
+ export function parseTmuxVersion(out) {
230
+ const m = /(\d+)\.(\d+)/.exec(String(out || ''));
231
+ return m ? [Number(m[1]), Number(m[2])] : null;
232
+ }
233
+
234
+ export const supportsPopup = (v) => !!v && (v[0] > 3 || (v[0] === 3 && v[1] >= 2));
235
+
236
+ /** argv (after `tmux`) that binds Ctrl-b h. Contains no user data. */
237
+ export function helpBindArgv(version) {
238
+ if (supportsPopup(version)) {
239
+ return ['bind-key', HELP_KEY, 'display-popup', '-E', '-w', '80%', '-h', '60%', HELP_COMMAND];
240
+ }
241
+ return ['bind-key', HELP_KEY, 'display-message',
242
+ 'Quox: Ctrl-b d leave it running | Ctrl-b [ scroll back | quox sessions ls | quox sessions help'];
243
+ }
244
+
245
+ /**
246
+ * Bind Ctrl-b h idempotently (bind-key is tmux-server wide). Returns
247
+ * 'cold' when no tmux server is running yet (nothing to bind into),
248
+ * 'present' when already bound to the quox help, 'bound' otherwise, 'failed'.
249
+ */
250
+ export function ensureHelpBinding(d) {
251
+ const keys = d.spawnSync('tmux', ['list-keys', '-T', 'prefix'], { encoding: 'utf8' });
252
+ if (!keys || keys.error || keys.status !== 0) return 'cold';
253
+ if (String(keys.stdout || '').includes('quox sessions help')) return 'present';
254
+ const v = d.spawnSync('tmux', ['-V'], { encoding: 'utf8' });
255
+ const r = d.spawnSync('tmux', helpBindArgv(parseTmuxVersion(v && v.stdout)), { stdio: 'ignore' });
256
+ return r && r.status === 0 ? 'bound' : 'failed';
257
+ }
258
+
259
+ export function runGoverned({ program = 'claude', name, args = [], resume = false, noTmux = false, bin, preattach }, over) {
260
+ const d = defaults(over);
261
+ const n = name || autoName(d.now());
262
+ if (program === 'run') {
263
+ if (!validateProgram(bin)) {
264
+ d.write(`Refusing program "${bin}": use a plain command name (letters, digits, dot, underscore, plus, hyphen), no paths or shell characters.\n`);
265
+ return 2;
266
+ }
267
+ const found = (d.which || whichSync)(bin, d.env.PATH);
268
+ if (!found) {
269
+ d.write(`${bin} was not found on PATH.\n`);
270
+ return 1;
271
+ }
272
+ }
273
+ const full = taggedName(program, n, bin);
274
+ if (!validateName(n) || !validateName(full)) {
275
+ d.write(`Invalid session name "${n}". Use 1-48 characters: letters, digits, dot, underscore, hyphen.\n`);
276
+ return 2;
277
+ }
278
+ let alive = false;
279
+ if (!noTmux && !d.env.TMUX) {
280
+ const r = d.spawnSync('tmux', ['has-session', '-t', '=' + PREFIX + full], { stdio: 'ignore' });
281
+ if (tmuxMissing(r)) {
282
+ d.write(`tmux is not installed. Install it (apt install tmux / brew install tmux), or run without it: quox ${program === 'tui' ? 'tui --governed' : program === 'run' ? `wrap ${bin}` : program} --no-tmux\n`);
283
+ return 1;
284
+ }
285
+ alive = r.status === 0;
286
+ }
287
+ const plan = planLaunch({ name: n, args, env: d.env, alive, resume, noTmux, program, bin, preattach });
288
+ const shown = program === 'run' ? bin : program;
289
+ d.write(renderIntro({ name: full, host: d.hostname(), mode: plan.mode, tmuxSession: plan.tmuxSession, note: plan.note, program: shown, preattach }, { color: d.color }));
290
+ let cmd = plan.cmd;
291
+ const viaTmux = cmd[0] === 'tmux';
292
+ if (viaTmux) {
293
+ const state = ensureHelpBinding(d);
294
+ if (state === 'cold' && plan.mode !== 'REATTACH') {
295
+ // No tmux server yet: create the session detached so a server exists,
296
+ // bind the help key into it, then attach.
297
+ const created = d.spawnSync('tmux', ['new-session', '-d', ...tmuxEnvArgs(preattachEnv(preattach)), '-s', plan.tmuxSession, '--', ...programCmd(program, args, bin)], { stdio: 'ignore' });
298
+ if (created && created.status !== 0) {
299
+ d.write('Could not start the tmux session.\n');
300
+ return 1;
301
+ }
302
+ ensureHelpBinding(d);
303
+ cmd = ['tmux', 'attach-session', '-t', plan.tmuxSession];
304
+ }
305
+ }
306
+ if (viaTmux) {
307
+ try {
308
+ d.startHeartbeat({
309
+ tmuxSession: plan.tmuxSession, backend: program === 'run' ? bin : program,
310
+ agent: (preattach && preattach.agent) || null, slot: (preattach && preattach.slot) || null,
311
+ });
312
+ } catch { /* the index is best effort; never block the launch */ }
313
+ }
314
+ const [exe, ...exeArgs] = cmd;
315
+ // Direct run (--no-tmux, or already inside tmux): the env rides the spawn.
316
+ const r = d.spawnSync(exe, exeArgs, { stdio: 'inherit', ...(exe !== 'tmux' && plan.env ? { env: { ...d.env, ...plan.env } } : {}) });
317
+ if (r && r.error) {
318
+ d.write(`Could not run ${exe}: ${r.error.message || r.error.code}\n`);
319
+ return 1;
320
+ }
321
+ return r && typeof r.status === 'number' ? r.status : 0;
322
+ }
323
+
324
+ function listSessions(d) {
325
+ const r = d.spawnSync('tmux', ['list-sessions', '-F', '#{session_name}\t#{session_created}\t#{session_attached}\t#{session_windows}'], { encoding: 'utf8' });
326
+ if (tmuxMissing(r)) return null;
327
+ return r.status === 0 ? parseSessions(r.stdout) : [];
328
+ }
329
+
330
+ export function runLs(over) {
331
+ const d = defaults(over);
332
+ const s = listSessions(d);
333
+ if (s === null) {
334
+ d.write('tmux is not installed (apt install tmux / brew install tmux).\n');
335
+ return 1;
336
+ }
337
+ if (!s.length) {
338
+ d.write('No Quox sessions running. Start one with: quox claude (or quox codex, quox gemini, quox wrap <program>)\n');
339
+ return 0;
340
+ }
341
+ const pad = (v, n) => String(v).padEnd(n);
342
+ d.write(`${pad('NAME', 24)}${pad('PROGRAM', 12)}${pad('CREATED', 18)}${pad('ATTACHED', 10)}WINDOWS\n`);
343
+ for (const x of s) {
344
+ const c = x.created.toISOString().slice(0, 16).replace('T', ' ');
345
+ d.write(`${pad(x.name, 24)}${pad(x.program, 12)}${pad(c, 18)}${pad(x.attached ? 'yes' : 'no', 10)}${x.windows}\n`);
346
+ }
347
+ return 0;
348
+ }
349
+
350
+ export function runKill(name, over) {
351
+ const d = defaults(over);
352
+ if (!validateName(name)) {
353
+ d.write(`Invalid session name "${name}".\n`);
354
+ return 2;
355
+ }
356
+ const has = d.spawnSync('tmux', ['has-session', '-t', '=' + PREFIX + name], { stdio: 'ignore' });
357
+ if (has.status !== 0) {
358
+ d.write(`No such session: ${name}. See: quox sessions ls\n`);
359
+ return 1;
360
+ }
361
+ const r = d.spawnSync('tmux', ['kill-session', '-t', PREFIX + name], { stdio: 'ignore' });
362
+ d.write(r.status === 0 ? `Killed ${name}.\n` : `Failed to kill ${name}.\n`);
363
+ return r.status === 0 ? 0 : 1;
364
+ }
365
+
366
+ export function runAttach(name, over) {
367
+ const d = defaults(over);
368
+ if (!validateName(name)) {
369
+ d.write(`Invalid session name "${name}".\n`);
370
+ return 2;
371
+ }
372
+ const has = d.spawnSync('tmux', ['has-session', '-t', '=' + PREFIX + name], { stdio: 'ignore' });
373
+ if (tmuxMissing(has)) {
374
+ d.write('tmux is not installed (apt install tmux / brew install tmux).\n');
375
+ return 1;
376
+ }
377
+ if (has.status !== 0) {
378
+ d.write(`No such session: ${name}. See: quox sessions ls\n`);
379
+ return 1;
380
+ }
381
+ const verb = d.env.TMUX ? 'switch-client' : 'attach-session';
382
+ const r = d.spawnSync('tmux', [verb, '-t', PREFIX + name], { stdio: 'inherit' });
383
+ return r && typeof r.status === 'number' ? r.status : 0;
384
+ }
385
+
386
+ /** `quox sessions help`: print the cheat sheet for the current (or given) session. */
387
+ export function runHelp({ wait = false } = {}, over) {
388
+ const d = defaults(over);
389
+ let name;
390
+ if (d.env.TMUX) {
391
+ const r = d.spawnSync('tmux', ['display-message', '-p', '#{session_name}'], { encoding: 'utf8' });
392
+ const s = r && r.status === 0 ? String(r.stdout || '').trim() : '';
393
+ if (s.startsWith(PREFIX) && validateName(s.slice(PREFIX.length))) name = s.slice(PREFIX.length);
394
+ }
395
+ d.write(renderHelp({ name }) + '\n');
396
+ if (wait) {
397
+ d.write('Press any key to close.\n');
398
+ if (d.waitKey) d.waitKey();
399
+ else if (process.stdin.isTTY) d.spawnSync('sh', ['-c', 'stty raw -echo; dd bs=1 count=1 >/dev/null 2>&1; stty sane'], { stdio: 'inherit' });
400
+ }
401
+ return 0;
402
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * HYPERION activity: the shared reader and plain-text renderers behind `quox hyperion`,
3
+ * `quox hyperion deck` and the QUOXON /hyperion command. Pure functions over the
4
+ * GET /api/v1/proxmox/activity payload; no colour (callers paint), no grey, labels not bare values.
5
+ */
6
+ import { resolveEndpoint } from '../api/endpoints.js';
7
+
8
+ export function fetchActivity(client) {
9
+ const ep = resolveEndpoint('v1.proxmox.activity');
10
+ return client.get(ep.path, { service: ep.service });
11
+ }
12
+
13
+ const ms = (v) => {
14
+ if (v == null || v === '') return null;
15
+ const n = typeof v === 'number' ? v : Date.parse(v);
16
+ return Number.isFinite(n) ? n : null;
17
+ };
18
+
19
+ function span(delta) {
20
+ const s = Math.max(0, Math.floor(delta / 1000));
21
+ if (s < 60) return `${s}s`;
22
+ const m = Math.floor(s / 60);
23
+ if (m < 60) return `${m}m`;
24
+ const h = Math.floor(m / 60);
25
+ if (h < 24) return `${h}h`;
26
+ return `${Math.floor(h / 24)}d`;
27
+ }
28
+
29
+ /** Expiry wins ("expires in 3h" / "expired 1h ago"), else how long it has been held, else "-". */
30
+ export function whenText(held, now = Date.now()) {
31
+ const exp = ms(held && held.expires);
32
+ if (exp != null) return exp >= now ? `expires in ${span(exp - now)}` : `expired ${span(now - exp)} ago`;
33
+ const since = ms(held && held.since);
34
+ if (since != null) return `held ${span(now - since)}`;
35
+ return '-';
36
+ }
37
+
38
+ export function holderText(held) {
39
+ const h = held || {};
40
+ if (h.source === 'pool') return h.lessee ? `${h.lessee} (pool)` : 'free (pool)';
41
+ if (h.source === 'lane') return `${h.lessee || 'unknown'} (lane)`;
42
+ return 'nobody recorded';
43
+ }
44
+
45
+ const isHeld = (v) => Boolean(v.held_by && v.held_by.lessee);
46
+ const pct = (x) => (typeof x === 'number' ? `${Math.round(x * 100)}%` : '?');
47
+ const gb = (b) => (typeof b === 'number' ? (b / 1e9).toFixed(1) : '?');
48
+ const clipTo = (s, n) => (s.length > n ? `${s.slice(0, n - 1)}…` : s);
49
+
50
+ /** Full one-shot view: VM table, node load, pool counts, last 5 ops. */
51
+ export function statusLines(data, { now = Date.now() } = {}) {
52
+ const d = data || {};
53
+ const out = [];
54
+ const vms = d.vms || [];
55
+ if (!vms.length) out.push('No VMs are held or pooled right now.');
56
+ else {
57
+ const rows = vms.map((v) => [String(v.vmid), v.name || '', v.node || '', v.status || '', holderText(v.held_by), (v.held_by && v.held_by.purpose) || '-', whenText(v.held_by, now)]);
58
+ const head = ['VMID', 'NAME', 'NODE', 'STATUS', 'HELD BY', 'PURPOSE', 'AGE / EXPIRY'];
59
+ const w = head.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
60
+ const fmt = (r) => r.map((c, i) => c.padEnd(w[i])).join(' ').trimEnd();
61
+ out.push(fmt(head), ...rows.map(fmt));
62
+ }
63
+ if (d.vms_truncated) out.push(`⚠ ${d.vms_truncated} more tagged VMs not shown`);
64
+ if (d.vm_config_errors) out.push(`⚠ ${d.vm_config_errors} VM description(s) could not be read, holder taken from tags`);
65
+ out.push('');
66
+ out.push('Node load');
67
+ if (d.nodes_error) out.push(` Node load unavailable: ${d.nodes_error}`);
68
+ else if (!(d.nodes || []).length) out.push(' no nodes reported');
69
+ else for (const n of d.nodes) out.push(` ${String(n.node).padEnd(10)} ${String(n.status).padEnd(8)} cpu ${pct(n.cpu)} mem ${gb(n.mem_used)}/${gb(n.mem_total)} GB`);
70
+ out.push('');
71
+ if (d.pool) {
72
+ const counts = Object.entries(d.pool.counts || {}).map(([k, v]) => `${k} ${v}`).join(', ') || 'no VMs';
73
+ out.push(`Pool: ${d.pool.enabled ? 'enabled' : 'disabled'}, ${counts}${d.pool.paused ? ' (paused, waiting for approval)' : ''}`);
74
+ } else out.push(`Pool unavailable: ${d.pool_error || 'unknown'}`);
75
+ out.push('');
76
+ if (d.ops_error) out.push(`Recent ops unavailable: ${d.ops_error}`);
77
+ else {
78
+ const ops = (d.recent_ops || []).slice(0, 5);
79
+ out.push(`Recent ops (${ops.length})`);
80
+ if (!ops.length) out.push(' none recorded');
81
+ for (const o of ops) out.push(` ${o.ts} ${o.intent} vmid ${o.vmid ?? '-'} by ${o.actor || '?'} outcome ${o.outcome ?? '-'}`);
82
+ }
83
+ return out;
84
+ }
85
+
86
+ /** Compact list for the TUI: a header, then one line per held VM. */
87
+ export function heldLines(data, { now = Date.now(), width = 100 } = {}) {
88
+ const held = ((data && data.vms) || []).filter(isHeld);
89
+ if (!held.length) return ['No VMs are held right now.'];
90
+ const lines = [`${held.length} held`];
91
+ for (const v of held) {
92
+ const purpose = v.held_by.purpose ? `, ${v.held_by.purpose}` : '';
93
+ lines.push(clipTo(`${v.vmid} ${v.name || ''} on ${v.node || '?'} ${v.status || ''}: held by ${holderText(v.held_by)}${purpose}, ${whenText(v.held_by, now)}`, width));
94
+ }
95
+ return lines;
96
+ }
97
+
98
+ /** Result text for the QuoxGlyph header line. */
99
+ export function summaryText(data) {
100
+ const vms = (data && data.vms) || [];
101
+ return `${vms.length} VMs · ${vms.filter(isHeld).length} held`;
102
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * CLI-ERRORS (owner 2026-10-17): "Already approved by <who> at <time>; nothing to do".
3
+ * Mirrors services/auth/lib/alreadyResolved.js so an older server (no `resolution` in its
4
+ * 400 body) still gets the same one-line answer from the card itself.
5
+ */
6
+ import { QuoxUserError } from './errors.js';
7
+
8
+ const APPROVE = new Set(['approve', 'approved', 'accept', 'accepted', 'grant', 'granted']);
9
+ const DENY = new Set(['deny', 'denied', 'dismissed', 'reject', 'rejected']);
10
+
11
+ function normalise(action) {
12
+ const a = String(action || '').trim().toLowerCase();
13
+ if (APPROVE.has(a)) return 'approved';
14
+ if (DENY.has(a)) return 'rejected';
15
+ if (a === 'answer') return 'answered';
16
+ if (a === 'defer') return 'deferred';
17
+ if (a === 'request_changes') return 'changes requested';
18
+ return a ? 'resolved' : null;
19
+ }
20
+
21
+ /** A y/n decision answered "y" reads as approved, "n" as rejected; another option as answered "<key>". */
22
+ function outcomeOf(prior) {
23
+ const opt = prior && typeof prior.option === 'string' ? prior.option : null;
24
+ if (opt === 'y') return 'approved';
25
+ if (opt === 'n') return 'rejected';
26
+ const n = normalise(prior?.action);
27
+ return n === 'answered' && opt ? `answered "${opt}"` : n;
28
+ }
29
+
30
+ function toIso(ts) {
31
+ if (!ts) return null;
32
+ const s = String(ts);
33
+ const d = new Date(/[zZ]|[+-]\d\d:?\d\d$/.test(s) ? s : `${s.replace(' ', 'T')}Z`);
34
+ return Number.isNaN(d.getTime()) ? null : d.toISOString();
35
+ }
36
+
37
+ export function humanUtc(iso) {
38
+ return iso ? `${iso.slice(0, 10)} ${iso.slice(11, 16)} UTC` : 'an unknown time';
39
+ }
40
+
41
+ export function resolutionFromItem(item, requestedAction) {
42
+ if (!item || !item.id) return null;
43
+ const response = item.response || null;
44
+ const outcome = item.status === 'expired' ? 'expired' : item.status === 'cancelled' ? 'cancelled' : (outcomeOf(response) || 'resolved');
45
+ const name = item.resolved_by_name || response?.responded_by_name || null;
46
+ const at = toIso(item.resolved_at) || toIso(response?.responded_at) || null;
47
+ const requested = String(requestedAction || '').trim().toLowerCase() || null;
48
+ const matches = Boolean(requested) && outcome !== 'expired' && outcome !== 'cancelled' && normalise(requested) === outcome;
49
+ let message;
50
+ if (outcome === 'expired') message = `This approval expired at ${humanUtc(at)}; nothing was approved. Ask for it again.`;
51
+ else if (outcome === 'cancelled') message = `This item was cancelled at ${humanUtc(at)}${name ? ` by ${name}` : ''}; there is nothing to respond to.`;
52
+ else {
53
+ const head = `Already ${outcome}${name ? ` by ${name}` : ''} at ${humanUtc(at)}`;
54
+ message = matches || !requested ? `${head}; nothing to do.` : `${head}; your ${requested} was not applied.`;
55
+ }
56
+ return { item_id: item.id, status: item.status, outcome, resolved_by: item.resolved_by || null, resolved_by_name: name, resolved_at: at, requested, matches, message };
57
+ }
58
+
59
+ /** A respond whose outcome does NOT match what is already recorded: exit 6 (conflict). */
60
+ export function alreadyResolvedError(resolution, cause) {
61
+ return new QuoxUserError(resolution.message, {
62
+ next: `\`quox inbox show ${resolution.item_id}\` shows the decision.`,
63
+ exitCode: 6, code: 'already_resolved', status: cause?.status, body: cause?.body,
64
+ });
65
+ }