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,79 @@
1
+ /**
2
+ * Composer ring (owner-approved lab prototype `quox-intro-lab rules`, plus the green writing sweep from
3
+ * `quox-intro-lab fx`). The two composer rules are one light ring: top rule left to right is the first half
4
+ * of the perimeter, bottom rule right to left the second. The gradient is the quox.ai panel ring:
5
+ * conic(cyan .85, purple .8 @35%, transparent @60%, teal .55 @85%, cyan .85). ~1 revolution / 2.5 s.
6
+ * Modes: work (cyan/purple), write (greens, while answer text streams), wait (slower amber, approval held).
7
+ * When the turn ends the sweep eases to a stop over ~400 ms. Pure state stepping; no timers here.
8
+ */
9
+ import { mix, smooth, hexRgb } from './color.js';
10
+
11
+ export const PALS = {
12
+ work: [[0x38, 0xbd, 0xf8], [0xa8, 0x55, 0xf7], [0x06, 0xb6, 0xd4]],
13
+ write: [[0x22, 0xc5, 0x5e], [0x4a, 0xde, 0x80], [0x16, 0xa3, 0x4a]],
14
+ wait: [[0xf5, 0x9e, 0x0b], [0xea, 0x58, 0x0c], [0xb4, 0x53, 0x09]],
15
+ };
16
+ const STOPS = [[0, 0, 0.85], [0.35, 1, 0.8], [0.6, 0, 0], [0.85, 2, 0.55], [1, 0, 0.85]];
17
+ export const DIM = [58, 66, 82];
18
+ export const OMEGA = 360 / 2.5; // deg/s while working
19
+ export const SPEED = { work: 1, write: 1, wait: 0.3 };
20
+ export const SETTLE_S = 0.4, RAMP_S = 0.15, BLEND_S = 0.2;
21
+
22
+ export const initialRing = () => ({ phase: 'idle', angle: 0, k: 0, om: 0, wt: { work: 1, write: 0, wait: 0 }, at: null, settleAt: null, from: null });
23
+
24
+ /** Colour + alpha of the ring at perimeter position u in [0,1) for palette weights wt. */
25
+ export function ringAt(u, wt) {
26
+ let i = 0; while (i < STOPS.length - 2 && u > STOPS[i + 1][0]) i++;
27
+ const [p0, c0, a0] = STOPS[i], [p1, c1, a1] = STOPS[i + 1];
28
+ const t = (u - p0) / (p1 - p0 || 1);
29
+ const col = (c) => [0, 1, 2].map((ch) => wt.work * PALS.work[c][ch] + wt.write * PALS.write[c][ch] + wt.wait * PALS.wait[c][ch]);
30
+ const A = a0 + (a1 - a0) * t;
31
+ const pm = mix(col(c0).map((v) => v * a0), col(c1).map((v) => v * a1), t); // premultiplied: the clear stop does not tint
32
+ return [A ? pm.map((v) => v / A) : col(c0), A];
33
+ }
34
+
35
+ /**
36
+ * Advance the ring to time `now` (ms). active = a turn is running; mode = 'work' | 'write' | 'wait'.
37
+ * Returns a new state; `animating` tells the clock whether frames are still wanted.
38
+ */
39
+ export function ringStep(prev, { active, mode = 'work', now }) {
40
+ const s = { ...(prev || initialRing()), wt: { ...(prev || initialRing()).wt } };
41
+ const dt = s.at == null ? 0 : Math.max(0, Math.min(0.25, (now - s.at) / 1000));
42
+ s.at = now;
43
+ if (active) {
44
+ if (s.phase !== 'run') s.phase = 'run';
45
+ s.om = OMEGA * (SPEED[mode] ?? 1);
46
+ s.k = Math.min(1, s.k + dt / RAMP_S);
47
+ for (const m of Object.keys(s.wt)) s.wt[m] += ((m === mode ? 1 : 0) - s.wt[m]) * Math.min(1, dt / BLEND_S);
48
+ } else if (s.phase === 'run') {
49
+ s.phase = 'settle'; s.settleAt = now; s.from = { om: s.om, k: s.k };
50
+ }
51
+ if (s.phase === 'settle') {
52
+ const p = (now - s.settleAt) / 1000 / SETTLE_S;
53
+ if (p >= 1) { s.phase = 'idle'; s.k = 0; s.om = 0; } else { s.om = s.from.om * (1 - p) ** 2; s.k = s.from.k * (1 - smooth(p)); }
54
+ }
55
+ s.angle = (s.angle + s.om * dt) % 360;
56
+ return s;
57
+ }
58
+ export const ringAnimating = (s) => Boolean(s) && s.phase !== 'idle';
59
+
60
+ /** Per-cell rgb for one rule of `cols` cells. base = the rule's static colour (hex or rgb). Pure. */
61
+ export function ruleColors(cols, top, s, base) {
62
+ const b = Array.isArray(base) ? base : hexRgb(base);
63
+ const out = new Array(cols);
64
+ for (let x = 0; x < cols; x++) {
65
+ if (!s || s.k <= 0) { out[x] = b; continue; }
66
+ const pos = top ? (x + 0.5) / (2 * cols) : (cols + (cols - 1 - x) + 0.5) / (2 * cols);
67
+ const u = (((pos - s.angle / 360) % 1) + 1) % 1;
68
+ const [c, a] = ringAt(u, s.wt);
69
+ out[x] = mix(b, mix(DIM, c, a), s.k);
70
+ }
71
+ return out;
72
+ }
73
+
74
+ /** Which ring mode a live turn is in, from what the client actually knows. */
75
+ export function ringMode({ approvals = 0, act = null } = {}) {
76
+ if (approvals > 0 || act?.tone === 'wait') return 'wait';
77
+ if (act?.word === 'Writing') return 'write';
78
+ return 'work';
79
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * QUOXON-MOUSE (owner 2026-10-15: "when I click on '◆ QLOOP-ENGINE' enter /say [slot]", "select a chunk of text I
3
+ * typed and delete it"). Optional, OFF by default: mouse tracking makes the terminal send clicks to Quox, so
4
+ * selecting or copying text then needs Shift+drag in most terminals (said when it is turned on).
5
+ *
6
+ * Pure parts only (the app wires them):
7
+ * - MOUSE_ON / MOUSE_OFF: SGR button tracking with drag (1000 + 1002 + 1006). Never any-motion (1003).
8
+ * - createScreenMirror: what Ink has put on the screen, as plain lines. Fed every string Ink writes (term.js), it
9
+ * models exactly Ink's output: text, newlines, eraseLines (frame repaints) and clear-terminal. The live frame is
10
+ * the last write; everything above it is static history. Wrapping is applied at the current width when read, so
11
+ * a resize reflows it the way the terminal does.
12
+ * - screenHit: a click's screen row -> the mirror line under it. Rows are counted up from the cursor (which Ink
13
+ * always leaves on the line after the frame), read with a cursor position report (DSR) at click time, so the
14
+ * map holds whether the frame sits at the bottom of a full screen or halfway down a fresh one.
15
+ * - classifyClick: what that line is (a ◆ sender, a card's answer key label, the shared ask's y/N/c).
16
+ * - composerIndexAt: a click in the composer rows -> a buffer index, through Ink's word wrap.
17
+ */
18
+ import { graphemes, composerFrom } from './textBuffer.js';
19
+
20
+ export const MOUSE_ON = '\x1b[?1000h\x1b[?1002h\x1b[?1006h';
21
+ export const MOUSE_OFF = '\x1b[?1006l\x1b[?1002l\x1b[?1000l';
22
+ export const DSR = '\x1b[6n';
23
+ /** Two presses on the same cell within this window are a double-click. */
24
+ export const DOUBLE_MS = 400;
25
+ /** How long a click waits for the terminal's cursor report before it is dropped. */
26
+ export const DSR_TIMEOUT_MS = 400;
27
+
28
+ export function mouseOnNote() {
29
+ return 'Mouse on. Click a ◆ sender to reply, click a card\'s y / n / c to answer, click or drag in your draft to place the cursor or select (Backspace deletes, typing replaces, double-click selects a word). While it is on your terminal sends clicks and the wheel to Quox: hold Shift to select or copy text (Option on macOS Terminal and iTerm2), and use Shift+wheel or the scrollbar to scroll back. /mouse off returns all of it.';
30
+ }
31
+
32
+ // ---- cell widths --------------------------------------------------------------------------------
33
+ const WIDE = /\p{Extended_Pictographic}/u;
34
+ const EAW = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-꓏가-힣豈-﫿︰-﹏＀-⦆¢-₩]/u;
35
+ /** Terminal cells one grapheme takes: 2 for emoji presentation and East Asian wide, else 1. */
36
+ export function cellWidth(g) {
37
+ const cp = g.codePointAt(0);
38
+ if (EAW.test(g)) return 2;
39
+ if (WIDE.test(g) && (cp >= 0x1f000 || g.includes('️'))) return 2;
40
+ return 1;
41
+ }
42
+ const cellsOf = (s) => graphemes(String(s)).reduce((n, x) => n + cellWidth(x.text), 0);
43
+ /** Grapheme index in `gs` covering 0-based cell `col` (gs.length when past the end). */
44
+ function cellToIndex(gs, col) {
45
+ let at = 0;
46
+ for (let i = 0; i < gs.length; i++) { const w = cellWidth(gs[i].text); if (col < at + w) return i; at += w; }
47
+ return gs.length;
48
+ }
49
+ /** 1-based start cell of grapheme index `i`. */
50
+ function indexToCell(gs, i) { let at = 1; for (let k = 0; k < i && k < gs.length; k++) at += cellWidth(gs[k].text); return at; }
51
+
52
+ // ---- screen mirror ------------------------------------------------------------------------------
53
+ const ANSI_RE = /\x1b\[[0-9;?<>=]*[ -\/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[()][0-9A-Za-z]|\x1b[=>78]/g;
54
+ const ERASE_RE = /^((?:\x1b\[2K\x1b\[1A)*\x1b\[2K\x1b\[G)/;
55
+ const CLEAR_RE = /\x1b\[2J|\x1bc/;
56
+
57
+ /** Wrap one plain line into rows of at most `cols` cells (what the terminal shows). */
58
+ function wrapRows(line, cols) {
59
+ if (!(cols > 0)) return [line];
60
+ const gs = graphemes(line);
61
+ if (!gs.length) return [''];
62
+ const out = []; let row = ''; let w = 0;
63
+ for (const g of gs) {
64
+ const cw = cellWidth(g.text);
65
+ if (w + cw > cols) { out.push(row); row = ''; w = 0; }
66
+ row += g.text; w += cw;
67
+ }
68
+ out.push(row);
69
+ return out;
70
+ }
71
+
72
+ export function createScreenMirror({ columns = () => null, cap = 3000 } = {}) {
73
+ let lines = ['']; // logical lines; the last one is the cursor line
74
+ let frameLen = 0; // logical lines written by the last write (the live frame, once Ink has written it)
75
+ const append = (text) => {
76
+ const parts = text.replace(ANSI_RE, '').replace(/\r/g, '').split('\n');
77
+ lines[lines.length - 1] += parts[0];
78
+ for (let i = 1; i < parts.length; i++) lines.push(parts[i]);
79
+ if (lines.length > cap) lines = lines.slice(lines.length - cap);
80
+ return parts.length - 1;
81
+ };
82
+ const m = {
83
+ /** One string Ink wrote, before any diffing. */
84
+ write(chunk) {
85
+ let s = String(chunk);
86
+ const clear = s.search(CLEAR_RE);
87
+ if (clear >= 0) {
88
+ const all = [...s.matchAll(/\x1b\[2J|\x1b\[3J|\x1b\[H|\x1bc/g)];
89
+ const last = all[all.length - 1];
90
+ s = s.slice(last.index + last[0].length);
91
+ lines = ['']; frameLen = 0;
92
+ }
93
+ const e = ERASE_RE.exec(s);
94
+ if (e) {
95
+ const n = (e[1].match(/\x1b\[2K/g) || []).length;
96
+ const drop = Math.min(n - 1, lines.length - 1);
97
+ lines.length -= drop;
98
+ lines[lines.length - 1] = '';
99
+ s = s.slice(e[1].length);
100
+ }
101
+ const plain = s.replace(ANSI_RE, '');
102
+ if (!plain) return;
103
+ const rows = append(s);
104
+ if (rows > 0) frameLen = rows;
105
+ },
106
+ /** Test helper: Ink repainting its frame as these lines. */
107
+ frame(fl) { m.write(`\x1b[2K\x1b[G${fl.join('\n')}\n`); },
108
+ /** Raw output we cannot place (a hand-over, a child on the terminal): forget what is above. */
109
+ reset() { lines = ['']; frameLen = 0; },
110
+ /** -> { rows, frameStart, staticRows, frameRows }: wrapped at the current width, cursor line excluded. */
111
+ snapshot() {
112
+ const cols = columns();
113
+ const body = lines.slice(0, -1);
114
+ const fl = Math.min(frameLen, body.length);
115
+ const stat = body.slice(0, body.length - fl).flatMap(l => wrapRows(l, cols));
116
+ const fr = body.slice(body.length - fl).flatMap(l => wrapRows(l, cols));
117
+ return { rows: [...stat, ...fr], frameStart: stat.length, staticRows: stat, frameRows: fr };
118
+ }
119
+ };
120
+ return m;
121
+ }
122
+
123
+ /** A click at 1-based (x, y) with the cursor on screen row `cursorRow` -> the line under it, or null. */
124
+ export function screenHit(snap, cursorRow, x, y) {
125
+ if (!snap || !(cursorRow > 0)) return null;
126
+ const idx = snap.rows.length - (cursorRow - y);
127
+ if (y >= cursorRow || idx < 0 || idx >= snap.rows.length) return null;
128
+ return {
129
+ text: snap.rows[idx], col: x, inFrame: idx >= snap.frameStart,
130
+ frameRow: idx - snap.frameStart, frameLines: snap.rows.slice(snap.frameStart), rowsBelow: snap.rows.slice(idx + 1)
131
+ };
132
+ }
133
+
134
+ // ---- what was clicked ---------------------------------------------------------------------------
135
+ const SENDER_RE = /^\s*◆ (\S+)( \(no reply address\))?/;
136
+ const CARD_KEY_RE = /(?<![\w/])([ync]) (approve|deny|chat|yes)(?![\w])/g;
137
+ const ASK_KEYS_RE = /(?<![\w/])(y|run)\/(N)\/(c)\s*$/;
138
+ const isAskRow = (r) => /^\s*▶ /.test(r) && (ASK_KEYS_RE.test(r) || /\b[ync] (approve|deny|chat)\b/.test(r));
139
+
140
+ /**
141
+ * -> { kind: 'sender', slot, who? } | { kind: 'key', key: 'y'|'n'|'c'|'run', source: 'approval'|'ask' } | null.
142
+ * A key needs the click ON its label. A card's labels count only in the live frame (a card is never answered from
143
+ * scrollback); an ask's y/N/c only when no newer ask sits below it on screen.
144
+ */
145
+ export function classifyClick(hit) {
146
+ if (!hit || typeof hit.text !== 'string') return null;
147
+ const t = hit.text;
148
+ const gs = graphemes(t);
149
+ const s = SENDER_RE.exec(t);
150
+ if (s) return s[2] ? { kind: 'sender', slot: null, who: s[1] } : { kind: 'sender', slot: s[1].toLowerCase() };
151
+ const within = (from, len) => {
152
+ const gi = graphemes(t.slice(0, from)).length;
153
+ const a = indexToCell(gs, gi); const b = a + cellsOf(t.slice(from, from + len));
154
+ return hit.col >= a && hit.col < b;
155
+ };
156
+ if (hit.inFrame) {
157
+ CARD_KEY_RE.lastIndex = 0;
158
+ let m;
159
+ while ((m = CARD_KEY_RE.exec(t))) if (within(m.index, m[0].length)) return { kind: 'key', key: m[1], source: 'approval' };
160
+ }
161
+ const k = ASK_KEYS_RE.exec(t);
162
+ if (k && /^\s*▶ /.test(t)) {
163
+ if ((hit.rowsBelow || []).some(isAskRow)) return null;
164
+ const base = k.index + k[0].indexOf(k[1]);
165
+ const yPos = base, nPos = yPos + k[1].length + 1, cPos = nPos + 2;
166
+ if (within(yPos, k[1].length)) return { kind: 'key', key: k[1] === 'run' ? 'run' : 'y', source: 'ask' };
167
+ if (within(nPos, 1)) return { kind: 'key', key: 'n', source: 'ask' };
168
+ if (within(cPos, 1)) return { kind: 'key', key: 'c', source: 'ask' };
169
+ }
170
+ return null;
171
+ }
172
+
173
+ // ---- the composer -------------------------------------------------------------------------------
174
+ const isRule = (r) => /^\s*─/.test(r || '');
175
+ const isPrompt = (r) => /^› /.test(r || '') || r === '›';
176
+
177
+ /**
178
+ * A click on frame row `row` (0-based), 1-based column `col` -> the buffer index under it, or null when the row is
179
+ * not one of the composer's. The composer is the "› " row right under a rule, down to the next rule.
180
+ */
181
+ export function composerIndexAt(frameLines, row, col, buf) {
182
+ let top = -1;
183
+ for (let i = frameLines.length - 1; i >= 1; i--) if (isPrompt(frameLines[i]) && isRule(frameLines[i - 1])) { top = i; break; }
184
+ if (top < 0 || row < top) return null;
185
+ let bottom = top;
186
+ while (bottom < frameLines.length && !isRule(frameLines[bottom])) bottom++;
187
+ if (row >= bottom) return null;
188
+ if (!buf.text) return 0;
189
+ const lines = buf.text.split('\n');
190
+ const starts = []; { let o = 0; for (const l of lines) { starts.push(o); o += l.length + 1; } }
191
+ const from = composerFrom(buf);
192
+ let r = top;
193
+ for (let li = from; li < lines.length && r < bottom; li++) {
194
+ const T = graphemes(lines[li]);
195
+ let p = 0; let first = true;
196
+ const rest = () => T.slice(p).some(g => !/\s/.test(g.text)); // trailing spaces never take a row of their own
197
+ while (r < bottom && (first || rest())) {
198
+ const R = graphemes(String(frameLines[r]).replace(/\s+$/, '')).slice(2);
199
+ while (!first && p < T.length && /\s/.test(T[p].text) && R.length && !/\s/.test(R[0].text)) p++;
200
+ const take = Math.min(R.length, T.length - p);
201
+ if (r === row) {
202
+ const gi = Math.min(T.length, p + Math.min(cellToIndex(R, Math.max(0, col - 3)), take));
203
+ return starts[li] + (gi < T.length ? T[gi].index : lines[li].length);
204
+ }
205
+ p += Math.max(take, 0); r++; first = false;
206
+ if (take <= 0) break;
207
+ }
208
+ }
209
+ return buf.text.length;
210
+ }
211
+
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Z2H-R2B: what Quox does when NO AI provider is connected. A turn is answered locally (no server call),
3
+ * with a full friendly card; nothing here names one vendor as the default. "add a host" has a deterministic
4
+ * path that needs no AI.
5
+ */
6
+ import { CHOICES } from './model.js';
7
+ import { providerState } from './providers.js';
8
+
9
+ export const NO_AI_STATUS = 'no AI connected';
10
+ export const NO_AI_CARD = [
11
+ 'No AI is connected yet. Pick one:',
12
+ '/connect openai (recommended), /connect anthropic, /connect ollama <host>, or /connect for the list.'
13
+ ];
14
+ export const HOSTS_USAGE = '/hosts add';
15
+
16
+ /** -> true when at least one provider is connected, false when readable and none is, null when nothing could be read. */
17
+ export function anyProviderConnected(vault, caps) {
18
+ let known = false;
19
+ for (const c of CHOICES) {
20
+ const st = providerState({ cap: c.cap || c.id, vault: c.vault }, caps, vault).state;
21
+ if (st === 'connected') return true;
22
+ if (st !== 'unknown') known = true;
23
+ }
24
+ return known ? false : null;
25
+ }
26
+
27
+ export const asksAddHost = (text) => /^\s*(?:please\s+)?(?:(?:i\s+(?:want|need)\s+to|can\s+you|help\s+me|let'?s)\s+)?(?:add|connect|enrol+|enroll|register|onboard)\s+(?:a|my|another|the)?\s*(?:new\s+)?(?:host|server|machine|node|box)s?\b/i.test(String(text || '').replace(/[.?!]+\s*$/, ''));
28
+
29
+ /** Z2H-R4: "watch my servers" and friends; watching starts with adding a host, which needs no AI. */
30
+ export const asksWatchHosts = (text) => /^\s*(?:please\s+)?(?:(?:i\s+(?:want|need)\s+(?:you\s+)?to|can\s+you|help\s+me)\s+)?(?:watch|monitor|keep\s+an\s+eye\s+on|look\s+after|keep\s+watch\s+(?:on|over))\s+(?:my|our|the)\s+(?:\w+\s+)?(?:servers?|hosts?|machines?|nodes?|boxes|fleet|homelab|infrastructure|infra)\b/i.test(String(text || '').replace(/[.?!]+\s*$/, ''));
31
+
32
+ /**
33
+ * The deterministic add-a-host lines. `base` = the server URL when known. `check` = the result of probing
34
+ * `${base}/install` ({ ok, status }) or null when it could not be read. Z2H-3c: a public install answered 503
35
+ * there (no QuoxAgent bundled), so the curl line we printed failed on the user's host; a failed probe now says
36
+ * so plainly instead of handing out a command that cannot work. Unreadable (null) keeps the old lines.
37
+ */
38
+ export function addHostLines(base, check = null) {
39
+ const b = String(base || '').replace(/\/+$/, '') || '<your-quox-url>';
40
+ if (check && check.ok === false && Number(check.status) >= 400) {
41
+ return [
42
+ `This Quox cannot enrol hosts yet: its host installer is not available (${b}/install answered HTTP ${check.status}).`,
43
+ 'The QuoxAgent installer is not bundled in this install, so the add-a-host command would fail on your machine.',
44
+ 'Nothing was changed on either machine.'
45
+ ];
46
+ }
47
+ return [
48
+ 'To add a host, no AI needed. On the machine you want to add, run:',
49
+ // REL-423: never `curl | sudo bash`: that pipe exits 0 when the download fails (TLS error, HTTP error) and runs
50
+ // whatever partial body arrived. Download to a temp file with curl -f, then run it, so a failure stops loudly.
51
+ // Two lines (shell continuation) so the TUI, which clips long lines, never cuts the command short.
52
+ `( f=$(mktemp) && trap 'rm -f "$f"' EXIT && curl -fsSL ${b}/install -o "$f" && \\`,
53
+ ` sudo bash "$f" --host-id <name> )`,
54
+ 'Self-signed certificate? Run quox fleet enrol <name>: it prints this with the certificate pinned.',
55
+ 'Then check it arrived with: quox fleet hosts'
56
+ ];
57
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * E4 agent card. Sources: the shipped assistant identity (name, tagline), the org agent record
3
+ * (description, status, model when its config carries one), the collector's agent -> tools map
4
+ * (GET /api/v1/tools/agents), slots (GET /api/v1/slots: a slot named after the agent, or assigned
5
+ * to it), and the agent's last AEE envelope (GET /aee/query?from=agent.<id>&limit=1) with its WARD
6
+ * receipt when verify-receipt answered. There is no per-agent model in the shipped config (models
7
+ * are routed per turn), so the model row appears only when the server record names one.
8
+ */
9
+ import { seg, fit, ago, short, boxWidth } from './model.js';
10
+ import { providerOf } from '../theme.js';
11
+
12
+ /** The model an org agent record names, if any: config_json.{model,provider}. */
13
+ export function modelOf(record) {
14
+ let cfg = record?.config_json ?? record?.config ?? null;
15
+ if (typeof cfg === 'string') { try { cfg = JSON.parse(cfg); } catch { cfg = null; } }
16
+ const model = cfg?.model || cfg?.llm?.model || null;
17
+ const provider = cfg?.provider || cfg?.llm?.provider || null;
18
+ return model || provider ? { model, provider } : null;
19
+ }
20
+
21
+ /** A slot belongs to an agent when it is named after it or assigned to it. */
22
+ export function slotFor(slots, id) {
23
+ const n = String(id || '').toLowerCase();
24
+ return (slots || []).find(s => String(s.name || '').toLowerCase() === n || String(s.assigned_to || '').toLowerCase() === n) || null;
25
+ }
26
+
27
+ /**
28
+ * d: { id, name, tagline, description, status, record, tools: [] | null, slot, last: envelope | null,
29
+ * lastErr, receipt: { ok, result } | null, now }
30
+ */
31
+ export function agentCard(d, cols) {
32
+ const now = d.now ?? Date.now();
33
+ const w = boxWidth(cols, 72), inner = w - 4, nw = w < 56;
34
+ const nm = String(d.name || d.id || 'agent');
35
+ const tag = d.tagline ? (nw ? '' : ` · ${d.tagline}`) : '';
36
+ const right = d.slot ? (nw ? `slot ${d.slot.name}` : `slot ${d.slot.name} · ${d.slot.state || 'unknown'}`) : '';
37
+ const rows = [];
38
+ const L = (label, value, role = 'value') => [seg(label.padEnd(6), 'muted'), ...(Array.isArray(value) ? value : [seg(fit(value, inner - 6), role)])];
39
+ if (d.description) rows.push(L('role', d.description));
40
+ if (d.status && d.status !== 'active') rows.push(L('state', d.status, 'warn'));
41
+ const m = modelOf(d.record);
42
+ if (m) {
43
+ const p = providerOf(m.provider, m.model);
44
+ rows.push(L('model', [seg(fit(m.model || m.provider, inner - 6), p ? `provider:${p}` : 'value')]));
45
+ }
46
+ if (Array.isArray(d.tools)) rows.push(L('tools', d.tools.length ? `${d.tools.length}: ${d.tools.join(', ')}` : 'none assigned'));
47
+ else if (d.toolsErr) rows.push(L('tools', `could not read (${d.toolsErr})`, 'warn'));
48
+ if (d.slot?.holder_session && d.slot.state === 'held') rows.push(L('held', `by ${d.slot.holder_session}${d.slot.last_heartbeat_at ? ` · heartbeat ${ago(d.slot.last_heartbeat_at, now)} ago` : ''}`, 'muted'));
49
+ if (d.last) {
50
+ const what = d.last.payload?.tool_name || d.last.intent || 'action';
51
+ const when = d.last.ts ? `${ago(d.last.ts, now)} ago` : '';
52
+ const r = d.receipt?.ok ? d.receipt.result : null;
53
+ if (r?.status === 'WITNESSED') rows.push(L('last', [seg('◉ ', 'proto:ward'), seg(fit(`${what} · WARD seq ${r.entry?.seq} witnessed · ${when}`, inner - 8), 'muted')]));
54
+ else {
55
+ const w8 = r?.status === 'NOT_WITNESSED' ? 'not witnessed yet' : r?.status === 'UNTIPPED' ? `WARD seq ${r.entry?.seq}, no signed tip yet` : d.receipt && !d.receipt.ok ? 'WARD did not answer' : 'witness not checked';
56
+ rows.push(L('last', [seg('· ', 'muted'), seg(fit(`${what} · ${w8} · ${when} · ${short(d.last.id, 14)}`, inner - 8), 'muted')]));
57
+ }
58
+ } else if (d.lastErr) rows.push(L('last', `could not read (${d.lastErr})`, 'warn'));
59
+ else rows.push(L('last', 'no recorded action', 'muted'));
60
+ return { box: { width: w, title: `● ${nm.toUpperCase()}${tag}`, titleRole: `agent:${d.id || nm}`, right, rightRole: 'muted', border: 'violet' }, rows };
61
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * E5 "While you were away": a factual digest since a moment, from GET /api/v1/away (collector). Every
3
+ * item carries the source that reported it and its time; nothing is summarised by a model. Sources the
4
+ * server could not read, or that went silent, come back in `blind` and are always named ("Can't see"),
5
+ * so an empty list never reads as an all clear it cannot vouch for.
6
+ */
7
+ import { seg, fit, hhmm, plural, boxWidth, parseTs } from './model.js';
8
+
9
+ /** Importance: approvals waiting first (they block work), then alerts, failures, then outcomes. */
10
+ export const RANK = {
11
+ approval_waiting: 0, alert: 1, turn_failed: 2, objective_failed: 2, workflow_failed: 2,
12
+ approval_resolved: 3, objective_finished: 4, workflow_finished: 4, decision: 5
13
+ };
14
+ const GLYPH = {
15
+ approval_waiting: ['⚠ ', 'warn'], alert: ['⚠ ', 'warn'], turn_failed: ['✖ ', 'error'], objective_failed: ['✖ ', 'error'],
16
+ workflow_failed: ['✖ ', 'error'], approval_resolved: ['✔ ', 'ok'], objective_finished: ['✔ ', 'ok'],
17
+ workflow_finished: ['✔ ', 'ok'], decision: ['◆ ', 'indigo']
18
+ };
19
+ const rankOf = (it) => RANK[it?.kind] ?? 6;
20
+
21
+ export function rankItems(items) {
22
+ return [...(items || [])].sort((a, b) => rankOf(a) - rankOf(b) || (parseTs(b.ts) ?? 0) - (parseTs(a.ts) ?? 0));
23
+ }
24
+
25
+ /** "5h", "45m", "2d" for the away window. */
26
+ export function awayFor(sinceTs, now) {
27
+ const t = parseTs(sinceTs);
28
+ if (t == null) return '';
29
+ const m = Math.max(0, Math.round((now - t) / 60000));
30
+ if (m < 60) return `${m}m`;
31
+ const h = Math.round(m / 60);
32
+ return h < 48 ? `${h}h` : `${Math.round(h / 24)}d`;
33
+ }
34
+
35
+ /** d: { since, items, blind, activity, compact?, max?, now } */
36
+ export function awayCard(d, cols) {
37
+ const now = d.now ?? Date.now();
38
+ const w = boxWidth(cols, 96), inner = w - 4;
39
+ const items = rankItems(d.items);
40
+ const max = d.compact ? (d.max ?? 5) : items.length;
41
+ const shown = items.slice(0, max);
42
+ const rows = [];
43
+ const win = awayFor(d.since, now);
44
+ if (!items.length) rows.push([seg(d.standing && d.standing.count > 0 ? 'Nothing new needed you.' : 'Nothing needed you.', 'muted')]);
45
+ for (const it of shown) {
46
+ const [g, role] = GLYPH[it.kind] || ['· ', 'muted'];
47
+ const meta = `${it.source || '?'} · ${hhmm(it.ts) || 'time unknown'}`;
48
+ if (d.compact) {
49
+ rows.push([seg(g, role), seg(fit(it.title, Math.max(8, inner - 2 - meta.length - 2)), 'value'), seg(` ${meta}`, 'muted')]);
50
+ } else {
51
+ rows.push([seg(g, role), seg(fit(it.title, inner - 2), 'value', rankOf(it) === 0)]);
52
+ rows.push([seg(' '), seg(fit(meta, inner - 2), 'muted')]);
53
+ }
54
+ }
55
+ if (items.length > shown.length) rows.push([seg(`+${items.length - shown.length} more · /away`, 'muted')]);
56
+ if (d.standing && d.standing.count > 0) rows.push([seg(fit(`${plural(d.standing.count, 'older item')} still need${d.standing.count === 1 ? 's' : ''} you · quox lucid`, inner), 'muted')]);
57
+ const act = d.activity;
58
+ if (act && act.total > 0 && !d.compact) {
59
+ const top = (act.agents || []).slice(0, 4).map(a => `${a.agent} ${a.count}`).join(', ');
60
+ rows.push([seg(' ')]);
61
+ rows.push([seg(fit(`${act.partial ? 'at least ' : ''}${plural(act.total, 'agent action')}${top ? `: ${top}` : ''}${act.source ? ` (${act.source})` : ''}`, inner), 'muted')]);
62
+ }
63
+ const blind = d.blind || [];
64
+ if (blind.length && d.compact) {
65
+ const what = blind.map(b => (b.reason ? `${b.source} (${b.reason})` : b.source)).join(', ');
66
+ rows.push([seg(fit(`Can't see: ${what}`, inner), 'faint')]);
67
+ } else if (blind.length) {
68
+ rows.push([seg("Can't see:", 'faint')]);
69
+ for (const b of blind) rows.push([seg(' '), seg(fit(`${b.source}${b.reason ? `: ${b.reason}` : ''}`, inner - 2), 'faint')]);
70
+ }
71
+ return { box: { width: w, title: `While you were away${win ? ` (${win})` : ''}`, titleRole: 'value', right: d.compact ? '' : `since ${hhmm(d.since)}`, rightRole: 'muted', border: items.some(i => rankOf(i) === 0) ? 'warn' : 'muted' }, rows };
72
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * E3 Crystal eligibility, computed from a workflow's REAL execution history against quoxflow's
3
+ * crystalliser gate. There is no read-only eligibility route (POST /api/skills/from-execution is the
4
+ * check AND the crystallisation, a write), so the rule is ported here verbatim from
5
+ * quoxflow/src/pipeline/crystalliser.ts (analyseExecutionHistory + extractVariables):
6
+ * >= 3 executions, >= 80% success, and every successful run follows one action sequence.
7
+ * Input mirrors that route's legacy path: completed + failed executions; a run's action log is
8
+ * output.action_log when the run recorded one, else the workflow definition's nodes (the route's
9
+ * synthetic log). The quoxflow route also reads its own execution-history table, which this
10
+ * client cannot see, so the card says what it counted.
11
+ */
12
+ import { seg, fit, short, boxWidth, plural } from './model.js';
13
+
14
+ export const MIN_EXECUTIONS = 3;
15
+ export const MIN_SUCCESS_RATE = 0.8;
16
+
17
+ /** crystalliser.ts analyseExecutionHistory, same order of checks and same reasons. */
18
+ export function analyseExecutionHistory(executions) {
19
+ const count = executions.length;
20
+ const successCount = executions.filter(e => e.success).length;
21
+ const successRate = count > 0 ? successCount / count : 0;
22
+ if (count < MIN_EXECUTIONS) return { ready: false, reason: `Insufficient executions (${count}/${MIN_EXECUTIONS} required)`, confidence: 0, executionCount: count, successRate };
23
+ if (successRate < MIN_SUCCESS_RATE) return { ready: false, reason: `Low success rate (${(successRate * 100).toFixed(0)}% < ${MIN_SUCCESS_RATE * 100}% required)`, confidence: 0, executionCount: count, successRate };
24
+ const signatures = new Set(executions.filter(e => e.success).map(e => e.actionLog.map(a => a.action).join(',')));
25
+ if (signatures.size > 1) return { ready: false, reason: `Inconsistent action structure across runs (${signatures.size} different patterns)`, confidence: 0, executionCount: count, successRate };
26
+ const confidence = Math.min(Math.min(count / 10, 1) * 0.3 + successRate * 0.5 + 0.2, 1.0);
27
+ return { ready: true, confidence, executionCount: count, successRate };
28
+ }
29
+
30
+ /** crystalliser.ts extractVariables: params that vary between successful runs, by action position. */
31
+ export function extractVariables(executions) {
32
+ const ok = executions.filter(e => e.success && e.actionLog.length > 0);
33
+ if (ok.length < 2) return [];
34
+ const vars = [];
35
+ ok[0].actionLog.forEach((first, idx) => {
36
+ for (const key of Object.keys(first.params || {})) {
37
+ const values = ok.map(e => e.actionLog[idx]?.params?.[key]).filter(v => v !== undefined);
38
+ if (new Set(values.map(v => JSON.stringify(v))).size > 1) vars.push({ name: key, values });
39
+ }
40
+ });
41
+ return vars;
42
+ }
43
+
44
+ /** Real executions (+ optional definition) -> crystalliser ExecutionRecords. */
45
+ export function toRecords(executions, def) {
46
+ const synthetic = Array.isArray(def?.nodes) ? def.nodes.map(n => ({ action: n.type, params: n.config ?? n.data ?? {} })) : null;
47
+ return executions
48
+ .filter(e => e && (e.status === 'completed' || e.status === 'failed'))
49
+ .map(e => {
50
+ const log = Array.isArray(e.output?.action_log) ? e.output.action_log.map(a => ({ action: a.action, params: a.params || {} })) : synthetic;
51
+ return { id: e.id, success: e.status === 'completed', actionLog: log || [], logKnown: Array.isArray(e.output?.action_log) };
52
+ });
53
+ }
54
+
55
+ /** Successful runs still needed so both the count and the rate pass (consecutive successes from now). */
56
+ export function runsNeeded(count, successes) {
57
+ for (let k = 0; k < 10000; k++) {
58
+ const n = count + k, s = successes + k;
59
+ if (n >= MIN_EXECUTIONS && s / n >= MIN_SUCCESS_RATE) return k;
60
+ }
61
+ return null;
62
+ }
63
+
64
+ /**
65
+ * d: { workflow: { id, name }, executions: [...completed and failed], def }
66
+ * -> view { box, rows }
67
+ */
68
+ export function crystalCard(d, cols) {
69
+ const w = boxWidth(cols, 66), inner = w - 4, nw = w < 58;
70
+ const wf = d.workflow || {};
71
+ const recs = toRecords(d.executions || [], d.def);
72
+ const a = analyseExecutionHistory(recs);
73
+ const n = recs.length, s = recs.filter(r => r.success).length;
74
+ const pct = n ? Math.round((s / n) * 100) : 0;
75
+ const okRuns = recs.filter(r => r.success);
76
+ const sigs = new Set(okRuns.map(r => r.actionLog.map(x => x.action).join(',')));
77
+ const unknownLogs = okRuns.some(r => !r.logKnown);
78
+ const rows = [];
79
+ const check = (pass, name, value) => [seg(pass === null ? '○' : pass ? '✔' : '✖', pass === null ? 'faint' : pass ? 'ok' : 'warn'), seg(' '), seg(name.padEnd(nw ? 10 : 14), pass === null ? 'muted' : 'value'), seg(fit(value, inner - (nw ? 12 : 16)), 'muted')];
80
+ rows.push(check(n >= MIN_EXECUTIONS, 'runs', nw ? `${n} (needs ≥ ${MIN_EXECUTIONS})` : `${n}`.padEnd(11) + `needs ≥ ${MIN_EXECUTIONS}`));
81
+ rows.push(check(n ? s / n >= MIN_SUCCESS_RATE : false, nw ? 'success' : 'success rate', nw ? `${pct}% ${s}/${n} (≥ 80%)` : `${pct}% ${s}/${n} needs ≥ 80%`));
82
+ if (!okRuns.length) rows.push(check(null, 'structure', 'no successful run to compare'));
83
+ else if (unknownLogs && sigs.size === 1) rows.push(check(true, 'structure', nw ? `${okRuns.length}/${okRuns.length} same steps` : `${okRuns.length}/${okRuns.length} same steps (definition, no run log)`));
84
+ else rows.push(check(sigs.size === 1, 'structure', sigs.size === 1 ? (nw ? `${okRuns.length}/${okRuns.length} identical` : `${okRuns.length}/${okRuns.length} runs, identical action sequence`) : `${sigs.size} different action patterns`));
85
+ const vars = a.ready ? extractVariables(recs) : [];
86
+ if (a.ready) rows.push(check(true, 'variables', vars.length ? `${vars.map(v => v.name).join(', ')}${nw ? '' : ' (vary between runs)'}` : 'none vary between runs'));
87
+
88
+ const crystal = (d.executions || []).find(e => e?.output?.crystal_id);
89
+ let verdict;
90
+ if (crystal) verdict = [seg(fit(`Already runs as Crystal ${short(crystal.output.crystal_id, 10)}${crystal.output.execution_mode ? ` (${crystal.output.execution_mode} replay)` : ''}`, inner), 'teal', true)];
91
+ else if (a.ready) verdict = [seg(nw ? 'Eligible · meets the gate' : 'Eligible · meets the crystalliser gate', 'teal', true)];
92
+ else if (/Inconsistent/.test(a.reason || '')) verdict = [seg(fit(`Not eligible: runs follow ${sigs.size} different action patterns`, inner), 'warn', true)];
93
+ else {
94
+ const k = runsNeeded(n, s);
95
+ verdict = [seg(fit(k == null ? `Not eligible yet: ${a.reason}` : `Not eligible yet: needs ${plural(k, 'more successful run')}`, inner), 'warn', true)];
96
+ }
97
+ rows.push(verdict);
98
+ rows.push([seg(fit(nw ? `from ${plural(n, 'recorded run')}` : `from ${plural(n, 'recorded run')} · quoxflow's rule, ≤ 500 per status`, inner), 'faint')]);
99
+ return { box: { width: w, title: nw ? 'Crystal candidate' : `Crystal candidate · ${wf.name || wf.id || ''}`, right: '', border: 'teal' }, rows, analysis: a };
100
+ }