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,88 @@
1
+ /**
2
+ * SCIM commands: org SCIM bearer tokens (IdP-driven directory sync auth)
3
+ *
4
+ * quox scim tokens list|create|delete
5
+ *
6
+ * Backend: services/auth/routes/webhooks.js, mounted at plain `/api`
7
+ * (org-scoped IN the URL: /api/orgs/:orgId/scim/tokens...). Enterprise-tier
8
+ * + ORG_MANAGE_SETTINGS gated — expect 402/403 on non-enterprise orgs.
9
+ *
10
+ * The plaintext bearer token is returned ONLY on create — it is never
11
+ * retrievable again after that response (only its SHA-256 is stored). We
12
+ * print it prominently and warn the user to record it now.
13
+ */
14
+
15
+ import { formatOutput, printSuccess, printWarning } from '../core/output.js';
16
+ import { resolveOrgId } from './helpers.js';
17
+ import { resolveEndpoint } from '../api/endpoints.js';
18
+
19
+ function printToken(token) {
20
+ printWarning('Record this bearer token now — it will not be shown again:');
21
+ console.log(` ${token}`);
22
+ }
23
+
24
+ export function registerScimCommands(program, ctx) {
25
+ const { client, auth } = ctx;
26
+
27
+ const scim = program
28
+ .command('scim')
29
+ .description('Manage org SCIM bearer tokens (enterprise — IdP directory sync)');
30
+
31
+ const tokens = scim
32
+ .command('tokens')
33
+ .description('Manage SCIM bearer tokens');
34
+
35
+ // ── scim tokens list ────────────────────────────────────────────────────────
36
+
37
+ tokens
38
+ .command('list')
39
+ .description('List SCIM tokens for the active org')
40
+ .action(async () => {
41
+ auth.requireAuth();
42
+ const orgId = resolveOrgId(auth, program);
43
+ const ep = resolveEndpoint('scim.tokens.list', { orgId });
44
+ const data = await client.get(ep.path, { service: ep.service });
45
+ const rows = data.tokens || data || [];
46
+
47
+ formatOutput({
48
+ columns: ['id', 'label', 'created_at', 'last_used_at', 'revoked_at'],
49
+ headers: ['ID', 'Label', 'Created', 'Last Used', 'Revoked'],
50
+ rows,
51
+ }, { format: program.opts().output, emptyMessage: 'No SCIM tokens configured.' });
52
+ });
53
+
54
+ // ── scim tokens create ──────────────────────────────────────────────────────
55
+
56
+ tokens
57
+ .command('create')
58
+ .description('Create a SCIM bearer token')
59
+ .option('--label <label>', 'human-readable label')
60
+ .action(async (options) => {
61
+ auth.requireAuth();
62
+ const orgId = resolveOrgId(auth, program);
63
+ const body = {};
64
+ if (options.label) body.label = options.label;
65
+
66
+ const ep = resolveEndpoint('scim.tokens.create', { orgId });
67
+ // NOTE: the backend response here is the raw `{ id, token }` shape,
68
+ // not wrapped under a named key like every other create route in
69
+ // this CLI — see endpoints/scim.js's header note.
70
+ const data = await client.post(ep.path, { service: ep.service, body });
71
+ printSuccess(`SCIM token created (id: ${data.id}).`);
72
+ if (data.token) printToken(data.token);
73
+ });
74
+
75
+ // ── scim tokens delete ──────────────────────────────────────────────────────
76
+
77
+ tokens
78
+ .command('delete')
79
+ .description('Revoke a SCIM token')
80
+ .argument('<tokenId>', 'token ID')
81
+ .action(async (tokenId) => {
82
+ auth.requireAuth();
83
+ const orgId = resolveOrgId(auth, program);
84
+ const ep = resolveEndpoint('scim.tokens.delete', { orgId, tokenId });
85
+ await client.delete(ep.path, { service: ep.service });
86
+ printSuccess('SCIM token revoked.');
87
+ });
88
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Scope commands
3
+ *
4
+ * quox scope list
5
+ *
6
+ * Scopes are read-only projections of the org/division/department/team
7
+ * hierarchy — there is no backend write route for scopes. To create,
8
+ * update, or delete a scope, use `quox org`, `quox division`,
9
+ * `quox department`, or `quox team` instead.
10
+ */
11
+
12
+ import { formatOutput } from '../core/output.js';
13
+ import { resolveOrgId } from './helpers.js';
14
+ import { resolveEndpoint } from '../api/endpoints.js';
15
+
16
+ export function registerScopeCommands(program, ctx) {
17
+ const { client, auth } = ctx;
18
+
19
+ const scope = program
20
+ .command('scope')
21
+ .description('View scopes (read-only — use `quox org`/`quox division`/`quox team` to manage)');
22
+
23
+ scope
24
+ .command('list')
25
+ .description('List scopes')
26
+ .action(async () => {
27
+ auth.requireAuth();
28
+ const orgId = resolveOrgId(auth, program);
29
+ // fleet #392: the route returns a nested {tree} unless flat=true is
30
+ // sent, and it was never sent — so `data.scopes` was undefined,
31
+ // `data` fell through as the object {tree: ...}, and `.map()` threw
32
+ // TypeError. This command crashed rather than merely showing dashes.
33
+ // Columns also renamed to the real ones (name_cache/scope_type);
34
+ // `resources` has no counterpart at all, so `path` and `depth` are
35
+ // shown instead of inventing a zero.
36
+ const data = await client.get(resolveEndpoint('scopes.list', { orgId }).path, {
37
+ service: resolveEndpoint('scopes.list', { orgId }).service,
38
+ query: { flat: 'true' },
39
+ });
40
+ const scopes = Array.isArray(data.scopes) ? data.scopes : [];
41
+ // fleet #399.3: `depth` is a scope-TYPE ordinal (services/auth/
42
+ // migrations/000-baseline.sql: "0=org, 1=division, 2=department,
43
+ // 3=team"), not a count of path segments below root. A team can
44
+ // legitimately show Depth 3 while its `path` reads only two levels
45
+ // deep. Header made self-explanatory rather than recomputing depth.
46
+ formatOutput({
47
+ columns: ['id', 'name', 'type', 'path', 'depth'],
48
+ headers: ['ID', 'Name', 'Type', 'Path', 'Depth (0=org,1=div,2=dept,3=team)'],
49
+ rows: scopes.map(s => ({
50
+ id: s.id,
51
+ name: s.name_cache,
52
+ type: s.scope_type,
53
+ path: s.path,
54
+ depth: s.depth,
55
+ })),
56
+ }, { format: program.opts().output, emptyMessage: 'No scopes found.' });
57
+ });
58
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Security commands: org session policy, IP allowlist, org-level MFA policy
3
+ *
4
+ * quox security session-policy get|set
5
+ * quox security ip-allowlist list|add|remove
6
+ * quox security mfa get|set
7
+ *
8
+ * Backend: services/auth/routes/orgSecurity.js, mounted at plain `/api`
9
+ * (org-scoped IN the URL: /api/orgs/:orgId/security/...). Enterprise-tier +
10
+ * ORG_MANAGE_SETTINGS gated — expect 402/403 on non-enterprise orgs.
11
+ *
12
+ * `mfa` here is the ORG-WIDE policy mode (disabled|optional|required), not
13
+ * a specific user's MFA enrollment — see `quox mfa` (endpoints/mfa.js,
14
+ * lib/commands/*.js's personal-MFA equivalent) for that.
15
+ */
16
+
17
+ import { formatOutput, formatDetail, printSuccess, printInfo } from '../core/output.js';
18
+ import { resolveOrgId } from './helpers.js';
19
+ import { resolveEndpoint } from '../api/endpoints.js';
20
+
21
+ export function registerSecurityCommands(program, ctx) {
22
+ const { client, auth } = ctx;
23
+
24
+ const security = program
25
+ .command('security')
26
+ .description('Manage org security policy (enterprise — session, IP allowlist, MFA)');
27
+
28
+ // ── security session-policy ─────────────────────────────────────────────────
29
+
30
+ const sessionPolicy = security
31
+ .command('session-policy')
32
+ .description('Manage the org session timeout policy');
33
+
34
+ sessionPolicy
35
+ .command('get')
36
+ .description('Show the active session policy')
37
+ .action(async () => {
38
+ auth.requireAuth();
39
+ const orgId = resolveOrgId(auth, program);
40
+ const ep = resolveEndpoint('security.session-policy.get', { orgId });
41
+ const data = await client.get(ep.path, { service: ep.service });
42
+ formatDetail(data.policy || data, { format: program.opts().output, title: 'Session Policy' });
43
+ });
44
+
45
+ sessionPolicy
46
+ .command('set')
47
+ .description('Update the session policy (0 = disabled for that dimension)')
48
+ .option('--idle-timeout-sec <n>', 'idle timeout in seconds', (v) => parseInt(v, 10))
49
+ .option('--max-session-sec <n>', 'max session lifetime in seconds', (v) => parseInt(v, 10))
50
+ .option('--remember-me <bool>', 'true|false — allow "remember me" opt-in')
51
+ .action(async (options) => {
52
+ auth.requireAuth();
53
+ const orgId = resolveOrgId(auth, program);
54
+
55
+ const body = {};
56
+ if (options.idleTimeoutSec !== undefined) body.idleTimeoutSec = options.idleTimeoutSec;
57
+ if (options.maxSessionSec !== undefined) body.maxSessionSec = options.maxSessionSec;
58
+ if (options.rememberMe !== undefined) {
59
+ if (options.rememberMe !== 'true' && options.rememberMe !== 'false') {
60
+ throw new Error('--remember-me must be "true" or "false".');
61
+ }
62
+ body.rememberMeEnabled = options.rememberMe === 'true';
63
+ }
64
+ if (Object.keys(body).length === 0) {
65
+ throw new Error('Specify at least one of --idle-timeout-sec, --max-session-sec, --remember-me.');
66
+ }
67
+
68
+ const ep = resolveEndpoint('security.session-policy.set', { orgId });
69
+ const data = await client.put(ep.path, { service: ep.service, body });
70
+ printSuccess('Session policy updated.');
71
+ formatDetail(data.policy || data, { format: program.opts().output });
72
+ });
73
+
74
+ // ── security ip-allowlist ───────────────────────────────────────────────────
75
+
76
+ const ipAllowlist = security
77
+ .command('ip-allowlist')
78
+ .description('Manage the org IP allowlist (empty list = allow all)');
79
+
80
+ ipAllowlist
81
+ .command('list')
82
+ .description('List IP allowlist entries')
83
+ .action(async () => {
84
+ auth.requireAuth();
85
+ const orgId = resolveOrgId(auth, program);
86
+ const ep = resolveEndpoint('security.ip-allowlist.list', { orgId });
87
+ const data = await client.get(ep.path, { service: ep.service });
88
+ const rows = data.entries || data || [];
89
+
90
+ formatOutput({
91
+ columns: ['id', 'cidr', 'label', 'created_at'],
92
+ headers: ['ID', 'CIDR', 'Label', 'Created'],
93
+ rows,
94
+ }, { format: program.opts().output, emptyMessage: 'No IP allowlist entries (all IPs allowed).' });
95
+ });
96
+
97
+ ipAllowlist
98
+ .command('add')
99
+ .description('Add an IP allowlist entry')
100
+ .argument('<cidr>', 'IPv4 CIDR (e.g. "203.0.113.0/24")')
101
+ .option('--label <label>', 'human-readable label')
102
+ .action(async (cidr, options) => {
103
+ auth.requireAuth();
104
+ const orgId = resolveOrgId(auth, program);
105
+ const body = { cidr };
106
+ if (options.label) body.label = options.label;
107
+
108
+ const ep = resolveEndpoint('security.ip-allowlist.add', { orgId });
109
+ const data = await client.post(ep.path, { service: ep.service, body });
110
+ printSuccess(`IP allowlist entry "${cidr}" added.`);
111
+ formatDetail(data.entry || data, { format: program.opts().output });
112
+ });
113
+
114
+ ipAllowlist
115
+ .command('remove')
116
+ .description('Remove an IP allowlist entry')
117
+ .argument('<entryId>', 'entry ID')
118
+ .action(async (entryId) => {
119
+ auth.requireAuth();
120
+ const orgId = resolveOrgId(auth, program);
121
+ const ep = resolveEndpoint('security.ip-allowlist.remove', { orgId, entryId });
122
+ await client.delete(ep.path, { service: ep.service });
123
+ printSuccess('IP allowlist entry removed.');
124
+ });
125
+
126
+ // ── security mfa ────────────────────────────────────────────────────────────
127
+
128
+ const mfa = security
129
+ .command('mfa')
130
+ .description('Manage the org-wide MFA policy mode');
131
+
132
+ mfa
133
+ .command('get')
134
+ .description('Show the active org MFA policy mode')
135
+ .action(async () => {
136
+ auth.requireAuth();
137
+ const orgId = resolveOrgId(auth, program);
138
+ const ep = resolveEndpoint('security.mfa.get', { orgId });
139
+ const data = await client.get(ep.path, { service: ep.service });
140
+ formatDetail(data, { format: program.opts().output, title: 'Org MFA Policy' });
141
+ });
142
+
143
+ mfa
144
+ .command('set')
145
+ .description('Set the org-wide MFA policy mode')
146
+ .argument('<mode>', 'disabled|optional|required')
147
+ .action(async (mode) => {
148
+ auth.requireAuth();
149
+ const orgId = resolveOrgId(auth, program);
150
+ const ep = resolveEndpoint('security.mfa.set', { orgId });
151
+ const data = await client.put(ep.path, { service: ep.service, body: { mode } });
152
+ printSuccess(`Org MFA policy set to "${data.mode || mode}".`);
153
+ if (mode === 'required') {
154
+ printInfo('Members without MFA enrolled will be required to enroll on next login.');
155
+ }
156
+ });
157
+ }
@@ -0,0 +1,292 @@
1
+ /**
2
+ * quox server: lifecycle of a local self-hosted install (plan C4).
3
+ *
4
+ * status version, health per service, disk, ports, last update (read-only)
5
+ * update current -> latest public quoxcore tag; backup first; fetch, build, migrate, health;
6
+ * auto-rollback on failure; resumable; --dry-run prints the plan and runs nothing
7
+ * rollback previous snapshot + previous version; --dry-run
8
+ * uninstall lists exactly what goes, two-step confirmation, keeps backups unless --purge; --dry-run
9
+ * doctor the quox doctor checks + server-side checks (read-only)
10
+ * diag a redacted support bundle written locally (allowlist redaction), never uploaded
11
+ *
12
+ * Every mutating host command goes through lib/server/sys.js gate(): in a dry run it is printed, never run.
13
+ * The install is the one Getting started recorded (or --dir / QUOX_INSTALL_DIR / the running containers).
14
+ */
15
+ import chalk from 'chalk';
16
+ import { homedir } from 'node:os';
17
+ import { resolve } from 'node:path';
18
+ import { resolveEndpoint } from '../api/endpoints.js';
19
+ import { createSys } from '../server/sys.js';
20
+ import { resolveInstall, backupsRoot, readInstalled, fetchAvailable, cmpSemver, vtag, compareUrl, releaseNotesUrl } from '../server/install.js';
21
+ import { gatherStatus, statusRows, defaultUrls } from '../server/status.js';
22
+ import { runUpdate, runRollback, UPDATE_STEPS } from '../server/update.js';
23
+ import { planUninstall, runUninstall } from '../server/uninstall.js';
24
+ import { serverChecks } from '../server/doctor.js';
25
+ import { writeDiagBundle } from '../server/diag.js';
26
+ import { latestSnapshot, listSnapshots } from '../server/snapshot.js';
27
+
28
+ const icon = (s) => (s === 'pass' ? chalk.green('✔') : s === 'warn' ? chalk.yellow('⚠') : s === 'skip' ? chalk.dim('·') : chalk.red('✖'));
29
+
30
+ /** Pure: is the two-step uninstall confirmation satisfied? */
31
+ export function uninstallConfirmed({ word, path, root }) {
32
+ return String(word || '').trim() === 'uninstall' && resolve(String(path || '').trim() || '/nonexistent') === resolve(root);
33
+ }
34
+
35
+ function printRows(rows, out = console.log) {
36
+ for (const r of rows) {
37
+ if (r.kind === 'head') out(chalk.bold(`\n ${r.text}`));
38
+ else if (r.kind === 'gap') out('');
39
+ else if (r.kind === 'dim') out(chalk.dim(` ${r.key ? `${String(r.key).padEnd(16)} ` : ''}${r.text}`));
40
+ else out(` ${String(r.key).padEnd(16)} ${r.text}`);
41
+ }
42
+ out('');
43
+ }
44
+
45
+ export function registerServerCommands(program, ctx = {}) {
46
+ const deps = ctx.serverDeps || {};
47
+ const env = deps.env || process.env;
48
+ const sys = deps.sys || createSys();
49
+ const fetchImpl = deps.fetchImpl || ((...a) => globalThis.fetch(...a));
50
+ const backups = deps.backups || backupsRoot(env);
51
+ const log = deps.log || ((l) => console.log(chalk.dim(` ${l}`)));
52
+ const ask = deps.ask || (async (message) => {
53
+ const { default: inquirer } = await import('inquirer');
54
+ const a = await inquirer.prompt([{ type: 'input', name: 'v', message }]);
55
+ return a.v;
56
+ });
57
+ const isTTY = deps.isTTY ?? (() => Boolean(process.stdin.isTTY));
58
+ const urls = () => deps.urls || defaultUrls(ctx.config);
59
+ const json = () => program.opts().output === 'json' || program.opts().json;
60
+ const dry = (o) => Boolean(o.dryRun || program.opts().dryRun);
61
+ const yes = (o) => Boolean(o.yes || program.opts().yes);
62
+
63
+ async function install(o) {
64
+ const i = await resolveInstall({ dir: o.dir, env, sys, journey: deps.journey });
65
+ if (i.error) { const e = new Error(i.error); e.exitCode = 2; throw e; }
66
+ return i;
67
+ }
68
+
69
+ const server = program.command('server').description('Your self-hosted Quox install: status, update, rollback, uninstall, doctor, diag');
70
+ const withDir = (c) => c.option('--dir <path>', 'the install directory (default: the one Getting started recorded)');
71
+
72
+ withDir(server.command('status').description('Version, health per service, disk, ports and the last update (read-only)'))
73
+ .action(async (o) => {
74
+ const i = await install(o);
75
+ const st = await gatherStatus({ install: i, sys, fetchImpl, urls: urls(), backups });
76
+ if (json()) { console.log(JSON.stringify(st, null, 2)); return; }
77
+ printRows(statusRows(st));
78
+ if (!st.http.every((h) => h.ok) || (st.services.ok && st.services.services.some((s) => s.state !== 'running'))) process.exitCode = 1;
79
+ });
80
+
81
+ withDir(server.command('update').description('Update to the latest release: backup first, then fetch, build, migrate, health; rolls back on failure'))
82
+ .option('--dry-run', 'print the plan and run nothing')
83
+ .option('--to <version>', 'a specific release (default: the latest quoxcore tag)')
84
+ .option('--force', 'proceed despite local changes or low disk, or re-apply the same version')
85
+ .option('--restart', 'abandon a stopped update to a different version and start this one')
86
+ .option('--no-rollback', 'on failure, stop where it failed instead of rolling back')
87
+ .option('-y, --yes', 'skip the confirmation')
88
+ .action(async (o) => {
89
+ const i = await install(o);
90
+ const dryRun = dry(o);
91
+ if (!dryRun && !yes(o)) {
92
+ const installed = await readInstalled(i, sys);
93
+ let target = o.to ? vtag(o.to) : null;
94
+ if (!target) { const av = await fetchAvailable({ fetchImpl }); if (!av.ok) throw new Error(`could not read the available versions: ${av.error}`); target = av.latest; }
95
+ const cur = installed.release ? vtag(installed.release) : '?';
96
+ if (cmpSemver(target, cur) !== null && cmpSemver(target, cur) <= 0 && !o.force) { console.log(`Already on ${cur} (latest is ${target}). Nothing to do.`); return; }
97
+ console.log(`\n Update ${cur} -> ${chalk.bold(target)} at ${i.repoDir}`);
98
+ console.log(` Changes: ${installed.release ? compareUrl(cur, target) : releaseNotesUrl(target)}`);
99
+ console.log(` A backup (database + config) is taken first, into ${backups}. The stack stops briefly while volumes are archived.`);
100
+ console.log(' Preview every command first with: quox server update --dry-run\n');
101
+ if (!isTTY()) throw new Error('quox server update needs confirmation; re-run with --yes in a non-interactive shell');
102
+ const a = await ask(`Type "update" to update to ${target}`);
103
+ if (String(a).trim() !== 'update') { console.log('Not updated. Nothing was changed.'); return; }
104
+ }
105
+ const r = await runUpdate({ install: i, sys, backups, urls: urls(), fetchImpl, dryRun, to: o.to, force: o.force, restart: o.restart, noRollback: o.rollback === false, log });
106
+ if (json()) { console.log(JSON.stringify(r, null, 2)); if (!r.ok) process.exitCode = 2; return; }
107
+ const p = r.plan || {};
108
+ if (r.upToDate) { console.log(`Already on ${p.current} (latest is ${p.target}). Nothing to do.`); return; }
109
+ if (dryRun && r.ok) {
110
+ console.log(chalk.bold(`\n Plan: ${p.current || '?'} (${p.currentCommit || '?'}) -> ${p.target}${p.targetCommit ? ` (${p.targetCommit})` : ''}`));
111
+ if (p.changes) console.log(` What changes: ${p.changes}`);
112
+ console.log(` Release notes: ${p.releaseNotes} (if published for this tag)`);
113
+ if (p.resumedAfter) console.log(` Resumes after: ${p.resumedAfter}`);
114
+ console.log(` Steps: ${UPDATE_STEPS.join(' -> ')}; on failure it rolls back automatically.`);
115
+ if (p.problems?.length) for (const x of p.problems) console.log(chalk.yellow(` ⚠ ${x}`));
116
+ console.log(chalk.dim(`\n Dry run: ${r.planned.length} changes listed above, none made. Re-run without --dry-run to update.\n`));
117
+ return;
118
+ }
119
+ if (r.ok) { console.log(chalk.green(`\n ✔ Updated to ${p.target}. Backup: ${p.snapshot}\n`)); return; }
120
+ process.exitCode = 2;
121
+ console.log(chalk.red(`\n ✖ Update failed at ${r.failedStep}: ${r.error}`));
122
+ if (r.rollback) console.log(r.rolledBack ? chalk.yellow(` Rolled back (${r.rollback.scope}); you are on ${p.current} again.`) : chalk.red(` Rollback did not complete: ${r.rollback.error}. Backup: ${p.snapshot}. Run quox server rollback.`));
123
+ else if (r.failedStep !== 'preflight' && r.failedStep !== 'backup') console.log(` Not rolled back. Re-run quox server update to resume, or quox server rollback.`);
124
+ console.log('');
125
+ });
126
+
127
+ withDir(server.command('rollback').description('Restore the previous snapshot and version (database, config, code)'))
128
+ .option('--dry-run', 'print the plan and run nothing')
129
+ .option('--snapshot <dir>', 'a specific snapshot (default: the latest)')
130
+ .option('--list', 'list the snapshots and exit')
131
+ .option('-y, --yes', 'skip the confirmation')
132
+ .action(async (o) => {
133
+ if (o.list) {
134
+ const s = listSnapshots(backups);
135
+ if (json()) { console.log(JSON.stringify(s, null, 2)); return; }
136
+ if (!s.length) { console.log(`No snapshots in ${backups}.`); return; }
137
+ for (const x of s) console.log(` ${x.createdAt} ${String(x.release || '?').padEnd(8)} ${String(x.commit || '?').padEnd(10)} ${x.reason} ${x.dir}`);
138
+ return;
139
+ }
140
+ const i = await install(o);
141
+ const dryRun = dry(o);
142
+ const snap = o.snapshot ? listSnapshots(backups).find((s) => s.dir === o.snapshot || s.dir.endsWith(`/${o.snapshot}`)) : latestSnapshot(backups);
143
+ if (!snap) throw new Error(o.snapshot ? `no complete snapshot at ${o.snapshot}` : `no snapshot in ${backups}; nothing to roll back to`);
144
+ if (!dryRun && !yes(o)) {
145
+ console.log(`\n Roll back to ${snap.release || '?'} (${snap.commit}) from ${snap.createdAt}.`);
146
+ console.log(chalk.yellow(' This REPLACES the current database and config with the snapshot; anything since then is lost.'));
147
+ console.log(' Preview first with: quox server rollback --dry-run\n');
148
+ if (!isTTY()) throw new Error('quox server rollback needs confirmation; re-run with --yes in a non-interactive shell');
149
+ const a = await ask('Type "rollback" to continue');
150
+ if (String(a).trim() !== 'rollback') { console.log('Not rolled back. Nothing was changed.'); return; }
151
+ }
152
+ const r = await runRollback({ install: i, sys, backups, urls: urls(), fetchImpl, dryRun, snapshotDir: snap.dir, log });
153
+ if (json()) { console.log(JSON.stringify(r, null, 2)); if (!r.ok) process.exitCode = 2; return; }
154
+ if (r.ok && dryRun) { console.log(chalk.dim(`\n Dry run: rollback to ${snap.release || snap.commit} from ${snap.dir}; ${r.planned.length} changes listed above, none made.\n`)); return; }
155
+ if (r.ok) { console.log(chalk.green(`\n ✔ Rolled back to ${snap.release || snap.commit}.\n`)); return; }
156
+ process.exitCode = 2;
157
+ console.log(chalk.red(`\n ✖ Rollback failed at ${r.step}: ${r.error}\n`));
158
+ });
159
+
160
+ withDir(server.command('uninstall').description('Remove this install (lists everything first; keeps backups unless --purge)'))
161
+ .option('--dry-run', 'list what would be removed and run nothing')
162
+ .option('--purge', 'also delete every backup')
163
+ .option('--no-backup', 'skip the final snapshot')
164
+ .option('-y, --yes', 'non-interactive: confirm step one (needs --confirm too)')
165
+ .option('--confirm <path>', 'non-interactive: confirm step two by naming the install directory')
166
+ .action(async (o) => {
167
+ const i = await install(o);
168
+ const dryRun = dry(o);
169
+ const plan = await planUninstall({ install: i, sys, backups, purge: Boolean(o.purge), home: deps.home || homedir() });
170
+ if (json() && dryRun) { console.log(JSON.stringify({ plan }, null, 2)); return; }
171
+ console.log(chalk.bold('\n This removes:'));
172
+ console.log(` Containers (${plan.containers.length}): ${plan.containers.join(', ') || plan.containersError || 'none'}`);
173
+ console.log(` Volumes, with ALL their data (${plan.volumes.length}): ${plan.volumes.join(', ') || 'none'}`);
174
+ console.log(` Images built for this install (${plan.images.length}): ${plan.images.join(', ') || 'none'}`);
175
+ console.log(` Networks: ${plan.networks}`);
176
+ console.log(` Directories: ${[...plan.dirs, ...(plan.root ? [plan.root] : [])].join(', ')}`);
177
+ if (plan.journeyFile) console.log(` Getting started state: ${plan.journeyFile}`);
178
+ if (plan.refused.length) console.log(chalk.yellow(` Refusing to remove (too broad): ${plan.refused.join(', ')}`));
179
+ console.log(chalk.bold(' It keeps:'));
180
+ console.log(plan.backups.removed ? chalk.red(` Nothing: --purge also deletes ${plan.backups.count} backup(s) in ${plan.backups.dir}`)
181
+ : ` Backups in ${plan.backups.dir} (${plan.backups.count} now${o.backup === false ? '' : ', plus a final snapshot taken first'})`);
182
+ console.log('');
183
+ if (!dryRun) {
184
+ let ok;
185
+ if (isTTY() && !(yes(o) && o.confirm)) {
186
+ const w = await ask('Step 1 of 2: type "uninstall" to continue');
187
+ if (String(w).trim() !== 'uninstall') { console.log('Not uninstalled. Nothing was changed.'); return; }
188
+ const p = await ask(`Step 2 of 2: type the install directory (${i.root}) to confirm`);
189
+ ok = uninstallConfirmed({ word: 'uninstall', path: p, root: i.root });
190
+ } else ok = yes(o) && uninstallConfirmed({ word: 'uninstall', path: o.confirm, root: i.root });
191
+ if (!ok) {
192
+ if (!isTTY()) throw new Error(`quox server uninstall needs two confirmations; in a non-interactive shell pass --yes and --confirm ${i.root}`);
193
+ console.log('The directory did not match. Not uninstalled. Nothing was changed.');
194
+ return;
195
+ }
196
+ }
197
+ const r = await runUninstall({ install: i, sys, backups, purge: Boolean(o.purge), noBackup: o.backup === false, dryRun, log, plan });
198
+ if (json()) { console.log(JSON.stringify(r, null, 2)); if (!r.ok) process.exitCode = 2; return; }
199
+ if (!r.ok) { process.exitCode = 2; console.log(chalk.red(` ✖ Uninstall stopped at ${r.step}: ${r.error}\n`)); return; }
200
+ if (dryRun) { console.log(chalk.dim(` Dry run: ${r.planned.length} changes listed above, none made.\n`)); return; }
201
+ console.log(chalk.green(` ✔ Uninstalled.${r.finalSnapshot ? ` Final snapshot: ${r.finalSnapshot}` : ''}\n`));
202
+ });
203
+
204
+ withDir(server.command('doctor').description('The quox doctor checks plus server-side checks (read-only)'))
205
+ .action(async (o) => {
206
+ const i = await install(o);
207
+ const { runDoctorChecks } = await import('./doctor.js');
208
+ const platform = deps.platformChecks ? await deps.platformChecks() : await runDoctorChecks({ config: ctx.config });
209
+ const srv = await serverChecks({ install: i, sys, fetchImpl, urls: urls(), backups });
210
+ const all = [...platform, ...srv];
211
+ const failed = all.filter((c) => c.status === 'fail' || c.status === 'error').length;
212
+ const warned = all.filter((c) => c.status === 'warn').length;
213
+ if (json()) { console.log(JSON.stringify({ platform, server: srv, summary: { failed, warnings: warned } }, null, 2)); }
214
+ else {
215
+ console.log(chalk.bold('\n Platform'));
216
+ for (const c of platform) console.log(` ${icon(c.status)} ${String(c.name).padEnd(20)} ${chalk.dim(c.detail || '')}`);
217
+ console.log(chalk.bold('\n Server'));
218
+ for (const c of srv) console.log(` ${icon(c.status)} ${String(c.name).padEnd(20)} ${chalk.dim(c.detail || '')}`);
219
+ console.log(`\n ${all.length - failed - warned} passed · ${warned} warnings · ${failed} failed\n`);
220
+ }
221
+ process.exitCode = failed ? 2 : warned ? 1 : 0;
222
+ });
223
+
224
+ withDir(server.command('diag').description('Write a redacted support bundle locally (never uploaded)'))
225
+ .option('--out <dir>', 'where to write it (default: the current directory)')
226
+ .action(async (o) => {
227
+ const i = await install(o);
228
+ const { runDoctorChecks } = await import('./doctor.js');
229
+ const { resolveCliVersion } = await import('../core/version.js');
230
+ const st = await gatherStatus({ install: i, sys, fetchImpl, urls: urls(), backups });
231
+ const platform = deps.platformChecks ? await deps.platformChecks() : await runDoctorChecks({ config: ctx.config }).catch(() => []);
232
+ const srv = await serverChecks({ install: i, sys, fetchImpl, urls: urls(), backups });
233
+ const r = await writeDiagBundle({
234
+ install: i, sys, out: resolve(o.out || process.cwd()),
235
+ data: { versions: { cli: resolveCliVersion(), installed: st.installed, running: st.running }, status: st, doctor: [...platform, ...srv] }
236
+ });
237
+ if (json()) { console.log(JSON.stringify(r, null, 2)); return; }
238
+ console.log(chalk.green(`\n ✔ Support bundle written: ${r.file}`));
239
+ console.log(chalk.dim(' Nothing was uploaded. Secrets are kept out by an allowlist; look through it before you share it.\n'));
240
+ });
241
+
242
+ // `quox server config app-url [url]`: read or set the public app URL (instance_config app_base_url).
243
+ // Server-side source of truth, the same value Settings > General > App URL edits.
244
+ const cfg = server.command('config').description('Instance settings stored on the server');
245
+ cfg.command('app-url')
246
+ .description('Show or set the public app URL (used in sign-in links and emails). Pass a URL to set it, or --clear to fall back to the environment value')
247
+ .argument('[url]', 'the new public app URL, e.g. https://quox.example.com')
248
+ .option('--clear', 'remove the stored value')
249
+ .action(async (url, o) => {
250
+ const { client, auth } = ctx;
251
+ auth?.requireAuth?.();
252
+ const get = resolveEndpoint('admin.instance.basics.get');
253
+ const set = resolveEndpoint('admin.instance.basics.set');
254
+ if (url && o.clear) { const e = new Error('give a URL or --clear, not both'); e.exitCode = 2; throw e; }
255
+ if (url || o.clear) {
256
+ const r = await client.put(set.path, { service: set.service, body: { appBaseUrl: o.clear ? null : url } });
257
+ if (json()) { console.log(JSON.stringify(r, null, 2)); return; }
258
+ console.log(chalk.green(o.clear ? ' ✔ App URL cleared; using the environment value.' : ` ✔ App URL set to ${url}`));
259
+ console.log(chalk.dim(` Effective now: ${r.effective?.appBaseUrl}\n`));
260
+ return;
261
+ }
262
+ const r = await client.get(get.path, { service: get.service });
263
+ if (json()) { console.log(JSON.stringify(r, null, 2)); return; }
264
+ const src = r.effective?.appBaseUrlSource === 'instance_config' ? 'set on this server' : 'from the environment (not set in Settings)';
265
+ console.log(`${r.effective?.appBaseUrl} ${chalk.dim(`(${src})`)}`);
266
+ });
267
+
268
+ // `quox server config lan-targets [on|off]`: let integrations (Ollama, LM Studio, vLLM, Grafana ...)
269
+ // point at private-network addresses. Default off. Loopback and link-local stay blocked either way.
270
+ cfg.command('lan-targets')
271
+ .description('Show or set whether AI servers and integrations on your own private network are allowed (default off)')
272
+ .argument('[state]', 'on or off')
273
+ .action(async (state) => {
274
+ const { client, auth } = ctx;
275
+ auth?.requireAuth?.();
276
+ const get = resolveEndpoint('admin.instance.lan_targets.get');
277
+ const set = resolveEndpoint('admin.instance.lan_targets.set');
278
+ if (state !== undefined && !['on', 'off'].includes(String(state).toLowerCase())) { const e = new Error('give on or off'); e.exitCode = 2; throw e; }
279
+ if (state !== undefined) {
280
+ const r = await client.put(set.path, { service: set.service, body: { enabled: String(state).toLowerCase() === 'on' } });
281
+ if (json()) { console.log(JSON.stringify(r, null, 2)); return; }
282
+ console.log(chalk.green(r.enabled ? ' ✔ Private-network targets are ON: integrations may use addresses on your own network.' : ' ✔ Private-network targets are OFF.'));
283
+ console.log(chalk.dim(' Loopback (localhost) and link-local addresses stay blocked either way.\n'));
284
+ return;
285
+ }
286
+ const r = await client.get(get.path, { service: get.service });
287
+ if (json()) { console.log(JSON.stringify(r, null, 2)); return; }
288
+ console.log(`Private-network targets: ${r.enabled ? 'on' : 'off'} ${chalk.dim('(quox server config lan-targets on|off)')}`);
289
+ });
290
+
291
+ return server;
292
+ }