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,90 @@
1
+ /**
2
+ * useHealth — polls the aggregate service-health endpoint for the
3
+ * StatusBar's health dot.
4
+ *
5
+ * Wraps usePolledApi with a 30s cadence and derives a simple traffic-light
6
+ * color from whatever shape the health payload happens to be in. Never
7
+ * throws — any fetch failure or unrecognized shape degrades to 'grey'.
8
+ *
9
+ * Usage:
10
+ * const { color, loading, lastFetched, isStale } = useHealth(client, {
11
+ * intervalMs: 30000,
12
+ * });
13
+ *
14
+ * @param {Object} client - API client with a `.get(path, opts)` method
15
+ * @param {Object} [options]
16
+ * @param {number} [options.intervalMs=30000] - poll interval in ms
17
+ * @param {number} [options.staleAfterMs=90000] - age at which the reading is considered stale
18
+ * @returns {{ color: 'green'|'amber'|'red'|'grey', loading: boolean, lastFetched: number|null, isStale: boolean }}
19
+ */
20
+
21
+ import { usePolledApi } from './usePolledApi.js';
22
+ import { resolveEndpoint } from '../../api/endpoints.js';
23
+
24
+ const HEALTHY_STATUSES = new Set(['healthy', 'ok', 'up', 'online', 'pass', 'passing']);
25
+ const DEGRADED_STATUSES = new Set(['degraded', 'partial', 'warning', 'warn']);
26
+ const DOWN_STATUSES = new Set(['down', 'unhealthy', 'critical', 'fail', 'failing', 'offline']);
27
+
28
+ /** Normalize a single status-ish field into 'green' | 'amber' | 'red' | null (unknown). */
29
+ function statusToColor(status, healthy) {
30
+ if (typeof status === 'string') {
31
+ const s = status.toLowerCase();
32
+ if (HEALTHY_STATUSES.has(s)) return 'green';
33
+ if (DEGRADED_STATUSES.has(s)) return 'amber';
34
+ if (DOWN_STATUSES.has(s)) return 'red';
35
+ }
36
+ if (typeof healthy === 'boolean') return healthy ? 'green' : 'red';
37
+ return null;
38
+ }
39
+
40
+ /** Defensively derive a dot color from an unknown health-aggregate response shape. */
41
+ export function deriveHealthColor(data) {
42
+ if (!data || typeof data !== 'object') return 'grey';
43
+
44
+ // Direct top-level status/healthy fields.
45
+ const direct = statusToColor(data.status, data.healthy);
46
+ if (direct) return direct;
47
+
48
+ // Array of per-service entries — aggregate worst-case wins.
49
+ const services = Array.isArray(data.services)
50
+ ? data.services
51
+ : Array.isArray(data.data)
52
+ ? data.data
53
+ : null;
54
+
55
+ if (services && services.length > 0) {
56
+ let sawDegraded = false;
57
+ for (const svc of services) {
58
+ const c = statusToColor(svc?.status, svc?.healthy);
59
+ if (c === 'red') return 'red';
60
+ if (c === 'amber') sawDegraded = true;
61
+ // unknown entries (c === null) don't downgrade the aggregate
62
+ }
63
+ return sawDegraded ? 'amber' : 'green';
64
+ }
65
+
66
+ return 'grey';
67
+ }
68
+
69
+ export function useHealth(client, options = {}) {
70
+ const { intervalMs = 30000, staleAfterMs = 90000 } = options;
71
+ const active = Boolean(client && typeof client.get === 'function');
72
+
73
+ const { data, loading, error, lastFetched } = usePolledApi(
74
+ () => client.get(resolveEndpoint('v1.health.aggregate.list').path, { service: resolveEndpoint('v1.health.aggregate.list').service }),
75
+ [client],
76
+ { intervalMs, active }
77
+ );
78
+
79
+ const color = !active
80
+ ? 'grey'
81
+ : loading && lastFetched == null
82
+ ? 'grey'
83
+ : error
84
+ ? 'grey'
85
+ : deriveHealthColor(data);
86
+
87
+ const isStale = Boolean(lastFetched != null && Date.now() - lastFetched > staleAfterMs);
88
+
89
+ return { color, loading, lastFetched, isStale };
90
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * usePolledApi — React hook for API calls with optional background polling
3
+ *
4
+ * Generalization of useApi that adds:
5
+ * - `active` gating: only fetches / polls while active is true
6
+ * - `intervalMs` polling: re-fetches on a timer while active (cleared on
7
+ * inactive/unmount — never polls in the background)
8
+ * - `isStale` flag: true when the last successful fetch is older than
9
+ * 2x the poll interval (only meaningful when intervalMs > 0)
10
+ * - simple error backoff: a failed fetch skips the next scheduled tick
11
+ * instead of hammering a broken endpoint every intervalMs
12
+ *
13
+ * Usage:
14
+ * const { data, loading, error, refresh, lastFetched, isStale } = usePolledApi(
15
+ * () => client.get('/api/v1/fleet/status', { service: 'collector' }),
16
+ * [client],
17
+ * { intervalMs: 10000, active: isActive }
18
+ * );
19
+ *
20
+ * @param {Function} fetchFn - async function that returns data
21
+ * @param {Array} deps - dependency array (re-fetch when changed)
22
+ * @param {Object} [options]
23
+ * @param {number} [options.intervalMs=0] - poll interval in ms (0 = no polling)
24
+ * @param {boolean} [options.active=true] - whether fetching/polling is enabled
25
+ * @returns {{ data: any, loading: boolean, error: Error|null, refresh: Function, lastFetched: number|null, isStale: boolean }}
26
+ */
27
+
28
+ import { useState, useEffect, useCallback, useRef } from 'react';
29
+
30
+ export function usePolledApi(fetchFn, deps = [], options = {}) {
31
+ const { intervalMs = 0, active = true } = options;
32
+
33
+ const [data, setData] = useState(null);
34
+ const [loading, setLoading] = useState(true);
35
+ const [error, setError] = useState(null);
36
+ const [lastFetched, setLastFetched] = useState(null);
37
+
38
+ // Simple backoff: when a fetch fails, skip the next scheduled poll tick
39
+ // instead of hammering a broken endpoint every intervalMs.
40
+ const skipNextTickRef = useRef(false);
41
+
42
+ const refresh = useCallback(async () => {
43
+ setLoading(true);
44
+ setError(null);
45
+ try {
46
+ const result = await fetchFn();
47
+ setData(result);
48
+ setLastFetched(Date.now());
49
+ skipNextTickRef.current = false;
50
+ } catch (err) {
51
+ setError(err);
52
+ skipNextTickRef.current = true;
53
+ } finally {
54
+ setLoading(false);
55
+ }
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
57
+ }, deps);
58
+
59
+ // ── Fetch on activation and whenever deps change while active ────────
60
+ useEffect(() => {
61
+ if (!active) return;
62
+ refresh();
63
+ }, [active, refresh]);
64
+
65
+ // ── Poll on an interval, only while active ────────────────────────────
66
+ useEffect(() => {
67
+ if (!active || !intervalMs) return;
68
+
69
+ const id = setInterval(() => {
70
+ if (skipNextTickRef.current) {
71
+ // Back off one tick after a failure, then resume normal cadence.
72
+ skipNextTickRef.current = false;
73
+ return;
74
+ }
75
+ refresh();
76
+ }, intervalMs);
77
+
78
+ return () => clearInterval(id);
79
+ }, [active, intervalMs, refresh]);
80
+
81
+ const isStale = Boolean(
82
+ intervalMs > 0 && lastFetched != null && Date.now() - lastFetched > intervalMs * 2
83
+ );
84
+
85
+ return { data, loading, error, refresh, lastFetched, isStale };
86
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * useTerminalSize — reactive terminal dimensions
3
+ *
4
+ * Wraps ink's useStdout() and subscribes to the stdout 'resize' event so
5
+ * components can adapt their layout (separators, page sizes, overlay
6
+ * widths) to the actual terminal instead of hard-coded constants.
7
+ *
8
+ * Falls back to a sensible default (80x24) when stdout is unavailable
9
+ * (e.g. non-TTY output, piped/redirected streams).
10
+ *
11
+ * @returns {{ columns: number, rows: number }}
12
+ */
13
+
14
+ import { useState, useEffect } from 'react';
15
+ import { useStdout } from 'ink';
16
+
17
+ const FALLBACK = { columns: 80, rows: 24 };
18
+
19
+ function readSize(stdout) {
20
+ return {
21
+ columns: stdout?.columns || FALLBACK.columns,
22
+ rows: stdout?.rows || FALLBACK.rows,
23
+ };
24
+ }
25
+
26
+ export function useTerminalSize() {
27
+ const { stdout } = useStdout();
28
+ const [size, setSize] = useState(() => readSize(stdout));
29
+
30
+ useEffect(() => {
31
+ if (!stdout || typeof stdout.on !== 'function') return undefined;
32
+
33
+ const onResize = () => setSize(readSize(stdout));
34
+ stdout.on('resize', onResize);
35
+
36
+ return () => {
37
+ if (typeof stdout.off === 'function') {
38
+ stdout.off('resize', onResize);
39
+ } else if (typeof stdout.removeListener === 'function') {
40
+ stdout.removeListener('resize', onResize);
41
+ }
42
+ };
43
+ }, [stdout]);
44
+
45
+ return size;
46
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * QuoxCLI TUI Entry Point
3
+ *
4
+ * Creates the Ink app instance and renders the root App component.
5
+ * Receives the CLI context object containing config, auth credentials,
6
+ * and API client.
7
+ *
8
+ * Supports two modes:
9
+ * - Native TTY: uses fullscreen-ink for alternate screen buffer
10
+ * - PTY/Web terminal: falls back to basic Ink render (no raw mode needed)
11
+ *
12
+ * Registers a custom JSX loader hook via esbuild before importing .jsx files.
13
+ */
14
+
15
+ import { register } from 'node:module';
16
+
17
+ // Register JSX support before importing any .jsx files
18
+ register('./jsx-loader.js', import.meta.url);
19
+
20
+ /**
21
+ * Launch the QuoxCLI TUI.
22
+ *
23
+ * @param {Object} ctx - CLI context
24
+ * @param {Object} ctx.config - resolved configuration
25
+ * @param {Object} ctx.auth - auth credentials / token
26
+ * @param {Object} ctx.client - API client instance
27
+ */
28
+ export async function launchTUI(ctx, opts = {}) {
29
+ const React = (await import('react')).default;
30
+ const { App } = await import('./app.jsx');
31
+ const initialScreen = opts.initialScreen || null;
32
+
33
+ // Check if raw mode is supported (it's not when spawned via node-pty in a container)
34
+ const rawModeSupported = process.stdin.isTTY && typeof process.stdin.setRawMode === 'function';
35
+
36
+ if (rawModeSupported) {
37
+ // Native terminal — use fullscreen (alternate screen buffer)
38
+ try {
39
+ const { withFullScreen } = await import('fullscreen-ink');
40
+ const app = withFullScreen(
41
+ React.createElement(App, { ctx, initialScreen }),
42
+ { exitOnCtrlC: true }
43
+ );
44
+ await app.start();
45
+ await app.waitUntilExit();
46
+ return;
47
+ } catch {
48
+ // Fall through to basic mode if fullscreen fails
49
+ }
50
+ }
51
+
52
+ // PTY / web terminal — use basic Ink render without raw mode requirement
53
+ const { render } = await import('ink');
54
+ const instance = render(React.createElement(App, { ctx, initialScreen }));
55
+ await instance.waitUntilExit();
56
+ }
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Animated intro header for QuoxCLI TUI
3
+ *
4
+ * Renders a swirling animated orb with dot-density characters, "QUOX" in
5
+ * large block font with flowing gradient, and a shimmering subtitle.
6
+ * All on one screen with the main menu below — no "press any key" gate.
7
+ *
8
+ * Inspired by QuoxBastion's Go TUI intro (intro.go) and
9
+ * terminal-text-effects (TTX) orb_dots effect.
10
+ *
11
+ * Animation sequence (~2s):
12
+ * 0.0s Orb fades in with expanding energy ring
13
+ * 0.4s Block title appears with flowing gradient
14
+ * 0.7s Subtitle shimmers in
15
+ * 2.0s Animation completes and FREEZES on the final frame — no
16
+ * perpetual "breathing" interval runs after the burst, so the
17
+ * intro costs zero background CPU once it's settled.
18
+ *
19
+ * Lite mode (QUOX_TUI_LITE env var, or non-TTY stdout): skips the
20
+ * animated burst entirely and renders a single static brand-coherent
21
+ * banner with no setInterval at all — for constrained VMs / piped output.
22
+ */
23
+
24
+ import React, { useState, useEffect } from 'react';
25
+ import { Box, Text, useStdout } from 'ink';
26
+ import chalk from 'chalk';
27
+ import { colors } from '../theme.js';
28
+
29
+ // ── Brand palette (Quox blue → indigo → violet → lilac) ──────────────
30
+ const PALETTE = [
31
+ [30, 58, 138], // #1E3A8A deep blue
32
+ [59, 130, 246], // #3B82F6 electric blue
33
+ [99, 102, 241], // #6366F1 indigo
34
+ [139, 92, 246], // #8B5CF6 violet
35
+ [168, 85, 247], // #A855F7 purple
36
+ [192, 132, 252], // #C084FC lilac
37
+ [196, 181, 253], // #C4B5FD soft lavender
38
+ [139, 92, 246], // #8B5CF6 violet (loop)
39
+ ];
40
+
41
+ // Dot density characters — refined look vs Bastion's block chars (░▒▓█)
42
+ const DOT_CHARS = ' \u00b7\u2219\u2022\u25cf\u25c9\u2b24';
43
+
44
+ // Block font glyphs for "QUOX" — 5 rows each, rendered double-width (██)
45
+ export const BLOCK_FONT = {
46
+ 'Q': ['\u2588\u2588\u2588\u2588\u2588', '\u2588 \u2588', '\u2588 \u2588', '\u2588 \u2588\u2588', ' \u2588\u2588\u2588\u2588'],
47
+ 'U': ['\u2588 \u2588', '\u2588 \u2588', '\u2588 \u2588', '\u2588 \u2588', '\u2588\u2588\u2588\u2588\u2588'],
48
+ 'O': ['\u2588\u2588\u2588\u2588\u2588', '\u2588 \u2588', '\u2588 \u2588', '\u2588 \u2588', '\u2588\u2588\u2588\u2588\u2588'],
49
+ 'X': ['\u2588 \u2588', ' \u2588 \u2588 ', ' \u2588 ', ' \u2588 \u2588 ', '\u2588 \u2588'],
50
+ };
51
+
52
+ // ── Animation timing ──────────────────────────────────────────────────
53
+ const ORB_W = 26;
54
+ const ORB_H = 8;
55
+ const FAST_FRAMES = 60; // ~2s at ~20fps
56
+ const FAST_MS = 50; // 20fps during intro burst
57
+ const SLOW_MS = 250; // 4fps gentle breathing after
58
+
59
+ // ── Math helpers ──────────────────────────────────────────────────────
60
+
61
+ function noise(x, y, t) {
62
+ const n1 = Math.sin(x * 2.1 + t * 0.7) * Math.cos(y * 1.9 - t * 0.5);
63
+ const n2 = Math.sin(x * 0.9 - y * 1.3 + t * 0.8) * 0.5;
64
+ const n3 = Math.cos(x * 1.5 + y * 2.1 + t * 0.3) * 0.3;
65
+ return (n1 + n2 + n3) / 1.8;
66
+ }
67
+
68
+ function lerpColor(c1, c2, frac) {
69
+ return [
70
+ Math.round(c1[0] + (c2[0] - c1[0]) * frac),
71
+ Math.round(c1[1] + (c2[1] - c1[1]) * frac),
72
+ Math.round(c1[2] + (c2[2] - c1[2]) * frac),
73
+ ];
74
+ }
75
+
76
+ export function gradientColor(t) {
77
+ t = t - Math.floor(t); // wrap to [0, 1)
78
+ const pos = t * (PALETTE.length - 1);
79
+ const i = Math.floor(pos);
80
+ const j = Math.min(i + 1, PALETTE.length - 1);
81
+ return lerpColor(PALETTE[i], PALETTE[j], pos - i);
82
+ }
83
+
84
+ // ── Render functions ──────────────────────────────────────────────────
85
+
86
+ /**
87
+ * Render one row of the animated orb.
88
+ *
89
+ * Uses Gaussian glow, swirling motion, organic noise, pulsing breath,
90
+ * and an optional expanding energy ring for the intro burst.
91
+ */
92
+ function renderOrbLine(w, h, y, t, ringR) {
93
+ const cx = (w - 1) / 2;
94
+ const cy = (h - 1) / 2;
95
+ const R = Math.min(cx, cy) * 0.9;
96
+
97
+ // Global fade-in: 0→1 over first ~0.5s
98
+ const fadeIn = Math.min(1.0, t / 0.6);
99
+
100
+ let line = '';
101
+ for (let x = 0; x < w; x++) {
102
+ const dx = (x - cx) / R;
103
+ const dy = (y - cy) / R * 1.8; // terminal aspect ratio correction
104
+ const r = Math.sqrt(dx * dx + dy * dy);
105
+ const angle = Math.atan2(dy, dx);
106
+
107
+ // Gaussian glow — soft falloff from center
108
+ const glow = Math.exp(-Math.pow(r * 1.5, 2));
109
+
110
+ // Swirling motion — rotates based on distance and time
111
+ const swirl = Math.sin(r * 4 - t * 2 + angle * 2) * 0.3;
112
+
113
+ // Gentle pulse (breathing)
114
+ const pulse = Math.sin(t * 3) * 0.15 + 1.0;
115
+
116
+ // Organic noise for natural feel
117
+ const n = noise(dx * 3, dy * 3, t) * 0.2;
118
+
119
+ let intensity = glow * (1.0 + swirl + n) * pulse * fadeIn;
120
+
121
+ // Expanding energy ring (sweeps outward during intro burst)
122
+ if (ringR > 0) {
123
+ const distToRing = Math.abs(r - ringR);
124
+ const ringWidth = 0.3;
125
+ if (distToRing < ringWidth) {
126
+ const ringIntensity = (1.0 - distToRing / ringWidth) * 0.8 * fadeIn;
127
+ intensity = Math.max(intensity, ringIntensity);
128
+ }
129
+ }
130
+
131
+ intensity = Math.max(0, Math.min(1, intensity));
132
+
133
+ const charIdx = Math.floor(intensity * (DOT_CHARS.length - 1));
134
+ const ch = DOT_CHARS[charIdx];
135
+
136
+ if (ch === ' ' || intensity < 0.02) {
137
+ line += ' ';
138
+ } else {
139
+ // Color from brand gradient, position shifts with radius + time
140
+ const colorT = (r * 0.5 + t * 0.1) % 1.0;
141
+ const c = gradientColor(colorT);
142
+ // Fade RGB channels by intensity for natural glow falloff
143
+ line += chalk.rgb(
144
+ Math.max(0, Math.floor(c[0] * intensity)),
145
+ Math.max(0, Math.floor(c[1] * intensity)),
146
+ Math.max(0, Math.floor(c[2] * intensity)),
147
+ )(ch);
148
+ }
149
+ }
150
+ return line;
151
+ }
152
+
153
+ /**
154
+ * Render one row of the "QUOX" block font title.
155
+ *
156
+ * Each filled glyph pixel becomes a double-width block (██) with
157
+ * a flowing gradient that sweeps left-to-right over time.
158
+ */
159
+ function renderTitleRow(row, t) {
160
+ const text = 'QUOX';
161
+ let result = '';
162
+ let col = 0;
163
+ let totalWidth = 0;
164
+
165
+ // Calculate total glyph width for gradient positioning
166
+ for (const ch of text) {
167
+ const glyph = BLOCK_FONT[ch];
168
+ if (glyph) totalWidth += glyph[0].length + 1;
169
+ }
170
+
171
+ for (const ch of text) {
172
+ const glyph = BLOCK_FONT[ch];
173
+ if (!glyph) continue;
174
+ const glyphRow = glyph[row];
175
+ for (let i = 0; i < glyphRow.length; i++) {
176
+ const pos = (col + i) / totalWidth;
177
+ const colorT = (pos + t * 0.3) % 1.0;
178
+ const c = gradientColor(colorT);
179
+ if (glyphRow[i] === ' ') {
180
+ result += ' '; // double-width space
181
+ } else {
182
+ result += chalk.rgb(c[0], c[1], c[2])('\u2588\u2588'); // double-width block
183
+ }
184
+ }
185
+ result += ' '; // gap between letters
186
+ col += glyphRow.length + 1;
187
+ }
188
+ return result;
189
+ }
190
+
191
+ /**
192
+ * Render subtitle with per-character shimmer gradient.
193
+ */
194
+ function renderSubtitle(t) {
195
+ const text = 'C O M M A N D L I N E I N T E R F A C E';
196
+ let result = '';
197
+ for (let i = 0; i < text.length; i++) {
198
+ const pos = i / text.length + t * 0.2;
199
+ const c = gradientColor(pos);
200
+ result += chalk.rgb(c[0], c[1], c[2])(text[i]);
201
+ }
202
+ return result;
203
+ }
204
+
205
+ // ── Module-level replay prevention ────────────────────────────────────
206
+ let _hasPlayed = false;
207
+
208
+ /** Reset intro animation state (for tests). */
209
+ export function _resetIntroState() {
210
+ _hasPlayed = false;
211
+ }
212
+
213
+ // ── Component ─────────────────────────────────────────────────────────
214
+
215
+ /**
216
+ * IntroHeader — animated header with orb, block title, and subtitle.
217
+ *
218
+ * On first mount, plays a 2-second animation burst (20fps), then FREEZES
219
+ * on the final frame — the interval is cleared entirely, so there is no
220
+ * perpetual "breathing" re-render eating CPU in the background. Return
221
+ * visits (e.g. navigating back to the menu) render that same frozen
222
+ * frame statically, with no timer scheduled at all.
223
+ *
224
+ * Lite mode (QUOX_TUI_LITE env var truthy, or non-TTY stdout) skips the
225
+ * animated burst entirely and renders a static single-frame banner with
226
+ * no setInterval whatsoever — for constrained VMs / piped output.
227
+ *
228
+ * Responsive:
229
+ * - Terminal >= 30 rows: full orb + block title
230
+ * - Terminal >= 22 rows: block title only (no orb)
231
+ * - Terminal < 22 rows: simple text title
232
+ */
233
+ export function IntroHeader() {
234
+ const { stdout } = useStdout();
235
+ const termRows = stdout?.rows || 24;
236
+
237
+ // Lite mode: no animation, no timers — a single static render.
238
+ const liteMode = Boolean(process.env.QUOX_TUI_LITE) || stdout?.isTTY === false;
239
+
240
+ const skip = _hasPlayed;
241
+ const [frame, setFrame] = useState((skip || liteMode) ? FAST_FRAMES + 10 : 0);
242
+
243
+ useEffect(() => {
244
+ if (liteMode) {
245
+ // Static banner — never schedules a timer.
246
+ _hasPlayed = true;
247
+ return undefined;
248
+ }
249
+
250
+ if (skip) {
251
+ // Already played this process — render the frozen final frame
252
+ // statically, no timer scheduled (no perpetual breathing).
253
+ return undefined;
254
+ }
255
+
256
+ // First visit: play the burst once, then freeze permanently.
257
+ _hasPlayed = true;
258
+ let f = 0;
259
+ const id = setInterval(() => {
260
+ f++;
261
+ setFrame(f);
262
+ if (f >= FAST_FRAMES) {
263
+ // Burst complete — clear the interval entirely. No slow-breathing
264
+ // phase follows; the final frame simply stays on screen.
265
+ clearInterval(id);
266
+ }
267
+ }, FAST_MS);
268
+
269
+ return () => clearInterval(id);
270
+ }, [liteMode]);
271
+
272
+ const t = frame * 0.08;
273
+
274
+ // Responsive thresholds
275
+ const showOrb = termRows >= 30;
276
+ const showBlockTitle = termRows >= 22;
277
+
278
+ // Stagger timing (instant on replay / lite mode)
279
+ const titleReady = frame >= 8 || skip || liteMode;
280
+ const subtitleReady = frame >= 14 || skip || liteMode;
281
+
282
+ // Expanding energy ring (first 1.5s of the intro burst only)
283
+ const ringR = (!skip && !liteMode && frame < 30) ? (frame / 30) * 2.0 : -1;
284
+
285
+ // ── Build the divider ───────────────────────────────────────────────────────
286
+ const divider = chalk.hex('#334155')('\u2500'.repeat(46));
287
+
288
+ // ── Lite mode: static, brand-coherent, single-frame banner ─────
289
+ if (liteMode) {
290
+ return (
291
+ <Box flexDirection="column" alignItems="center">
292
+ <Text color={colors.primary} bold>QUOX</Text>
293
+ <Text color={colors.dim}>COMMAND LINE INTERFACE</Text>
294
+ <Text>{divider}</Text>
295
+ </Box>
296
+ );
297
+ }
298
+
299
+ return (
300
+ <Box flexDirection="column" alignItems="center">
301
+ {/* ── Animated orb ───────────────────────────────────────── */}
302
+ {showOrb && Array.from({ length: ORB_H }, (_, y) => (
303
+ <Text key={`o${y}`}>{renderOrbLine(ORB_W, ORB_H, y, t, ringR)}</Text>
304
+ ))}
305
+
306
+ {/* ── Block title "QUOX" ─────────────────────────────────── */}
307
+ {titleReady && showBlockTitle && (
308
+ <Box flexDirection="column" alignItems="center">
309
+ {Array.from({ length: 5 }, (_, row) => (
310
+ <Text key={`t${row}`}>{renderTitleRow(row, t)}</Text>
311
+ ))}
312
+ </Box>
313
+ )}
314
+
315
+ {/* ── Simple title fallback (small terminals) ────────────── */}
316
+ {titleReady && !showBlockTitle && (
317
+ <Box>
318
+ <Text color="#3B82F6" bold>QUOX</Text>
319
+ <Text color="#71717A"> Command Line Interface</Text>
320
+ </Box>
321
+ )}
322
+
323
+ {/* ── Subtitle + divider ─────────────────────────────────── */}
324
+ {subtitleReady && (
325
+ <Box flexDirection="column" alignItems="center">
326
+ <Text>{renderSubtitle(t)}</Text>
327
+ <Text>{divider}</Text>
328
+ </Box>
329
+ )}
330
+ </Box>
331
+ );
332
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Custom Node.js module hook for JSX transformation.
3
+ * Uses esbuild's transform API to convert JSX to plain JS on-the-fly.
4
+ *
5
+ * Registered via Module.register() in index.js before any .jsx imports.
6
+ */
7
+
8
+ import { readFile } from 'node:fs/promises';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { transformSync } from 'esbuild';
11
+
12
+ export async function load(url, context, nextLoad) {
13
+ if (url.endsWith('.jsx')) {
14
+ const filePath = fileURLToPath(url);
15
+ const source = await readFile(filePath, 'utf8');
16
+
17
+ const { code } = transformSync(source, {
18
+ loader: 'jsx',
19
+ jsx: 'automatic',
20
+ format: 'esm',
21
+ sourcefile: filePath,
22
+ });
23
+
24
+ return {
25
+ format: 'module',
26
+ source: code,
27
+ shortCircuit: true,
28
+ };
29
+ }
30
+
31
+ return nextLoad(url, context);
32
+ }