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,240 @@
1
+ /**
2
+ * Objective commands: create, list, get, start, approve, plus the HITL
3
+ * checkpoint verbs (checkpoints, approve-checkpoint, deny-checkpoint).
4
+ *
5
+ * Objectives are served by the tasks service (port 3104), not the auth
6
+ * service. Org/user scoping comes from the JWT (org_id/sub claims) rather
7
+ * than a URL path segment — see services/tasks/middleware/auth.js.
8
+ *
9
+ * Two approval gates exist and must not be confused:
10
+ * `approve` resolves a decomposed objective's DAG plan.
11
+ * `approve-checkpoint` resolves one HITL checkpoint (pre-start gate or a
12
+ * live loop gate: budget, risk, interval).
13
+ */
14
+
15
+ import { formatOutput, formatDetail, printSuccess } from '../core/output.js';
16
+ import { parseJSON } from './helpers.js';
17
+ import { resolveEndpoint } from '../api/endpoints.js';
18
+
19
+ /**
20
+ * Format a timestamp as a short relative-time string ("5m ago").
21
+ *
22
+ * Checkpoint rows carry SQLite `datetime('now')` values ("2026-08-14
23
+ * 04:00:00"), which are UTC but have no zone marker, so Node would read them
24
+ * as local time and skew the age by the offset. Normalise that shape to ISO
25
+ * UTC first; anything already ISO passes straight through.
26
+ */
27
+ function relativeTime(ts) {
28
+ if (!ts) return '—';
29
+ const iso = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(ts)
30
+ ? `${ts.replace(' ', 'T')}Z`
31
+ : ts;
32
+ const then = new Date(iso).getTime();
33
+ if (Number.isNaN(then)) return '—';
34
+ const diffSec = Math.max(0, Math.floor((Date.now() - then) / 1000));
35
+ if (diffSec < 60) return `${diffSec}s ago`;
36
+ const diffMin = Math.floor(diffSec / 60);
37
+ if (diffMin < 60) return `${diffMin}m ago`;
38
+ const diffHr = Math.floor(diffMin / 60);
39
+ if (diffHr < 24) return `${diffHr}h ago`;
40
+ const diffDay = Math.floor(diffHr / 24);
41
+ return `${diffDay}d ago`;
42
+ }
43
+
44
+ /**
45
+ * Accumulate repeated `--capability-scope` flags into one array of raw
46
+ * strings. Each value may itself be a comma-separated list; splitting and
47
+ * trimming happen in the action, alongside the other body fields.
48
+ */
49
+ function collectScope(value, previous) {
50
+ return (previous || []).concat(value);
51
+ }
52
+
53
+ export function registerObjectiveCommands(program, ctx) {
54
+ const { client, auth } = ctx;
55
+
56
+ const objective = program
57
+ .command('objective')
58
+ .description('Manage objectives (autonomous multi-step goals)');
59
+
60
+ objective
61
+ .command('create')
62
+ .description('Create a new objective (DAG plan by default, or a QLoop loop with --execution-mode loop)')
63
+ .argument('<goal>', 'objective title / goal statement')
64
+ .option('--description <text>', 'longer description')
65
+ .option('--team <id>', 'team ID to run the objective under')
66
+ .option('--max-tokens <n>', 'max token budget')
67
+ .option('--max-tool-calls <n>', 'max tool call budget')
68
+ .option('--max-duration-ms <n>', 'max wall-clock duration in ms')
69
+ .option('--require-approval', 'require approval before starting')
70
+ .option('--checkpoint', 'checkpoint after each task')
71
+ .option('--goal-condition <text>', 're-observe loop exit condition')
72
+ .option('--max-iterations <n>', 'max re-observe loop iterations')
73
+ .option('--metadata <json>', 'arbitrary metadata as JSON')
74
+ .option('--execution-mode <mode>', 'dag|loop. A loop runs a QLoop backend instead of a reviewable DAG plan, so it ALWAYS requires a before_start approval (see `quox objective checkpoints` and `approve-checkpoint`)')
75
+ .option('--loop-backend <backend>', 'claude|codex, loop mode only (default claude)')
76
+ .option('--capability-scope <tools>', 'comma-separated tool names the loop may call (repeatable); empty scope escalates every tool', collectScope)
77
+ .option('--system-prompt <text>', 'per-objective system prompt, carried verbatim to the run')
78
+ .action(async (goal, options) => {
79
+ auth.requireAuth();
80
+ const body = { title: goal };
81
+ if (options.description) body.description = options.description;
82
+ if (options.team) body.team_id = options.team;
83
+ if (options.maxTokens) body.max_tokens = Number(options.maxTokens);
84
+ if (options.maxToolCalls) body.max_tool_calls = Number(options.maxToolCalls);
85
+ if (options.maxDurationMs) body.max_duration_ms = Number(options.maxDurationMs);
86
+ if (options.requireApproval) body.require_approval_before_start = true;
87
+ if (options.checkpoint) body.checkpoint_after_each_task = true;
88
+ if (options.goalCondition) body.goal_condition = options.goalCondition;
89
+ if (options.maxIterations) body.max_iterations = Number(options.maxIterations);
90
+ if (options.metadata) body.metadata = parseJSON(options.metadata, '--metadata');
91
+ if (options.executionMode) body.execution_mode = options.executionMode;
92
+ if (options.loopBackend) body.loop_backend = options.loopBackend;
93
+ if (options.capabilityScope) {
94
+ const scope = options.capabilityScope
95
+ .flatMap((raw) => raw.split(','))
96
+ .map((t) => t.trim())
97
+ .filter(Boolean);
98
+ if (scope.length > 0) body.capability_scope = scope;
99
+ }
100
+ if (options.systemPrompt) body.system_prompt = options.systemPrompt;
101
+
102
+ const data = await client.post(resolveEndpoint('objectives.create').path, { service: resolveEndpoint('objectives.create').service, body });
103
+ printSuccess(`Objective created: ${data.id}`);
104
+ });
105
+
106
+ objective
107
+ .command('list')
108
+ .description('List objectives')
109
+ .option('--status <status>', 'filter by status')
110
+ .option('--team <id>', 'filter by team ID')
111
+ .option('--limit <n>', 'max results', '50')
112
+ .action(async (options) => {
113
+ auth.requireAuth();
114
+ const query = { limit: options.limit };
115
+ if (options.status) query.status = options.status;
116
+ if (options.team) query.team_id = options.team;
117
+ const data = await client.get(resolveEndpoint('objectives.list').path, { service: resolveEndpoint('objectives.list').service, query });
118
+ formatOutput({
119
+ columns: ['id', 'title', 'status', 'team_id', 'created_at'],
120
+ headers: ['ID', 'Title', 'Status', 'Team', 'Created'],
121
+ rows: data.objectives || data,
122
+ }, { format: program.opts().output });
123
+ });
124
+
125
+ objective
126
+ .command('get')
127
+ .description('Get objective details (tasks, checkpoints, DAG)')
128
+ .argument('<id>', 'objective ID')
129
+ .action(async (id) => {
130
+ auth.requireAuth();
131
+ const data = await client.get(resolveEndpoint('objectives.get', { id }).path, { service: resolveEndpoint('objectives.get', { id }).service });
132
+ formatDetail(data, { format: program.opts().output, title: data.title });
133
+ });
134
+
135
+ objective
136
+ .command('start')
137
+ .description('Start an objective (decompose into tasks)')
138
+ .argument('<id>', 'objective ID')
139
+ .option('--skip-checkpoint', 'bypass pre-start approval checkpoint')
140
+ .action(async (id, options) => {
141
+ auth.requireAuth();
142
+ const body = {};
143
+ if (options.skipCheckpoint) body.skip_checkpoint = true;
144
+ const data = await client.post(resolveEndpoint('objectives.start', { id }).path, { service: resolveEndpoint('objectives.start', { id }).service, body });
145
+ printSuccess(`Objective ${id}: ${data.status}${data.message ? ` — ${data.message}` : ''}`);
146
+ });
147
+
148
+ objective
149
+ .command('approve')
150
+ .description('Approve a decomposed objective\'s plan (starts execution)')
151
+ .argument('<id>', 'objective ID')
152
+ .action(async (id) => {
153
+ auth.requireAuth();
154
+ const data = await client.post(resolveEndpoint('objectives.approve-plan', { id }).path, { service: resolveEndpoint('objectives.approve-plan', { id }).service });
155
+ printSuccess(`Objective ${id}: ${data.status}${data.message ? ` — ${data.message}` : ''}`);
156
+ });
157
+
158
+ objective
159
+ .command('checkpoints')
160
+ .description('List pending HITL checkpoints across the org (approval queue)')
161
+ .option('--include-reviewed', 'include checkpoints that have already been reviewed')
162
+ .option('--limit <n>', 'max results', '100')
163
+ .action(async (options) => {
164
+ auth.requireAuth();
165
+ const query = { limit: options.limit };
166
+ if (options.includeReviewed) query.include_reviewed = 'true';
167
+ const data = await client.get(resolveEndpoint('objectives.checkpoints-pending').path, { service: resolveEndpoint('objectives.checkpoints-pending').service, query });
168
+ const rows = (data.checkpoints || data || []).map((cp) => ({
169
+ id: cp.id,
170
+ objective_title: cp.objectiveTitle,
171
+ checkpoint_type: cp.checkpointType,
172
+ age: relativeTime(cp.createdAt),
173
+ title: cp.title,
174
+ }));
175
+ formatOutput({
176
+ columns: ['id', 'objective_title', 'checkpoint_type', 'age', 'title'],
177
+ headers: ['Checkpoint', 'Objective', 'Type', 'Age', 'Title'],
178
+ rows,
179
+ }, { format: program.opts().output, emptyMessage: 'No pending checkpoints.' });
180
+ });
181
+
182
+ objective
183
+ .command('approve-checkpoint')
184
+ .description('Approve one HITL checkpoint (not the DAG plan, see `approve`)')
185
+ .argument('<objectiveId>', 'objective ID')
186
+ .argument('<checkpointId>', 'checkpoint ID')
187
+ .option('--notes <text>', 'review notes recorded against the decision')
188
+ .action(async (objectiveId, checkpointId, options) => {
189
+ auth.requireAuth();
190
+ const body = {};
191
+ if (options.notes) body.notes = options.notes;
192
+ const params = { id: objectiveId, checkpointId };
193
+ const data = await client.post(resolveEndpoint('objectives.checkpoint-approve', params).path, { service: resolveEndpoint('objectives.checkpoint-approve', params).service, body });
194
+ printSuccess(`Checkpoint ${checkpointId}: ${data.status}${data.objective_status ? ` (objective ${data.objective_status})` : ''}`);
195
+ });
196
+
197
+ objective
198
+ .command('deny-checkpoint')
199
+ .description('Reject one HITL checkpoint (not the DAG plan, see `approve`)')
200
+ .argument('<objectiveId>', 'objective ID')
201
+ .argument('<checkpointId>', 'checkpoint ID')
202
+ .option('--notes <text>', 'review notes recorded against the decision')
203
+ .action(async (objectiveId, checkpointId, options) => {
204
+ auth.requireAuth();
205
+ const body = {};
206
+ if (options.notes) body.notes = options.notes;
207
+ const params = { id: objectiveId, checkpointId };
208
+ const data = await client.post(resolveEndpoint('objectives.checkpoint-reject', params).path, { service: resolveEndpoint('objectives.checkpoint-reject', params).service, body });
209
+ printSuccess(`Checkpoint ${checkpointId}: ${data.status}${data.objective_status ? ` (objective ${data.objective_status})` : ''}`);
210
+ });
211
+
212
+ objective
213
+ .command('cancel')
214
+ .description('Cancel an objective (pending ones included, e.g. proof leftovers that never started); its open tasks are cancelled too. For a RUNNING loop prefer `halt`, which also kills the live session.')
215
+ .argument('<objectiveId>', 'objective ID')
216
+ .action(async (objectiveId) => {
217
+ auth.requireAuth();
218
+ const ep = resolveEndpoint('objectives.cancel', { id: objectiveId });
219
+ const data = await client.delete(ep.path, { service: ep.service });
220
+ printSuccess(`Objective ${objectiveId}: ${data?.status || 'cancelled'}`);
221
+ });
222
+
223
+ objective
224
+ .command('halt')
225
+ .description('Halt a running QLoop loop ON DEMAND (unconditional human stop; no pending gate needed). Fires the same signed-kill as a gate deny.')
226
+ .argument('<objectiveId>', 'loop objective ID')
227
+ .option('--notes <text>', 'reason recorded against the halt')
228
+ .action(async (objectiveId, options) => {
229
+ auth.requireAuth();
230
+ const body = {};
231
+ if (options.notes) body.notes = options.notes;
232
+ const params = { id: objectiveId };
233
+ const data = await client.post(resolveEndpoint('objectives.halt', params).path, { service: resolveEndpoint('objectives.halt', params).service, body });
234
+ if (data.status === 'halted') {
235
+ printSuccess(`Loop ${objectiveId} halted (interrupted=${data.interrupted}, objective ${data.objective_status})`);
236
+ } else {
237
+ printSuccess(`Loop ${objectiveId}: ${data.status}${data.reason ? ` (${data.reason})` : ''}`);
238
+ }
239
+ });
240
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * `quox offload` : run a heavy command on a pool worker VM and behave like running it locally.
3
+ *
4
+ * quox offload [--dir <path>] [--fresh] [--keep] [--cores N --memory-gb N --disk-gb N] -- <command...>
5
+ * quox offload status
6
+ * quox offload release
7
+ *
8
+ * Engine: lib/core/offload.js. The worker is leased through the human pool REST API
9
+ * (same as `quox vm pool lease`) with a dedicated key at ~/.config/quox/offload_ed25519.
10
+ */
11
+
12
+ import path from 'node:path';
13
+ import { printInfo } from '../core/output.js';
14
+ import { makePoolRest } from '../core/vm.js';
15
+ import { OffloadError, realDeps, runOffload, releaseWorker, workerStatus, gitToplevel, WORKER_NAME_RE } from '../core/offload.js';
16
+
17
+ export function registerOffloadCommands(program, ctx, { deps: injected } = {}) {
18
+ const { client, auth } = ctx;
19
+ const json = () => program.opts().output === 'json';
20
+ const base = () => injected || realDeps({ rest: makePoolRest({ client }) });
21
+ // --worker <name> / QUOX_OFFLOAD_WORKER: your own worker (state, lease, size), not the box's shared one.
22
+ const mk = (options = {}) => {
23
+ const d = base();
24
+ const name = options.worker ?? d.env?.QUOX_OFFLOAD_WORKER;
25
+ if (name !== undefined && name !== '' && !WORKER_NAME_RE.test(String(name))) {
26
+ throw new OffloadError(`worker name must be lowercase letters, digits and dashes (up to 32), got ${JSON.stringify(name)}`);
27
+ }
28
+ return name ? Object.assign(Object.create(Object.getPrototypeOf(d)), d, { worker: String(name) }) : d;
29
+ };
30
+ const WORKER_HELP = 'use your own named worker (its own lease and size) instead of the shared one; or set QUOX_OFFLOAD_WORKER';
31
+
32
+ const off = program
33
+ .command('offload')
34
+ .description('Run a heavy command on a pool worker VM (synced dir, cached npm ci, live output, same exit code)')
35
+ .argument('[command...]', 'command to run remotely (put it after --)')
36
+ .option('--dir <path>', 'directory to sync (default: git toplevel of the current directory)')
37
+ .option('--fresh', 'release the current worker, lease a new one and reinstall dependencies')
38
+ .option('--keep', 'leave the worker leased for reuse (the default; kept for compatibility)')
39
+ .option('--cores <n>', 'vCPU cores for a newly leased worker')
40
+ .option('--memory-gb <n>', 'memory in GB for a newly leased worker')
41
+ .option('--disk-gb <n>', 'disk in GB for a newly leased worker')
42
+ .option('--worker <name>', WORKER_HELP)
43
+ .option('--docker', 'also give the worker Docker (user hyp in the docker group) and build the quox-sandbox images from the synced repo when they are missing or changed')
44
+ .allowExcessArguments(true)
45
+ .action(async (command, options) => {
46
+ auth.requireAuth();
47
+ const cwd = process.cwd();
48
+ const dir = path.resolve(options.dir || gitToplevel(cwd));
49
+ try {
50
+ const code = await runOffload(mk(options), {
51
+ dir, cwd, command, fresh: !!options.fresh, docker: !!options.docker,
52
+ cores: options.cores, memoryGb: options.memoryGb, diskGb: options.diskGb,
53
+ });
54
+ process.exitCode = code;
55
+ } catch (e) {
56
+ if (!(e instanceof OffloadError)) throw e;
57
+ process.stderr.write(`offload: ${e.message}\n`);
58
+ process.exitCode = 1;
59
+ }
60
+ });
61
+
62
+ off.command('status')
63
+ .description('Show your offload worker: vmid, ip, lease expiry, synced dirs')
64
+ .option('--worker <name>', WORKER_HELP)
65
+ .action(async (options) => {
66
+ auth.requireAuth();
67
+ const s = await workerStatus(mk(options));
68
+ if (json()) { console.log(JSON.stringify(s, null, 2)); return; }
69
+ if (!s.worker) { printInfo('no offload worker leased (it is leased on the next `quox offload -- <command>`)'); return; }
70
+ const w = s.worker;
71
+ console.log(`worker ${w.vmid} ${w.ip || '?'} ${w.state} lessee ${w.lessee} lease expires ${w.lease_expires_at || '?'}`);
72
+ for (const d of s.dirs) console.log(` synced ${d.local} -> ${d.remote} ${d.synced_at || ''}`);
73
+ });
74
+
75
+ off.command('release')
76
+ .description('Release (destroy) your offload worker via the pool')
77
+ .option('--worker <name>', WORKER_HELP)
78
+ .action(async (options) => {
79
+ auth.requireAuth();
80
+ try {
81
+ const vmid = await releaseWorker(mk(options));
82
+ if (json()) console.log(JSON.stringify({ released: vmid }));
83
+ else printInfo(vmid ? `released worker ${vmid}` : 'no offload worker to release');
84
+ } catch (e) {
85
+ process.stderr.write(`offload: release failed: ${e.message}\n`);
86
+ process.exitCode = 1;
87
+ }
88
+ });
89
+ }
@@ -0,0 +1,329 @@
1
+ /**
2
+ * `quox onboard <host>`: guided, idempotent, resumable tenant-onboarding
3
+ * wizard.
4
+ *
5
+ * This is NET-NEW GLUE over verbs that already exist (dev doctor, org create,
6
+ * plan roadmap create, engagement create/target/verify/gripe-target/schedule,
7
+ * vault list). It does NOT reimplement any of their logic: every REST call
8
+ * goes through ctx.client + resolveEndpoint, exactly as the individual verbs
9
+ * do. The wizard's only job is to run the mechanical steps in the right order,
10
+ * skip anything already done, and STOP cleanly at the two human-auth gates
11
+ * (dev-enable + profile-verify, and the GitHub PAT) with the exact copy-paste
12
+ * command the operator must run before re-running the wizard.
13
+ *
14
+ * Design rules baked in here:
15
+ * - Idempotent: every step CHECKs before it acts, so a second run creates no
16
+ * duplicates and simply resumes at the first incomplete step.
17
+ * - Honest gates: a gated step is never reported as done. It prints a WAITING
18
+ * banner and the wizard returns (exit 0, since a gate is a pause, not an error).
19
+ * - Secret-free: the GitHub PAT is instruct-and-stop in v1. The token never
20
+ * enters this process; the operator adds it with `quox vault create
21
+ * --credentials-stdin` and re-runs.
22
+ */
23
+
24
+ import { resolveEndpoint } from '../api/endpoints.js';
25
+ import { line as glyphLine, printEvent } from '../core/quoxglyph.js';
26
+
27
+ // ── status line markers ────────────────────────────────────────────────────
28
+ // One line per step. [ok] already there, [+] created/changed, [wait] a human
29
+ // gate blocks progress. Kept as plain console.log (not the chalk helpers) so
30
+ // the markers are trivially assertable in tests and scannable in a wall of
31
+ // tool output.
32
+ function ok(msg) { console.log(` [ok] ${msg}`); }
33
+ function created(msg) { console.log(` [+] ${msg}`); }
34
+ function waiting(msg) { console.log(` [wait] needs you: ${msg}`); }
35
+
36
+ // ── small pure helpers (exported for unit tests) ───────────────────────────
37
+
38
+ /** Slug for a display name: lowercase, non-alphanumeric collapsed to '-'. */
39
+ export function slugify(name) {
40
+ return String(name || '')
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9]+/g, '-')
43
+ .replace(/^-+|-+$/g, '')
44
+ || 'tenant';
45
+ }
46
+
47
+ /**
48
+ * Infer an engagement target type from its value.
49
+ * - an ipv4 or ipv4/cidr -> 'cidr'
50
+ * - a scheme://... or host:port -> 'url'
51
+ * - a bare hostname -> 'domain'
52
+ * Default for a bare IP is 'cidr' (the engagement scope model treats a single
53
+ * host as a /32 cidr target).
54
+ */
55
+ export function inferTargetType(value) {
56
+ const v = String(value || '').trim();
57
+ // ipv4 with optional /nn suffix
58
+ if (/^\d{1,3}(\.\d{1,3}){3}(\/\d{1,2})?$/.test(v)) return 'cidr';
59
+ // explicit scheme, or a host:port pair
60
+ if (/:\/\//.test(v) || /^[^\s/]+:\d+$/.test(v)) return 'url';
61
+ return 'domain';
62
+ }
63
+
64
+ /** curl install command for a host that is not yet a fleet-enrolled dev server. */
65
+ export function buildOnboardInstallCommand(collectorBase, host) {
66
+ const base = String(collectorBase || '').replace(/\/+$/, '');
67
+ // REL-423: fail loud, never `curl | sudo bash` (that pipe reports success when the download fails).
68
+ return `( f=$(mktemp) && trap 'rm -f "$f"' EXIT && curl -fsSL ${base}/install -o "$f" && sudo bash "$f" --host-id ${host} )`;
69
+ }
70
+
71
+ // ── the wizard ──────────────────────────────────────────────────────────────
72
+
73
+ /**
74
+ * Run the onboarding orchestration. Returns { status: 'COMPLETE' | 'WAITING' }.
75
+ * Exported so it can be unit-tested directly with an injected fake client, and
76
+ * so a caller can branch on the outcome.
77
+ */
78
+ export async function runOnboard(host, options, ctx) {
79
+ const { client, auth, config } = ctx;
80
+ auth.requireAuth();
81
+
82
+ const name = options.name;
83
+ const targetValue = options.target || host;
84
+ const envTier = options.environmentTier || 'dev';
85
+ const cron = options.scheduleCron || '0 2 * * 3';
86
+ const tool = options.tool || 'vuln_scan';
87
+ const slug = slugify(name);
88
+
89
+ printEvent(glyphLine('quox', `onboarding ${host}`));
90
+
91
+ // ── Step 1: AGENT + DEV-ENABLE gate ──────────────────────────────────────
92
+ // Read real registry state; never infer readiness from an enable having
93
+ // returned 200. Not enrolled -> install. Enrolled but not dev-enabled or not
94
+ // profile_verified -> the enable/seam-key gate.
95
+ const orgIdForDev = auth.getOrgId(ctx.program?.opts?.().org);
96
+ const serversEp = resolveEndpoint('v1.dev.servers.list');
97
+ const serversResp = await client.get(serversEp.path, {
98
+ service: serversEp.service,
99
+ query: orgIdForDev ? { org_id: orgIdForDev } : {},
100
+ });
101
+ const servers = Array.isArray(serversResp?.servers) ? serversResp.servers : [];
102
+ const server = servers.find((s) => s.host_id === host);
103
+
104
+ if (!server) {
105
+ const collectorBase = config?.getServiceUrl ? config.getServiceUrl('collector') : '<collector-url>';
106
+ waiting(`${host} is not a fleet-enrolled dev server. Install the agent, then re-run:`);
107
+ console.log(` ${buildOnboardInstallCommand(collectorBase, host)}`);
108
+ return stop(host);
109
+ }
110
+
111
+ const enabledCaps = Array.isArray(server.enabled_capabilities) ? server.enabled_capabilities : [];
112
+ if (!server.profile_verified || enabledCaps.length === 0) {
113
+ const why = !server.profile_verified ? 'profile not verified' : 'no capabilities enabled';
114
+ waiting(`${host} is enrolled but not ready (${why}). Run (as an owner/admin):`);
115
+ console.log(` quox dev enable ${host}`);
116
+ console.log(' profile_verified needs the seam key plus an agent restart.');
117
+ console.log(` Confirm with: quox dev doctor ${host}`);
118
+ return stop(host);
119
+ }
120
+ ok(`${host} is a dev-enabled, profile-verified dev server`);
121
+
122
+ // ── Step 2: ORG ──────────────────────────────────────────────────────────
123
+ const orgsEp = resolveEndpoint('orgs.list');
124
+ const orgsResp = await client.get(orgsEp.path, { service: orgsEp.service });
125
+ const orgs = orgsResp?.orgs || orgsResp?.organizations || orgsResp || [];
126
+ let org = (Array.isArray(orgs) ? orgs : []).find(
127
+ (o) => o.name === name || o.slug === slug || o.id === name,
128
+ );
129
+ let orgId;
130
+ if (org) {
131
+ orgId = org.id;
132
+ ok(`org "${name}" exists (${orgId})`);
133
+ } else {
134
+ const createEp = resolveEndpoint('orgs.create');
135
+ const createResp = await client.post(createEp.path, {
136
+ service: createEp.service,
137
+ body: { name, slug },
138
+ });
139
+ org = createResp?.organization || createResp?.org || createResp;
140
+ orgId = org.id;
141
+ created(`org "${name}" (${orgId})`);
142
+ }
143
+
144
+ // ── Step 3: ROADMAP ───────────────────────────────────────────────────────
145
+ const roadmapId = slug;
146
+ const roadmapsEp = resolveEndpoint('quoxplan.roadmaps.list');
147
+ const roadmapsResp = await client.get(roadmapsEp.path, { service: roadmapsEp.service });
148
+ const roadmaps = roadmapsResp?.roadmaps || [];
149
+ const existingRoadmap = roadmaps.find((r) => r.id === roadmapId || r.org_id === orgId);
150
+ if (existingRoadmap) {
151
+ ok(`roadmap "${existingRoadmap.id}" exists for this tenant`);
152
+ } else {
153
+ const rmCreateEp = resolveEndpoint('quoxplan.roadmaps.create');
154
+ await client.post(rmCreateEp.path, {
155
+ service: rmCreateEp.service,
156
+ body: { org_id: orgId, id: roadmapId, name: `${name} delivery`, actor: 'quox-cli' },
157
+ });
158
+ created(`roadmap "${roadmapId}" for org ${orgId}`);
159
+ }
160
+
161
+ // ── Step 4: ENGAGEMENT ────────────────────────────────────────────────────
162
+ const engagementName = `${name} security`;
163
+ const engListEp = resolveEndpoint('engagement.list');
164
+ const engListResp = await client.get(engListEp.path, { service: engListEp.service });
165
+ const engagements = engListResp?.engagements || [];
166
+ let engagement = engagements.find((e) => e.name === engagementName);
167
+ let engagementId;
168
+ if (engagement) {
169
+ engagementId = engagement.id;
170
+ ok(`engagement "${engagementName}" exists (${engagementId})`);
171
+ } else {
172
+ const engCreateEp = resolveEndpoint('engagement.create');
173
+ const engResp = await client.post(engCreateEp.path, {
174
+ service: engCreateEp.service,
175
+ body: { name: engagementName, environment_tier: envTier, risk_ceiling: 'safe_active' },
176
+ });
177
+ engagement = engResp.engagement;
178
+ engagementId = engagement.id;
179
+ created(`engagement "${engagementName}" (${engagementId})`);
180
+ }
181
+
182
+ // ── Step 5: TARGET ────────────────────────────────────────────────────────
183
+ const engGetEp = resolveEndpoint('engagement.get', { id: engagementId });
184
+ const engDetail = await client.get(engGetEp.path, { service: engGetEp.service });
185
+ const currentEngagement = engDetail?.engagement || engagement;
186
+ const targets = Array.isArray(engDetail?.targets) ? engDetail.targets : [];
187
+ let target = targets.find((t) => t.value === targetValue);
188
+ let targetVerified = false;
189
+
190
+ if (!target) {
191
+ const type = inferTargetType(targetValue);
192
+ const addEp = resolveEndpoint('engagement.target.create', { id: engagementId });
193
+ const addResp = await client.post(addEp.path, {
194
+ service: addEp.service,
195
+ body: { type, value: targetValue, verification_method: 'quoxagent_install' },
196
+ });
197
+ target = addResp.target;
198
+ created(`target ${targetValue} (${type}) added`);
199
+ const verEp = resolveEndpoint('engagement.target.verify', { id: engagementId, targetId: target.id });
200
+ const verResp = await client.post(verEp.path, { service: verEp.service, body: {} });
201
+ targetVerified = verResp?.target?.verification_status === 'verified';
202
+ if (targetVerified) created(`target ${targetValue} verified`);
203
+ else waiting(`target ${targetValue} did not verify (status: ${verResp?.target?.verification_status || 'unknown'})`);
204
+ } else if (target.verification_status === 'verified') {
205
+ targetVerified = true;
206
+ ok(`target ${targetValue} already verified`);
207
+ } else {
208
+ const verEp = resolveEndpoint('engagement.target.verify', { id: engagementId, targetId: target.id });
209
+ const verResp = await client.post(verEp.path, { service: verEp.service, body: {} });
210
+ targetVerified = verResp?.target?.verification_status === 'verified';
211
+ if (targetVerified) created(`target ${targetValue} verified`);
212
+ else waiting(`target ${targetValue} did not verify (status: ${verResp?.target?.verification_status || 'unknown'})`);
213
+ }
214
+
215
+ // ── Step 6: GITHUB PAT gate ───────────────────────────────────────────────
216
+ // The repo is only meaningful if a customer GitHub connector token exists to
217
+ // file into it. Check the org vault for a github credential; if absent, STOP
218
+ // with the exact instruct-and-stop command (secret via --credentials-stdin,
219
+ // NEVER argv), and do NOT touch gripe target or schedule.
220
+ const repo = options.repo;
221
+ if (repo) {
222
+ const vaultEp = resolveEndpoint('vault.list', { orgId });
223
+ const vaultResp = await client.get(vaultEp.path, {
224
+ service: vaultEp.service,
225
+ query: { type: 'github' },
226
+ });
227
+ const creds = vaultResp?.credentials || vaultResp || [];
228
+ const githubCred = (Array.isArray(creds) ? creds : []).find((c) => c.integration_type === 'github');
229
+ if (!githubCred) {
230
+ waiting(`no GitHub connector credential in org ${orgId}. Add the fine-grained PAT, then re-run:`);
231
+ console.log(` printf '%s' '<PAT>' | quox vault create --org ${orgId} --type github --label "${name} github" --credentials-stdin`);
232
+ console.log(` (the JSON on stdin is {"token":"<PAT>"}; scope the PAT to ${repo} with Issues read/write)`);
233
+ return stop(host, { orgId, roadmapId, engagementId, targetValue, targetVerified });
234
+ }
235
+ ok(`GitHub connector credential present in org ${orgId}`);
236
+
237
+ // ── Step 7: GRIPE TARGET ────────────────────────────────────────────────
238
+ const wantRepo = repo;
239
+ const wantConnector = orgId;
240
+ if (currentEngagement.gripe_repo === wantRepo && currentEngagement.gripe_connector_org_id === wantConnector) {
241
+ ok(`gripe target already ${wantRepo} via org ${wantConnector}`);
242
+ } else {
243
+ const gtEp = resolveEndpoint('engagement.gripe_target.set', { id: engagementId });
244
+ await client.patch(gtEp.path, {
245
+ service: gtEp.service,
246
+ body: { gripe_repo: wantRepo, gripe_connector_org_id: wantConnector },
247
+ });
248
+ created(`gripe target -> ${wantRepo} via org ${wantConnector}`);
249
+ }
250
+ } else {
251
+ ok('no --repo given; skipping GitHub connector and gripe target');
252
+ }
253
+
254
+ // ── Step 8: SCHEDULE ──────────────────────────────────────────────────────
255
+ const schedListEp = resolveEndpoint('engagement.schedule.list', { id: engagementId });
256
+ const schedResp = await client.get(schedListEp.path, { service: schedListEp.service });
257
+ const schedules = Array.isArray(schedResp?.schedules) ? schedResp.schedules : [];
258
+ let schedule = schedules.find((s) => s.target_value === targetValue && s.tool === tool);
259
+ if (schedule) {
260
+ ok(`schedule for ${tool} on ${targetValue} exists (next: ${schedule.next_run_at || 'n/a'})`);
261
+ } else {
262
+ const schedCreateEp = resolveEndpoint('engagement.schedule.create', { id: engagementId });
263
+ const createResp = await client.post(schedCreateEp.path, {
264
+ service: schedCreateEp.service,
265
+ body: { target_value: targetValue, tool, schedule_kind: 'cron', cron_expression: cron },
266
+ });
267
+ schedule = createResp.schedule;
268
+ created(`schedule ${tool} on ${targetValue} @ "${cron}" (next: ${schedule?.next_run_at || 'n/a'})`);
269
+ }
270
+
271
+ // ── Step 9: SUMMARY ───────────────────────────────────────────────────────
272
+ // Every human gate that could stop the wizard has already returned by here,
273
+ // so the only thing left that can hold onboarding open is an unverified
274
+ // target (verification is best-effort and can legitimately not land yet).
275
+ const complete = targetVerified;
276
+ printSummary({
277
+ status: complete ? 'COMPLETE' : 'WAITING',
278
+ host,
279
+ orgId,
280
+ roadmapId,
281
+ engagementId,
282
+ targetValue,
283
+ targetVerified,
284
+ gripeRepo: repo || null,
285
+ schedule: schedule ? { cron, next: schedule.next_run_at } : null,
286
+ remainingGate: targetVerified ? null : 'target not verified',
287
+ });
288
+ return { status: complete ? 'COMPLETE' : 'WAITING' };
289
+ }
290
+
291
+ /** Shared gate-stop: print the WAITING banner + resume hint, return WAITING. */
292
+ function stop(host, partial) {
293
+ console.log('');
294
+ console.log(' ===== WAITING on a human gate =====');
295
+ console.log(` Do the step above, then re-run: quox onboard ${host} ... to continue.`);
296
+ if (partial) {
297
+ console.log(` Progress so far: org ${partial.orgId}, roadmap ${partial.roadmapId}, engagement ${partial.engagementId}, target ${partial.targetValue} (verified: ${partial.targetVerified}).`);
298
+ }
299
+ return { status: 'WAITING' };
300
+ }
301
+
302
+ function printSummary(s) {
303
+ console.log('');
304
+ console.log(` ===== onboarding ${s.status} =====`);
305
+ console.log(` host: ${s.host}`);
306
+ console.log(` org: ${s.orgId}`);
307
+ console.log(` roadmap: ${s.roadmapId}`);
308
+ console.log(` engagement: ${s.engagementId}`);
309
+ console.log(` target: ${s.targetValue} (verified: ${s.targetVerified})`);
310
+ console.log(` gripe repo: ${s.gripeRepo || '(none)'}`);
311
+ if (s.schedule) console.log(` schedule: "${s.schedule.cron}" (next: ${s.schedule.next || 'n/a'})`);
312
+ if (s.remainingGate) console.log(` remaining: ${s.remainingGate}`);
313
+ }
314
+
315
+ export function registerOnboardCommands(program, ctx) {
316
+ program
317
+ .command('onboard <host>')
318
+ .description('Guided, idempotent, resumable wizard to onboard a customer site as a governed Quox tenant')
319
+ .requiredOption('--name <customerName>', 'the org display name')
320
+ .option('--repo <owner/name>', 'the customer GitHub repo for auto-filed security issues')
321
+ .option('--target <hostOrIp>', 'the scan target (defaults to <host>)')
322
+ .option('--environment-tier <tier>', 'dev | staging | production', 'dev')
323
+ .option('--schedule-cron <expr>', 'cron cadence for the recurring scan', '0 2 * * 3')
324
+ .option('--tool <name>', 'capability tool to schedule', 'vuln_scan')
325
+ .option('--yes', 'run non-interactively: do every automatable step and print gate instructions without pausing')
326
+ .action(async (host, options) => {
327
+ await runOnboard(host, options, ctx);
328
+ });
329
+ }