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,45 @@
1
+ /**
2
+ * The secure /connect card. Shown in place of the composer while it is open. It renders only the
3
+ * mask from the secret box, never the value.
4
+ */
5
+ import React from 'react';
6
+ import { Box, Text } from 'ink';
7
+ import { c, clip } from './theme.js';
8
+ import { Rule, AskCard } from './render.js';
9
+
10
+ const h = React.createElement;
11
+
12
+ /** card: { provider: PROVIDERS entry, phase: 'input'|'saving'|'consent', error?: string, question?: string }, mask: string */
13
+ export function ConnectCard({ card, mask, cols }) {
14
+ const p = card.provider;
15
+ const w = Math.max(10, cols - 4);
16
+ // QUOXON-Z2H lane R: the one consent question after a cloud connect (y records the user's own OK).
17
+ if (card.phase === 'consent') return h(AskCard, { text: card.question, cols }); // QUOXON-ASK: the same card as an approval
18
+ // #1288: an org admin's own Ollama on a private address: y/N, then the save with a confirm for that origin.
19
+ if (card.phase === 'private') return h(AskCard, { text: card.question, cols });
20
+ if (card.phase === 'saving' && card.privateOffer) {
21
+ return h(Box, { flexDirection: 'column', width: cols, marginTop: 1 },
22
+ h(Text, { color: c('dim') }, clip(` ◐ checking ${card.privateOffer.host} answers as Ollama, then saving…`, cols)));
23
+ }
24
+ if (card.phase === 'saving' && card.consentType) {
25
+ return h(Box, { flexDirection: 'column', width: cols, marginTop: 1 },
26
+ h(Rule, { cols, color: 'sky' }),
27
+ h(Text, { color: c('sky'), bold: true }, clip(`Ⓠ ${p.name} is connected · one question`, cols)),
28
+ h(Text, { color: c('text'), wrap: 'wrap' }, ` Your messages and any tool results go to ${p.name}, a third party, when it answers. Nothing is sent until you say yes. You can withdraw this any time (Settings > Privacy).`),
29
+ h(Text, { color: c('warn'), bold: true }, clip(` ${card.question}`, cols)),
30
+ h(Text, { color: c('text') }, card.phase === 'saving' ? clip(' ◐ recording your OK…', cols) : clip(' y yes · n or Enter no · Esc no', cols)),
31
+ h(Rule, { cols, color: 'sky' }));
32
+ }
33
+ return h(Box, { flexDirection: 'column', width: cols, marginTop: 1 },
34
+ h(Rule, { cols, color: 'sky' }),
35
+ h(Text, { color: c('sky'), bold: true }, clip(`Ⓠ Connect ${p.name}${card.subtitle ? ` · ${card.subtitle}` : ''} · goes straight to your org vault, never into chat`, cols)),
36
+ h(Text, { color: c('text') }, clip(` ${p.why}`, cols)),
37
+ p.disclosure ? h(Text, { color: c('warn'), wrap: 'wrap' }, ` ${p.disclosure}`) : null,
38
+ h(Text, { color: c('text') }, clip(` Get a key: ${p.keyUrl}`, cols)),
39
+ h(Text, { color: c('text') }, clip(` key ${mask || ''}${card.phase === 'input' ? '▏' : ''}`, cols)),
40
+ card.error ? h(Text, { color: c('warn') }, clip(` ${card.error}`, w + 2)) : null,
41
+ h(Text, { color: c('dim') }, card.phase === 'saving'
42
+ ? clip(` ◐ testing with ${p.name}, then saving…`, cols)
43
+ : clip(' paste your key · Enter test and save · Esc cancel · keys typed here stay in this card', cols)),
44
+ h(Rule, { cols, color: 'sky' }));
45
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * QUOXON-CONSENT: AI data sharing consent, in words a stuck user can act on.
3
+ * Consent is PERSONAL (one record per user, POST /api/consent), so the person typing grants it; no admin is
4
+ * involved. Cloud providers that receive the conversation need it; a local model (Ollama) never does.
5
+ * This file only words things and shapes the two API calls. The gate itself is the server's.
6
+ */
7
+ export const PRIVACY_PATH = '/settings#privacy';
8
+ const LABELS = { openai: 'OpenAI', perplexity: 'Perplexity', gemini: 'Google Gemini', google: 'Google', openrouter: 'OpenRouter', huggingface: 'Hugging Face', groq: 'Groq', together: 'Together AI', openai_compatible: 'your custom hosted endpoint' };
9
+ export const providerLabel = (p) => LABELS[String(p || '').toLowerCase()] || (p ? String(p) : 'your cloud AI provider');
10
+
11
+ // Keep in step with services/collector/lib/consentVerification.js THIRD_PARTY_PROVIDERS. Anthropic and local models are not in it.
12
+ export const CONSENT_PROVIDERS = ['openai', 'perplexity', 'gemini', 'google', 'openrouter', 'huggingface', 'groq', 'together', 'openai_compatible'];
13
+ export const needsConsent = (type) => CONSENT_PROVIDERS.includes(String(type || '').toLowerCase());
14
+
15
+ export const isConsentError = (ev) => ev?.type === 'error' && ev?.error === 'ai_consent_required';
16
+
17
+ /** The one-line headline for the error block (the card's first line). */
18
+ export function consentErrorText(ev) {
19
+ if (ev?.retryable) return 'Quox could not check your AI data sharing consent just now, so nothing was sent. Try again in a moment.';
20
+ return `Quox needs your OK before sending this conversation to ${providerLabel(ev?.provider)}.`;
21
+ }
22
+
23
+ /** The lines under the headline: what is shared, how to grant it, the local alternative, the dashboard link. Short lines, no clipping. */
24
+ export function consentHintLines(ev, dashboardBase) {
25
+ if (ev?.retryable) return [];
26
+ const who = providerLabel(ev?.provider);
27
+ const prov = ev?.provider || '<provider>';
28
+ const why = ev?.reason === 'consent_expired' ? 'Your earlier OK is out of date and needs renewing.'
29
+ : ev?.reason === 'provider_blocked' ? `You have blocked ${who} in your privacy settings; granting turns it back on.` : null;
30
+ const lines = [];
31
+ if (why) lines.push(why);
32
+ lines.push(`Shared: your messages and any tool results go to ${who}, a third party. Nothing was sent.`);
33
+ lines.push('Grant it: /consent grant (records your OK and sends this message again), or quox consent grant --provider ' + prov + ' in a shell.');
34
+ lines.push('Consent is personal, so you can give it yourself; no admin is needed.');
35
+ if (dashboardBase) lines.push(`Or in the dashboard: ${String(dashboardBase).replace(/\/$/, '')}${ev?.grant?.dashboard_path || PRIVACY_PATH}`);
36
+ lines.push('No cloud at all: a local model (Ollama) needs no consent. /connect ollama');
37
+ return lines;
38
+ }
39
+
40
+ /** GET /api/consent record -> { granted, providers: { name: bool } }. */
41
+ export function consentState(rec) {
42
+ const providers = rec?.provider_consent && typeof rec.provider_consent === 'object' ? rec.provider_consent : {};
43
+ return { granted: Boolean(rec?.ai_data_sharing_consent), providers };
44
+ }
45
+
46
+ /** Is `provider` allowed to receive data under this record? Mirrors the server: overall consent on, provider not set false. */
47
+ export const consentCovers = (rec, provider) => {
48
+ const s = consentState(rec);
49
+ return s.granted && s.providers[String(provider || '').toLowerCase()] !== false;
50
+ };
51
+
52
+ export function consentStatusLines(rec, { provider = null } = {}) {
53
+ const s = consentState(rec);
54
+ const names = Object.entries(s.providers).map(([k, v]) => `${k} ${v ? 'yes' : 'blocked'}`);
55
+ const lines = [[s.granted ? '✔ AI data sharing: you have given your OK.' : '· AI data sharing: not given yet, so nothing is sent to a cloud AI.', s.granted ? 'ok' : 'warn']];
56
+ if (names.length) lines.push([`Per provider: ${names.join(', ')}.`]);
57
+ if (!s.granted) lines.push([`/consent grant${provider ? ` ${provider}` : ''} gives it. Your messages and tool results then go to the cloud AI you chose. A local model (Ollama) never needs it.`]);
58
+ return lines;
59
+ }
60
+
61
+ /** The body for POST /api/consent: keeps every other provider's setting (the server replaces the map wholesale). */
62
+ export function grantBody(rec, provider) {
63
+ const keep = consentState(rec).providers;
64
+ const p = String(provider || '').toLowerCase();
65
+ return { ai_data_sharing_consent: true, provider_consent: { ...keep, ...(p ? { [p]: true } : {}) } };
66
+ }
67
+
68
+ /** What to say after /connect saves a hosted key and consent is not yet in place. */
69
+ export function connectConsentNote(providerId) {
70
+ return `One more step before it can answer: ${providerLabel(providerId)} receives your messages, so Quox needs your OK. /consent grant ${providerId}`;
71
+ }
72
+
73
+ /**
74
+ * QUOXON-Z2H lane R: the plain-text message `quox --print` gives for ai_consent_required, naming the exact
75
+ * shell command (there is no /consent in a one-shot run). profile = the -p the user named, if any.
76
+ */
77
+ export function consentPrintMessage(ev, profile = null) {
78
+ if (ev?.retryable) return consentErrorText(ev);
79
+ const who = providerLabel(ev?.provider);
80
+ const prov = ev?.provider || '<provider>';
81
+ const p = profile ? `-p ${profile} ` : '';
82
+ return `${consentErrorText(ev)} Nothing was sent.\n`
83
+ + ` Your messages and any tool results would go to ${who}, a third party. Consent is personal; no admin is needed.\n`
84
+ + ` Grant it, then send again: quox ${p}consent grant --provider ${prov}\n`
85
+ + ' Or use a local model (Ollama), which needs no consent.';
86
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Where am I: the slot this session holds and the agent attached to it, for the
3
+ * status line. Reads directory names and slot labels only, never token contents.
4
+ * Layout mirrors lib/core/callerSource.js (~/.quox/attach/sessions/claude-<pid>/<agent>/,
5
+ * ~/.quox/held-slots.json sessionKey claude-<pid>).
6
+ */
7
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+
11
+ const clean = (v, n = 32) => String(v == null ? '' : v).replace(/[^\w .:@+\-/~]/g, '').trim().slice(0, n) || null;
12
+
13
+ const defaults = {
14
+ readName: (pid) => { try { return readFileSync(`/proc/${pid}/comm`, 'utf8').trim(); } catch { return null; } },
15
+ readPpid: (pid) => {
16
+ try {
17
+ const s = readFileSync(`/proc/${pid}/stat`, 'utf8');
18
+ const p = parseInt(s.slice(s.lastIndexOf(')') + 2).trim().split(/\s+/)[1], 10);
19
+ return Number.isFinite(p) ? p : null;
20
+ } catch { return null; }
21
+ },
22
+ attachRoot: () => join(homedir(), '.quox', 'attach', 'sessions'),
23
+ heldSlotsPath: () => join(homedir(), '.quox', 'held-slots.json'),
24
+ readdir: (d) => { try { return readdirSync(d, { withFileTypes: true }); } catch { return []; } },
25
+ readJson: (p) => { try { return existsSync(p) ? JSON.parse(readFileSync(p, 'utf8')) : null; } catch { return null; } },
26
+ exists: existsSync,
27
+ pid: () => process.pid
28
+ };
29
+
30
+ function claudeAncestor(d, start) {
31
+ let pid = start;
32
+ for (let hop = 0; hop < 24; hop++) {
33
+ if (d.readName(pid) === 'claude') return pid;
34
+ const pp = d.readPpid(pid);
35
+ if (!pp || pp === pid || pid === 1) break;
36
+ pid = pp;
37
+ }
38
+ return null;
39
+ }
40
+
41
+ /** -> { slot: string|null, agent: string|null } ; env wins, then the attach/slot files of the claude ancestor. */
42
+ export function detectSlotAgent(env = process.env, overrides = {}) {
43
+ const d = { ...defaults, ...overrides };
44
+ let slot = clean(env.QUOX_SLOT);
45
+ let agent = clean(env.QUOX_AGENT);
46
+ if (slot && agent) return { slot, agent };
47
+ let pid = null;
48
+ try { pid = claudeAncestor(d, d.pid()); } catch { pid = null; }
49
+ if (pid) {
50
+ if (!agent) {
51
+ const dir = join(d.attachRoot(), `claude-${pid}`);
52
+ const found = d.readdir(dir).filter(e => e.isDirectory() && d.exists(join(dir, e.name, 'token')));
53
+ if (found.length) {
54
+ const kit = d.readJson(join(dir, found[0].name, 'kit.json'));
55
+ agent = clean(kit && kit.display_name) || clean(found[0].name);
56
+ }
57
+ }
58
+ if (!slot) {
59
+ const all = d.readJson(d.heldSlotsPath());
60
+ if (all && typeof all === 'object') {
61
+ for (const [name, e] of Object.entries(all)) {
62
+ if (e && e.sessionKey === `claude-${pid}`) { slot = clean(name); break; }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ return { slot, agent };
68
+ }
@@ -0,0 +1,34 @@
1
+ /** /copy helpers: pick the text and build the OSC 52 clipboard escape. */
2
+ import { stripMarks } from './palette.js';
3
+
4
+ export function osc52(text, env = process.env) {
5
+ const seq = `\x1b]52;c;${Buffer.from(stripMarks(text), 'utf8').toString('base64')}\x07`;
6
+ return env.TMUX ? `\x1bPtmux;${seq.replace(/\x1b/g, '\x1b\x1b')}\x1b\\` : seq;
7
+ }
8
+
9
+ /** Fenced code blocks (``` or ~~~) of a markdown string, in order, without the fence lines. */
10
+ export function codeBlocks(md) {
11
+ const out = []; let cur = null; let fence = null;
12
+ for (const line of String(md).split('\n')) {
13
+ const m = line.match(/^\s*(```+|~~~+)/);
14
+ if (cur === null) { if (m) { fence = m[1][0]; cur = []; } continue; }
15
+ if (m && m[1][0] === fence && line.trim() === m[1] ) { out.push(cur.join('\n')); cur = null; continue; }
16
+ cur.push(line);
17
+ }
18
+ if (cur && cur.length) out.push(cur.join('\n'));
19
+ return out;
20
+ }
21
+
22
+ /** arg: '' | 'N' | 'code'. -> { text } or { error } */
23
+ export function pickCopy(answers, arg) {
24
+ const list = (answers || []).filter(a => a.text && a.text.trim());
25
+ if (!list.length) return { error: 'No answer to copy yet.' };
26
+ if (arg === 'code') {
27
+ for (let i = list.length - 1; i >= 0; i--) { const b = codeBlocks(list[i].text); if (b.length) return { text: stripMarks(b[b.length - 1]) }; }
28
+ return { error: 'No code block in the answers so far.' };
29
+ }
30
+ const n = arg ? Number(arg) : 1;
31
+ if (!Number.isInteger(n) || n < 1) return { error: 'Usage: /copy [N|code]' };
32
+ if (n > list.length) return { error: `Only ${list.length} answer${list.length === 1 ? '' : 's'} so far.` };
33
+ return { text: stripMarks(list[list.length - n].text) };
34
+ }
@@ -0,0 +1,11 @@
1
+ /** The restart countdown line, fitted to the width: full text when it fits, shorter forms below, never wrapped. */
2
+ export function countdownLine(secs, cols) {
3
+ const room = Math.max(20, (cols || 80) - 4); // paddingLeft 2 plus a margin
4
+ const forms = [
5
+ `↻ New version ready. Restarting in ${secs}s · Esc to stay on this version · Enter to restart now`,
6
+ `↻ New version · restarting in ${secs}s · Esc stay · Enter now`,
7
+ `↻ Restarting in ${secs}s · Esc stay · Enter now`,
8
+ `↻ Restart in ${secs}s · Esc stay`,
9
+ ];
10
+ return forms.find(f => f.length <= room) || forms[forms.length - 1];
11
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Deterministic script for `quox --demo --autoplay`: the same timings on every run, so a screen
3
+ * recording is repeatable. No Math.random, no wall clock in the plan; only fixed numbers and a seeded PRNG.
4
+ */
5
+ export const AUTOPLAY_LABEL = 'DEMO: demo data, nothing real runs.';
6
+ export const AUTOPLAY_REQUEST = 'Show me what you can do (demo)';
7
+ export const AUTOPLAY_PROOF = '/proof';
8
+ export const AUTOPLAY = Object.freeze({ seed: 20261013, startMs: 900, typeMinMs: 38, typeMaxMs: 78, afterTypeMs: 450, approvalHoldMs: 2500, afterKeyMs: 400, afterTurnMs: 900, finalHoldMs: 600 });
9
+
10
+ /** Small seeded PRNG (mulberry32): deterministic across runs and platforms. */
11
+ export function prng(seed) {
12
+ let a = seed >>> 0;
13
+ return () => {
14
+ a = (a + 0x6D2B79F5) >>> 0;
15
+ let t = a;
16
+ t = Math.imul(t ^ (t >>> 15), t | 1);
17
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
18
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
19
+ };
20
+ }
21
+
22
+ /** Per-character delays (ms) for typing `text` at a human pace; identical for the same seed. */
23
+ export function typingPlan(text, seed = AUTOPLAY.seed, { min = AUTOPLAY.typeMinMs, max = AUTOPLAY.typeMaxMs } = {}) {
24
+ const r = prng(seed);
25
+ return [...text].map(() => Math.round(min + r() * (max - min)));
26
+ }
27
+
28
+ /** Is autoplay requested? flag or QUOX_DEMO_AUTOPLAY=1. */
29
+ export const autoplayOn = (flag, env = process.env) => Boolean(flag) || /^(1|true|yes|on)$/i.test(String(env.QUOX_DEMO_AUTOPLAY || ''));
@@ -0,0 +1,24 @@
1
+ /** Simulated QUOXON journey for --demo, screenshots and tests. Nothing here talks to a server. */
2
+ export const DEMO_STEPS = (turnId) => [
3
+ { wait: 150, ev: { type: 'turn.started', turn_id: turnId, conversation_id: 'demo-conv', agent: 'CommanderQ', provider: 'demo', model: 'demo-model', hat: 'operator' } },
4
+ { wait: 300, ev: { type: 'thinking', turn_id: turnId, stage: 'planning', message: 'Planning the steps' } },
5
+ { wait: 400, ev: { type: 'text.delta', turn_id: turnId, content: 'I will save that note, then restart the web service. ' } },
6
+ { wait: 300, ev: { type: 'tool.started', turn_id: turnId, call_id: 'c1', tool: 'memory_save', summary: 'save note "demo journey"' } },
7
+ { wait: 200, ev: { type: 'trace', turn_id: turnId, protocol: 'aee', label: 'envelope opened', ref: '01K4N8QXAB' } },
8
+ { wait: 700, ev: { type: 'tool.finished', turn_id: turnId, call_id: 'c1', tool: 'memory_save', status: 'ok', result_summary: 'saved', duration_ms: 640, envelope_id: 'env1', receipt: null } },
9
+ { wait: 300, ev: { type: 'tool.started', turn_id: turnId, call_id: 'c2', tool: 'host_restart', summary: 'restart web on edge-02' } },
10
+ { wait: 300, ev: { type: 'approval.requested', turn_id: turnId, call_id: 'c2', inbox_item_id: 'demo-item', approval_id: 'demo-appr', tool: 'host_restart', target: 'web on edge-02', scope: 'service restart, 1 host', risk: 'medium', dashboard_url: 'https://example.invalid/approvals/demo-item' } },
11
+ { gate: 'approval' },
12
+ { when: 'reject', wait: 200, ev: { type: 'approval.resolved', turn_id: turnId, call_id: 'c2', inbox_item_id: 'demo-item', decision: 'denied' } },
13
+ { when: 'reject', wait: 200, ev: { type: 'tool.finished', turn_id: turnId, call_id: 'c2', tool: 'host_restart', status: 'denied', result_summary: 'denied by you, not run', duration_ms: 0, envelope_id: null, receipt: null } },
14
+ { when: 'reject', wait: 300, ev: { type: 'text.delta', turn_id: turnId, content: 'Understood. The note is saved; web on edge-02 was NOT restarted because you denied it (demo data).' } },
15
+ { when: 'reject', wait: 200, ev: { type: 'turn.finished', turn_id: turnId, status: 'completed', conversation_id: 'demo-conv' } },
16
+ { when: 'approve', wait: 200, ev: { type: 'approval.resolved', turn_id: turnId, call_id: 'c2', inbox_item_id: 'demo-item', decision: 'approved' } },
17
+ { when: 'approve', wait: 900, ev: { type: 'tool.finished', turn_id: turnId, call_id: 'c2', tool: 'host_restart', status: 'ok', result_summary: 'restarted', duration_ms: 2300, envelope_id: 'env2', receipt: { ward_seq: 4069513, verified: true } } },
18
+ { when: 'approve', wait: 100, ev: { type: 'trace', turn_id: turnId, protocol: 'aocl', label: 'ingress ok identity ok policy ok' } },
19
+ { when: 'approve', wait: 100, ev: { type: 'trace', turn_id: turnId, protocol: 'ward', label: 'witnessed', ref: '4069513' } },
20
+ { when: 'approve', wait: 300, ev: { type: 'text.delta', turn_id: turnId, content: 'Done. The note is saved and web on edge-02 restarted (demo data, nothing real ran).' } },
21
+ { when: 'approve', wait: 200, ev: { type: 'turn.finished', turn_id: turnId, status: 'completed', conversation_id: 'demo-conv' } }
22
+ ];
23
+
24
+ export const DEMO_LABEL = 'DEMO: simulated journey, no server, nothing real runs';
@@ -0,0 +1,76 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { DEMO_STEPS } from './demo.fixtures.js';
3
+
4
+ const sleep = (ms, signal) => new Promise((res) => {
5
+ const done = () => { clearTimeout(t); signal?.removeEventListener('abort', done); res(); };
6
+ const t = setTimeout(done, ms);
7
+ signal?.addEventListener('abort', done, { once: true });
8
+ });
9
+
10
+ /** A fake api with the same surface as createApi(), driven by DEMO_STEPS. */
11
+ /**
12
+ * WALK-FIXES pty seam (demo only, never a real server): decisionsFile is a JSON state file
13
+ * { items: [decision inbox items], log: [], answerDelayMs?, approvalsDelayMs? }. approvals() lists the open ones,
14
+ * answerDecision() marks one answered and appends { id, key, note, at } to log, so a test in another process reads
15
+ * exactly what was submitted, and when.
16
+ */
17
+ function decisionStore(file) {
18
+ const load = () => JSON.parse(readFileSync(file, 'utf8'));
19
+ const wait = (ms) => new Promise(r => setTimeout(r, ms || 0));
20
+ return {
21
+ // staleMs: an answered item is still listed for this long (a server that lags behind its own answers)
22
+ async open() { const d = load(); await wait(d.approvalsDelayMs); return d.items.filter(x => x.status === 'open' || (d.staleMs && x.answeredAt && Date.now() - x.answeredAt < d.staleMs)); },
23
+ async answer(id, key, note) {
24
+ const d0 = load(); await wait(d0.answerDelayMs);
25
+ const d = load(); const it = d.items.find(x => x.id === id);
26
+ if (!it || it.status !== 'open') { const e = new Error('already handled'); e.status = 404; throw e; }
27
+ it.status = 'answered'; it.answeredAt = Date.now(); it.answer = { key, note: note || null };
28
+ d.log.push({ id, key, note: note || null, at: Date.now() });
29
+ writeFileSync(file, JSON.stringify(d));
30
+ return { ok: true };
31
+ }
32
+ };
33
+ }
34
+
35
+ export function createDemoApi({ speed = 1, objects = false, decisionsFile = null } = {}) {
36
+ const decs = decisionsFile ? decisionStore(decisionsFile) : null;
37
+ let gate = null;
38
+ let decision = 'approve';
39
+ return {
40
+ orgId: () => 'demo-org',
41
+ async *stream({ turnId }, signal) {
42
+ decision = 'approve';
43
+ for (const step of DEMO_STEPS(turnId)) {
44
+ if (signal?.aborted) return;
45
+ if (step.when && step.when !== decision) continue;
46
+ if (step.gate) { await new Promise((res) => { gate = res; signal?.addEventListener('abort', res, { once: true }); }); continue; }
47
+ await sleep(step.wait / speed, signal);
48
+ if (signal?.aborted) return;
49
+ yield step.ev;
50
+ }
51
+ },
52
+ async steer() { return { ok: false, reason: 'unsupported' }; },
53
+ async interrupt() { return { ok: false }; },
54
+ async health() { return { ok: true, url: 'demo' }; },
55
+ // the open approval is in the inbox while the demo holds at the gate, so a poll never prunes its card
56
+ async approvals() { return [...(gate ? [{ id: 'demo-item', title: 'host_restart on web on edge-02', approval_id: 'demo-appr', created_at: new Date().toISOString() }] : []), ...(decs ? await decs.open() : [])]; },
57
+ ...(decs ? { async answerDecision(id, key, { note } = {}) { return decs.answer(id, key, note); }, async approvalStats() { return { awaiting: (await decs.open()).length + (gate ? 1 : 0) }; } } : {}),
58
+ async skills() { return [{ id: 'sk1', name: 'daily-fleet-check', description: 'Check fleet health and summarise.', edition: 'stable' }]; },
59
+ async skill(id) { return { id, name: 'daily-fleet-check', description: 'Check fleet health and summarise.', edition: 'stable' }; },
60
+ async conversationExists() { return { exists: true }; },
61
+ // /proof works in the demo journey (main.js asks for it); the other object views stay unavailable offline
62
+ ...(objects ? { objects: {
63
+ async proof(tool) {
64
+ const env = { id: tool.envelopeId, corr: 'demo-corr', payload: { success: true, tool_name: tool.tool } };
65
+ return { data: { tool, envelope: { ok: true, env },
66
+ aocl: { ok: true, list: [{ intent: 'aocl.identity.verified' }, { intent: 'aocl.policy.evaluated', payload: { allPassed: true } }] },
67
+ receipt: { ok: true, result: { status: 'WITNESSED', entry: { seq: 4069513 } } } } };
68
+ },
69
+ ...Object.fromEntries(['skill', 'agent', 'workflowRun', 'crystal', 'memory', 'rules', 'reconcile'].map((k) => [k, async () => ({ error: 'Object views are not available in the demo.' })]))
70
+ } } : {}),
71
+ async respond(_id, action) {
72
+ if (decs && _id !== 'demo-item') return decs.answer(_id, action === 'reject' ? 'n' : 'y', null); // a yes/no decision answered as an approval (the bulk card)
73
+ decision = action === 'reject' ? 'reject' : 'approve'; const g = gate; gate = null; g?.(); return { ok: true };
74
+ }
75
+ };
76
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Is a dev observer wired to this install? /observe and /note exist only for the developer whose
3
+ * window a QUOXON observer session watches; a customer has no observer, so for them these commands
4
+ * are hidden (help, palette, autocomplete) and refuse with a plain line.
5
+ *
6
+ * The switch is config driven, in the same style as the dev-transcripts flag: QUOX_DEV_OBSERVER=1
7
+ * turns it on for one shell, QUOX_DEV_OBSERVER=0 forces it off, otherwise the flag file
8
+ * ~/.config/quox/dev-observer decides (touch it on a dev box; it survives new shells).
9
+ */
10
+ import { existsSync } from 'node:fs';
11
+ import { homedir } from 'node:os';
12
+ import { join } from 'node:path';
13
+
14
+ export const DEV_OBSERVER_REFUSAL = 'No dev observer is set up on this install, so there is nothing to send this to.';
15
+
16
+ export function devObserverOn(env = process.env, { home = homedir(), exists = existsSync } = {}) {
17
+ if (env.QUOX_DEV_OBSERVER === '0') return false;
18
+ if (env.QUOX_DEV_OBSERVER === '1') return true;
19
+ try { return exists(join(home, '.config', 'quox', 'dev-observer')); } catch { return false; }
20
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * TUI-DIVIDER: the composer's bottom rule (`──────── anthropic claude-sonnet-5-5 ─`) as a live AOCL strip.
3
+ *
4
+ * Owner design (2026-10-17 22:09, after the first cut): a PIPELINE, like the dashboard command screen's
5
+ * LayerStackMini. One compact chain of 11 dots, one per AOCL layer, each in its own layer colour, sits just left of
6
+ * the model label: `────────•─•─●─●─◆─•─•─•─•─•─• anthropic claude-sonnet-5-5 ─`. On Enter the dots light left to
7
+ * right as the real layer events arrive; when they reach the model, only the label animates (katakana shimmer).
8
+ * Motion lives in one place at a time (the newest dot, or the label), never across the row. The finished state is
9
+ * the label green (red on an error or a block, with the blocking dot marked) for ~1.6 s, then the normal rule.
10
+ *
11
+ * HONESTY: only REAL state moves this. A pip lights only when that layer's `aocl.layer` event arrives (or a
12
+ * pre-stream refusal's `aocl_layers` record), in the colour of its real result. A bypassed layer (not evaluated
13
+ * on this path) stays unlit, and so does every layer of a server that sends no `aocl.layer`. The "model is
14
+ * working" phase starts on a real signal: the last pre-model layer (L4) reported, or the first model output.
15
+ * Dots light one step apart for readability (STEP_MS), but never before their own event.
16
+ *
17
+ * Pure: `dividerStep` folds actions into state, `dividerFrame` draws one frame (or null = draw the normal rule).
18
+ * Nothing here runs a timer; the app asks the clock for frames only while `dividerLive` says so.
19
+ */
20
+ import stringWidth from 'string-width';
21
+ import { wordFrame, wordColor } from './indicator.js';
22
+ import { LAYER_NAMES } from './aocl.js';
23
+
24
+ export const ORDER = ['L0', 'L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'L7', 'L8', 'L9', 'L10'];
25
+ const PRE_LAST = 'L4';
26
+ export const GREEN_MS = 1600; // the label holds green this long after a completed reply (under the app's 2 s idle guarantee, motion-app.test)
27
+ export const RED_MS = 1600; // and red this long after an error or a block (the error itself stays in the transcript)
28
+ export const MIN_COLS = 70; // narrower than this: the plain rule, no animation
29
+ export const STEP_MS = 120; // dots light at most one per step, left to right (never before their event)
30
+ export const CURRENT_MS = 700; // the newest dot blinks this long after it lights; then it is just lit
31
+ export const TAG_MS = 1200; // the newest layer's name shows beside the chain this long
32
+ export const LIT_RESULTS = new Set(['pass', 'gated', 'blocked']);
33
+
34
+ // Real model work only. `thinking` frames are mostly server staging (routing, processing, provider notes) sent
35
+ // before turn.started; only the model's own reasoning counts.
36
+ const MODEL_SIGNALS = new Set(['chunk', 'text.delta', 'tool.started']);
37
+ const isModelSignal = (ev) => MODEL_SIGNALS.has(ev.type) || (ev.type === 'thinking' && ev.stage === 'model_reasoning');
38
+
39
+ /** A fresh strip for a turn the user just sent. */
40
+ export const dividerStart = (turnId, now) => ({ turnId, startedAt: now, pips: {}, seen: {}, order: [], modelAt: null, phase: 'gates', endedAt: null, blockedAt: null });
41
+
42
+ function light(d, l, now) {
43
+ const id = String(l?.id || '');
44
+ if (!ORDER.includes(id) || d.pips[id]) return d; // unknown layer or already lit: nothing changes
45
+ const result = String(l.result || '');
46
+ if (!LIT_RESULTS.has(result)) { // bypass: a real event, but not evaluated: the pipeline steps over it, unlit
47
+ return result === 'bypass' && !(d.seen || {})[id] ? { ...d, seen: { ...(d.seen || {}), [id]: { result, at: now } } } : d;
48
+ }
49
+ let n = { ...d, pips: { ...d.pips, [id]: { result, at: now } }, seen: { ...(d.seen || {}), [id]: { result, at: now } }, order: [...d.order, id] };
50
+ if (result === 'blocked') n = { ...n, phase: 'error', blockedAt: id, endedAt: n.endedAt ?? now };
51
+ return n;
52
+ }
53
+
54
+ /**
55
+ * Fold one reducer action into the strip. `a` is the quoxon reducer action ({type:'turn.begin'} or
56
+ * {type:'event', ev}); unknown actions return `d` unchanged (same object).
57
+ */
58
+ export function dividerStep(d, a, now) {
59
+ if (!a) return d;
60
+ if (a.type === 'turn.begin') return dividerStart(a.turnId, now);
61
+ if (a.type !== 'event' || !d || d.endedAt != null && a.ev?.type !== 'aocl.layer') return d;
62
+ const ev = a.ev || {};
63
+ if (ev.type === 'aocl.layer') {
64
+ if (d.endedAt != null && d.phase !== 'error') return d; // a finished green strip never changes
65
+ let n = light(d, ev, now);
66
+ const pre = ev.phase === 'pre' || ORDER.indexOf(String(ev.id)) <= ORDER.indexOf(PRE_LAST);
67
+ if (n.phase === 'gates' && (String(ev.id) === PRE_LAST || !pre)) n = { ...n, phase: 'model', modelAt: n.modelAt ?? now };
68
+ return n;
69
+ }
70
+ if (isModelSignal(ev)) return d.phase === 'gates' ? { ...d, phase: 'model', modelAt: now } : d;
71
+ if (ev.type === 'aocl.verdict' && ev.verdict === 'blocked') {
72
+ const id = ORDER.includes(ev.layer) ? ev.layer : 'L8';
73
+ return { ...d, phase: 'error', blockedAt: d.blockedAt || id, endedAt: d.endedAt ?? now };
74
+ }
75
+ if (ev.type === 'error') return { ...d, phase: 'error', endedAt: now };
76
+ if (ev.type === 'turn.finished') {
77
+ const ok = (ev.status || 'completed') === 'completed';
78
+ return ok && d.phase !== 'error' ? { ...d, phase: 'done', endedAt: now } : { ...d, phase: 'error', endedAt: d.endedAt ?? now };
79
+ }
80
+ return d;
81
+ }
82
+
83
+ /** True while the strip needs frames: a turn in flight, or the green/red hold after it. */
84
+ export function dividerLive(d, now) {
85
+ if (!d) return false;
86
+ if (d.endedAt == null) return true;
87
+ return now - d.endedAt < (d.phase === 'done' ? GREEN_MS : RED_MS);
88
+ }
89
+
90
+ // Each layer's own colour, from the dashboard (src/lib/aee/envelope.js AOCL_LAYERS, which LayerStackMini uses).
91
+ // L0 and L10 are grey there (#6b7280); the TUI has no grey text, so they take sky and violet.
92
+ export const LAYER_HEX = {
93
+ L0: '#38bdf8', L1: '#3b82f6', L2: '#3b82f6', L3: '#f59e0b', L4: '#8b5cf6', L5: '#8b5cf6',
94
+ L6: '#6366f1', L7: '#f97316', L8: '#14b8a6', L9: '#22c55e', L10: '#a855f7',
95
+ };
96
+ const hexRgb = (h) => [1, 3, 5].map(i => parseInt(h.slice(i, i + 2), 16));
97
+ const mixRgb = (a, b, t) => a.map((v, i) => Math.round(v + (b[i] - v) * t));
98
+ const DARK = [30, 36, 48];
99
+ export const layerRgb = (id) => hexRgb(LAYER_HEX[id]);
100
+ export const dimRgb = (id) => mixRgb(layerRgb(id), DARK, 0.6); // pending / bypassed: the layer's own hue, dimmed (not grey)
101
+
102
+ export const RGB = { done: [34, 197, 94], blocked: [239, 68, 68], model: [56, 189, 248] };
103
+ const LIT = { pass: '●', gated: '◆', blocked: '✖' };
104
+ const PENDING = '•';
105
+
106
+ /**
107
+ * When each layer the server reported was reached in the pipeline: in layer order, one STEP_MS apart, never
108
+ * before its own event. Bypassed layers are reached (the chain passes them) but stay unlit.
109
+ */
110
+ export function layerReachTimes(d) {
111
+ const out = {};
112
+ if (!d) return out;
113
+ let t = d.startedAt - STEP_MS;
114
+ for (const id of ORDER) {
115
+ const s = (d.seen || {})[id] || d.pips[id];
116
+ if (!s) continue;
117
+ t = Math.max(t + STEP_MS, s.at);
118
+ out[id] = t;
119
+ }
120
+ return out;
121
+ }
122
+
123
+ /** When the pipeline hands over to the model (the label starts to shimmer): every pre-model layer seen was reached. */
124
+ export function modelReachedAt(d) {
125
+ if (!d || d.modelAt == null) return null;
126
+ const r = layerReachTimes(d);
127
+ const pre = ORDER.slice(0, ORDER.indexOf(PRE_LAST) + 1).map(id => r[id]).filter(x => x != null);
128
+ return Math.max(d.modelAt, ...pre, d.startedAt) + (pre.length ? STEP_MS : 0);
129
+ }
130
+
131
+ /** The chain of 11 dots joined by rule: cells [{ t, rgb, bold }]. Motion only on the newest dot. */
132
+ export function chainCells(d, now, { still = false } = {}) {
133
+ const reach = layerReachTimes(d);
134
+ const reached = ORDER.filter(id => reach[id] != null && reach[id] <= now);
135
+ const newest = reached.filter(id => d.pips[id]).sort((a, b) => reach[b] - reach[a])[0] || null;
136
+ const cells = [];
137
+ ORDER.forEach((id, i) => {
138
+ if (i) cells.push({ t: '─' });
139
+ const p = d.pips[id];
140
+ if (p && reach[id] <= now) {
141
+ // only the newest dot blinks, only while the turn runs, and a blocked dot never dims (it stays red)
142
+ const blinkOff = !still && d.endedAt == null && p.result !== 'blocked' && id === newest && now - reach[id] < CURRENT_MS && Math.floor((now - reach[id]) / 175) % 2 === 1;
143
+ const rgb = p.result === 'blocked' ? RGB.blocked : layerRgb(id);
144
+ cells.push({ t: LIT[p.result], rgb: blinkOff ? dimRgb(id) : rgb, bold: !blinkOff });
145
+ } else cells.push({ t: PENDING, rgb: dimRgb(id) });
146
+ });
147
+ return { cells, newest, newestAt: newest ? reach[newest] : null };
148
+ }
149
+
150
+ /**
151
+ * One frame of the rule: `{ cells: [{ t, rgb?, bold?, label? }] }` whose widths sum to exactly `cols`, or null
152
+ * when the normal rule should draw (no strip, the hold is over, NO_COLOR, reduced motion, too narrow, no label).
153
+ * Cells without `rgb` take the rule's own colour; `label: true` takes the label's own colour.
154
+ * `still` (a slow redraw): no blink and no katakana, but every lit dot and the end colours stay.
155
+ */
156
+ export function dividerFrame({ cols, label, d, now, mono = false, reduced = false, still = false, seed = 0 }) {
157
+ if (mono || reduced || !d || cols < MIN_COLS || !label || !dividerLive(d, now)) return null;
158
+ const lab = ` ${label} `;
159
+ const lw = stringWidth(lab);
160
+ const chain = chainCells(d, now, { still });
161
+ const cw = chain.cells.length;
162
+ const left = cols - lw - 1 - cw;
163
+ if (left < 4) return null;
164
+ // the newest lit layer names itself to the left of the chain, in its colour, while it is new
165
+ const tag = chain.newest && now - chain.newestAt < TAG_MS ? ` ${chain.newest} ${LAYER_NAMES[chain.newest] || ''} ` : '';
166
+ const fill = Math.max(1, left - (tag && tag.length + 1 <= left - 1 ? tag.length : 0));
167
+ const leftCells = Array.from({ length: fill }, () => ({ t: '─' }));
168
+ if (fill < left) {
169
+ const rgb = d.pips[chain.newest].result === 'blocked' ? RGB.blocked : layerRgb(chain.newest);
170
+ leftCells.splice(fill - 1, 0, ...[...tag].map(ch => ({ t: ch, rgb, bold: true })));
171
+ }
172
+ while (leftCells.length < left) leftCells.push({ t: '─' });
173
+ let labelCells;
174
+ if (d.phase === 'done') labelCells = [{ t: lab, rgb: RGB.done, bold: true }];
175
+ else if (d.phase === 'error') labelCells = [{ t: lab, rgb: RGB.blocked, bold: true }];
176
+ else {
177
+ const at = modelReachedAt(d);
178
+ if (at != null && now >= at) {
179
+ if (still) labelCells = [{ t: lab, rgb: RGB.model, bold: true }];
180
+ else {
181
+ const fr = wordFrame(label, now, { since: at, seed });
182
+ const n = fr.length;
183
+ labelCells = [{ t: ' ' }, ...fr.map((c, i) => ({ t: c.ch, rgb: wordColor('working', i, n, now, c.flash ? 'flash' : c.glyph ? 'glyph' : 'letter') })), { t: ' ' }];
184
+ }
185
+ } else labelCells = [{ t: lab, label: true }];
186
+ }
187
+ return { cells: [...leftCells, ...chain.cells, ...labelCells, { t: '─' }] };
188
+ }
189
+
190
+ /** Plain text of a frame (tests and pty proofs). */
191
+ export const frameText = (f) => (f ? f.cells.map(c => c.t).join('') : null);