quoxcli 0.0.0-stage → 0.2.0

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 (348) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +142 -2
  3. package/bin/quox.js +292 -0
  4. package/bin/quoxon.js +8 -0
  5. package/lib/api/endpoints/admin.js +125 -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/agents.js +56 -0
  9. package/lib/api/endpoints/assistants.js +50 -0
  10. package/lib/api/endpoints/audit.js +14 -0
  11. package/lib/api/endpoints/auth.js +50 -0
  12. package/lib/api/endpoints/awareness.js +34 -0
  13. package/lib/api/endpoints/backup-schedules.js +20 -0
  14. package/lib/api/endpoints/backups.js +86 -0
  15. package/lib/api/endpoints/brain.js +140 -0
  16. package/lib/api/endpoints/budgets.js +62 -0
  17. package/lib/api/endpoints/calendarq.js +71 -0
  18. package/lib/api/endpoints/catalog.js +14 -0
  19. package/lib/api/endpoints/chat.js +54 -0
  20. package/lib/api/endpoints/consent.js +26 -0
  21. package/lib/api/endpoints/contactsq.js +66 -0
  22. package/lib/api/endpoints/conversations.js +32 -0
  23. package/lib/api/endpoints/custom-roles.js +92 -0
  24. package/lib/api/endpoints/divisions.js +56 -0
  25. package/lib/api/endpoints/domains.js +38 -0
  26. package/lib/api/endpoints/engagement.js +231 -0
  27. package/lib/api/endpoints/engine.js +11 -0
  28. package/lib/api/endpoints/entity.js +50 -0
  29. package/lib/api/endpoints/executions.js +32 -0
  30. package/lib/api/endpoints/file.js +20 -0
  31. package/lib/api/endpoints/files.js +26 -0
  32. package/lib/api/endpoints/github.js +21 -0
  33. package/lib/api/endpoints/governance.js +43 -0
  34. package/lib/api/endpoints/hat.js +36 -0
  35. package/lib/api/endpoints/inbox.js +32 -0
  36. package/lib/api/endpoints/inboxq.js +120 -0
  37. package/lib/api/endpoints/index.js +228 -0
  38. package/lib/api/endpoints/invites.js +19 -0
  39. package/lib/api/endpoints/llm-policy.js +43 -0
  40. package/lib/api/endpoints/lucid.js +89 -0
  41. package/lib/api/endpoints/members.js +32 -0
  42. package/lib/api/endpoints/memory.js +75 -0
  43. package/lib/api/endpoints/mfa.js +32 -0
  44. package/lib/api/endpoints/mon.js +178 -0
  45. package/lib/api/endpoints/notesq.js +43 -0
  46. package/lib/api/endpoints/objectives.js +62 -0
  47. package/lib/api/endpoints/orgs.js +38 -0
  48. package/lib/api/endpoints/plugins.js +134 -0
  49. package/lib/api/endpoints/powers.js +75 -0
  50. package/lib/api/endpoints/prompts.js +69 -0
  51. package/lib/api/endpoints/qlarity.js +122 -0
  52. package/lib/api/endpoints/quoxbox.js +55 -0
  53. package/lib/api/endpoints/quoxchat.js +54 -0
  54. package/lib/api/endpoints/quoxflow.js +32 -0
  55. package/lib/api/endpoints/quoxplan.js +114 -0
  56. package/lib/api/endpoints/quoxseo.js +108 -0
  57. package/lib/api/endpoints/reach.js +47 -0
  58. package/lib/api/endpoints/runs.js +38 -0
  59. package/lib/api/endpoints/safety.js +46 -0
  60. package/lib/api/endpoints/scim.js +46 -0
  61. package/lib/api/endpoints/scopes.js +14 -0
  62. package/lib/api/endpoints/security.js +69 -0
  63. package/lib/api/endpoints/setup.js +90 -0
  64. package/lib/api/endpoints/skills.js +180 -0
  65. package/lib/api/endpoints/sso.js +67 -0
  66. package/lib/api/endpoints/tags.js +32 -0
  67. package/lib/api/endpoints/teams.js +56 -0
  68. package/lib/api/endpoints/telephony.js +46 -0
  69. package/lib/api/endpoints/tg.js +56 -0
  70. package/lib/api/endpoints/tools.js +50 -0
  71. package/lib/api/endpoints/traces.js +157 -0
  72. package/lib/api/endpoints/triggers.js +70 -0
  73. package/lib/api/endpoints/user-library.js +41 -0
  74. package/lib/api/endpoints/users.js +20 -0
  75. package/lib/api/endpoints/v1.js +499 -0
  76. package/lib/api/endpoints/vault.js +192 -0
  77. package/lib/api/endpoints/voice.js +22 -0
  78. package/lib/api/endpoints/volt.js +84 -0
  79. package/lib/api/endpoints/ward.js +40 -0
  80. package/lib/api/endpoints/webhooks.js +46 -0
  81. package/lib/api/endpoints/workflows.js +162 -0
  82. package/lib/api/endpoints/x.js +24 -0
  83. package/lib/api/endpoints.js +15 -0
  84. package/lib/commands/admin.js +359 -0
  85. package/lib/commands/aee.js +99 -0
  86. package/lib/commands/agent.js +201 -0
  87. package/lib/commands/agentAttach.js +1440 -0
  88. package/lib/commands/approvals.js +47 -0
  89. package/lib/commands/assistant.js +114 -0
  90. package/lib/commands/auth.js +458 -0
  91. package/lib/commands/awareness.js +88 -0
  92. package/lib/commands/backup.js +373 -0
  93. package/lib/commands/brain.js +994 -0
  94. package/lib/commands/budget.js +241 -0
  95. package/lib/commands/build.js +156 -0
  96. package/lib/commands/calendar.js +216 -0
  97. package/lib/commands/capabilities.js +34 -0
  98. package/lib/commands/chat.js +240 -0
  99. package/lib/commands/claude.js +354 -0
  100. package/lib/commands/config-cmd.js +106 -0
  101. package/lib/commands/consent.js +84 -0
  102. package/lib/commands/contacts.js +967 -0
  103. package/lib/commands/contactsSelftest.js +132 -0
  104. package/lib/commands/conversation.js +74 -0
  105. package/lib/commands/department.js +75 -0
  106. package/lib/commands/dev.js +620 -0
  107. package/lib/commands/devResident.js +314 -0
  108. package/lib/commands/devSupervisor.js +1050 -0
  109. package/lib/commands/discord.js +96 -0
  110. package/lib/commands/doctor.js +181 -0
  111. package/lib/commands/domain.js +114 -0
  112. package/lib/commands/done.js +277 -0
  113. package/lib/commands/engagement.js +586 -0
  114. package/lib/commands/entity.js +119 -0
  115. package/lib/commands/facebook.js +212 -0
  116. package/lib/commands/file.js +128 -0
  117. package/lib/commands/fleet.js +882 -0
  118. package/lib/commands/governance.js +638 -0
  119. package/lib/commands/gripe.js +324 -0
  120. package/lib/commands/hat.js +205 -0
  121. package/lib/commands/helpers.js +79 -0
  122. package/lib/commands/inbox.js +122 -0
  123. package/lib/commands/instagram.js +264 -0
  124. package/lib/commands/integration.js +100 -0
  125. package/lib/commands/inventory.js +115 -0
  126. package/lib/commands/invites.js +45 -0
  127. package/lib/commands/licence.js +280 -0
  128. package/lib/commands/linkedin.js +178 -0
  129. package/lib/commands/llm-policy.js +117 -0
  130. package/lib/commands/llm.js +45 -0
  131. package/lib/commands/lucid.js +591 -0
  132. package/lib/commands/mail.js +531 -0
  133. package/lib/commands/memory.js +284 -0
  134. package/lib/commands/mon.js +514 -0
  135. package/lib/commands/monitoring.js +86 -0
  136. package/lib/commands/motion.js +22 -0
  137. package/lib/commands/notes.js +1246 -0
  138. package/lib/commands/notesBatch.js +160 -0
  139. package/lib/commands/notesBridges.js +56 -0
  140. package/lib/commands/notesLinks.js +60 -0
  141. package/lib/commands/notification.js +57 -0
  142. package/lib/commands/nucleus.js +92 -0
  143. package/lib/commands/objective.js +229 -0
  144. package/lib/commands/onboard.js +328 -0
  145. package/lib/commands/open.js +56 -0
  146. package/lib/commands/org.js +266 -0
  147. package/lib/commands/plan.js +906 -0
  148. package/lib/commands/plugin-doctor.js +1091 -0
  149. package/lib/commands/plugin.js +539 -0
  150. package/lib/commands/policy.js +78 -0
  151. package/lib/commands/powers.js +296 -0
  152. package/lib/commands/prompt.js +350 -0
  153. package/lib/commands/qlarity.js +690 -0
  154. package/lib/commands/quoxbox.js +270 -0
  155. package/lib/commands/quoxchat.js +266 -0
  156. package/lib/commands/quoxseo.js +340 -0
  157. package/lib/commands/reach.js +158 -0
  158. package/lib/commands/role.js +286 -0
  159. package/lib/commands/run.js +132 -0
  160. package/lib/commands/safety.js +107 -0
  161. package/lib/commands/scim.js +88 -0
  162. package/lib/commands/scope.js +58 -0
  163. package/lib/commands/security.js +157 -0
  164. package/lib/commands/service.js +89 -0
  165. package/lib/commands/settings.js +99 -0
  166. package/lib/commands/setup.js +543 -0
  167. package/lib/commands/skillManifest.js +113 -0
  168. package/lib/commands/skillRightsFormat.js +38 -0
  169. package/lib/commands/skills.js +644 -0
  170. package/lib/commands/slot.js +1198 -0
  171. package/lib/commands/sso.js +229 -0
  172. package/lib/commands/status.js +137 -0
  173. package/lib/commands/tag.js +91 -0
  174. package/lib/commands/team.js +140 -0
  175. package/lib/commands/tg.js +240 -0
  176. package/lib/commands/tiktok.js +218 -0
  177. package/lib/commands/tool.js +216 -0
  178. package/lib/commands/trace.js +525 -0
  179. package/lib/commands/trigger.js +128 -0
  180. package/lib/commands/vault.js +1424 -0
  181. package/lib/commands/voice.js +182 -0
  182. package/lib/commands/volt.js +528 -0
  183. package/lib/commands/watch.js +115 -0
  184. package/lib/commands/webhook.js +137 -0
  185. package/lib/commands/workflow.js +503 -0
  186. package/lib/commands/x.js +439 -0
  187. package/lib/core/actingAs.js +136 -0
  188. package/lib/core/aee.js +53 -0
  189. package/lib/core/agentAlias.js +163 -0
  190. package/lib/core/approvalContext.js +61 -0
  191. package/lib/core/auth.js +350 -0
  192. package/lib/core/banner.js +143 -0
  193. package/lib/core/brainPrime.js +90 -0
  194. package/lib/core/callerSource.js +129 -0
  195. package/lib/core/claudeLs.js +94 -0
  196. package/lib/core/client.js +584 -0
  197. package/lib/core/completions.js +163 -0
  198. package/lib/core/config.js +551 -0
  199. package/lib/core/confirm.js +37 -0
  200. package/lib/core/deeplinks.js +76 -0
  201. package/lib/core/doneEngineFetch.js +219 -0
  202. package/lib/core/errors.js +132 -0
  203. package/lib/core/global-flags.js +90 -0
  204. package/lib/core/governedSession.js +402 -0
  205. package/lib/core/lazyCommands.js +119 -0
  206. package/lib/core/liveSessionsReport.js +112 -0
  207. package/lib/core/org-override.js +52 -0
  208. package/lib/core/output.js +239 -0
  209. package/lib/core/prodEnv.js +7 -0
  210. package/lib/core/quoxglyph.js +158 -0
  211. package/lib/core/residentAlarm.js +282 -0
  212. package/lib/core/residentAuth.js +243 -0
  213. package/lib/core/residentPump.js +229 -0
  214. package/lib/core/residentSeed.js +104 -0
  215. package/lib/core/sessionHeartbeat.js +192 -0
  216. package/lib/core/sessionsOverview.js +160 -0
  217. package/lib/core/slotKeeper.js +282 -0
  218. package/lib/core/supervisorEngine.js +864 -0
  219. package/lib/core/systemdInstaller.js +256 -0
  220. package/lib/core/version.js +87 -0
  221. package/lib/core/websiteSession.js +148 -0
  222. package/lib/lucid/LucidIntro.jsx +105 -0
  223. package/lib/lucid/blind.js +71 -0
  224. package/lib/lucid/deck.js +32 -0
  225. package/lib/lucid/fmt.js +139 -0
  226. package/lib/lucid/sitrep.js +173 -0
  227. package/lib/plugins/loader.js +93 -0
  228. package/lib/quoxon/activity.js +104 -0
  229. package/lib/quoxon/api.js +473 -0
  230. package/lib/quoxon/app.js +1262 -0
  231. package/lib/quoxon/capabilities.js +75 -0
  232. package/lib/quoxon/commands.js +154 -0
  233. package/lib/quoxon/connect.js +467 -0
  234. package/lib/quoxon/connectCard.js +28 -0
  235. package/lib/quoxon/context.js +68 -0
  236. package/lib/quoxon/copy.js +33 -0
  237. package/lib/quoxon/demo.fixtures.js +24 -0
  238. package/lib/quoxon/demo.js +35 -0
  239. package/lib/quoxon/egg.js +36 -0
  240. package/lib/quoxon/events.js +458 -0
  241. package/lib/quoxon/gallery.js +223 -0
  242. package/lib/quoxon/indicator.js +193 -0
  243. package/lib/quoxon/intent.js +41 -0
  244. package/lib/quoxon/keys.js +121 -0
  245. package/lib/quoxon/main.js +244 -0
  246. package/lib/quoxon/markdown.js +317 -0
  247. package/lib/quoxon/model.js +205 -0
  248. package/lib/quoxon/motion/clock.js +69 -0
  249. package/lib/quoxon/motion/color.js +35 -0
  250. package/lib/quoxon/motion/frame.js +14 -0
  251. package/lib/quoxon/motion/reveal.js +54 -0
  252. package/lib/quoxon/motion/ring.js +79 -0
  253. package/lib/quoxon/objects/agent.js +61 -0
  254. package/lib/quoxon/objects/crystal.js +100 -0
  255. package/lib/quoxon/objects/fetch.js +170 -0
  256. package/lib/quoxon/objects/index.js +85 -0
  257. package/lib/quoxon/objects/ladder.js +71 -0
  258. package/lib/quoxon/objects/memory.js +44 -0
  259. package/lib/quoxon/objects/model.js +118 -0
  260. package/lib/quoxon/objects/proof.js +110 -0
  261. package/lib/quoxon/objects/skill.js +105 -0
  262. package/lib/quoxon/objects/view.js +76 -0
  263. package/lib/quoxon/objects/workflow.js +119 -0
  264. package/lib/quoxon/observers.js +42 -0
  265. package/lib/quoxon/onboarding/copy.js +235 -0
  266. package/lib/quoxon/onboarding/credential.js +95 -0
  267. package/lib/quoxon/onboarding/detect.js +22 -0
  268. package/lib/quoxon/onboarding/finish.js +119 -0
  269. package/lib/quoxon/onboarding/index.js +67 -0
  270. package/lib/quoxon/onboarding/installRunner.js +225 -0
  271. package/lib/quoxon/onboarding/intent.js +53 -0
  272. package/lib/quoxon/onboarding/journey.js +73 -0
  273. package/lib/quoxon/onboarding/preflight.js +142 -0
  274. package/lib/quoxon/onboarding/runner.js +58 -0
  275. package/lib/quoxon/onboarding/view.js +625 -0
  276. package/lib/quoxon/print.js +94 -0
  277. package/lib/quoxon/qart.js +379 -0
  278. package/lib/quoxon/qglyph.js +143 -0
  279. package/lib/quoxon/qhello.js +58 -0
  280. package/lib/quoxon/qmotion.js +111 -0
  281. package/lib/quoxon/render.js +648 -0
  282. package/lib/quoxon/roster.js +221 -0
  283. package/lib/quoxon/route.js +38 -0
  284. package/lib/quoxon/rules.js +130 -0
  285. package/lib/quoxon/session.js +68 -0
  286. package/lib/quoxon/skills.js +30 -0
  287. package/lib/quoxon/slash/compact.js +90 -0
  288. package/lib/quoxon/slash/ledger.js +83 -0
  289. package/lib/quoxon/slash/listView.js +28 -0
  290. package/lib/quoxon/slash/palette.js +104 -0
  291. package/lib/quoxon/slash/paletteController.js +59 -0
  292. package/lib/quoxon/slash/panels.js +101 -0
  293. package/lib/quoxon/slash/picker.js +38 -0
  294. package/lib/quoxon/slash/restart.js +144 -0
  295. package/lib/quoxon/slash/resume.js +59 -0
  296. package/lib/quoxon/slash/settings.js +50 -0
  297. package/lib/quoxon/sound/index.js +186 -0
  298. package/lib/quoxon/sound/mapping.js +119 -0
  299. package/lib/quoxon/sound/output.js +52 -0
  300. package/lib/quoxon/sound/synth.js +122 -0
  301. package/lib/quoxon/term.js +106 -0
  302. package/lib/quoxon/textBuffer.js +242 -0
  303. package/lib/quoxon/theme.js +253 -0
  304. package/lib/quoxon/transcript.js +150 -0
  305. package/lib/quoxon/web/qface.js +81 -0
  306. package/lib/quoxon/welcome.js +52 -0
  307. package/lib/tui/app.jsx +289 -0
  308. package/lib/tui/components/Breadcrumb.jsx +42 -0
  309. package/lib/tui/components/ChatInput.jsx +68 -0
  310. package/lib/tui/components/Confirm.jsx +32 -0
  311. package/lib/tui/components/DataTable.jsx +270 -0
  312. package/lib/tui/components/DetailPanel.jsx +241 -0
  313. package/lib/tui/components/HelpOverlay.jsx +67 -0
  314. package/lib/tui/components/Layout.jsx +129 -0
  315. package/lib/tui/components/Palette.jsx +141 -0
  316. package/lib/tui/components/SearchInput.jsx +125 -0
  317. package/lib/tui/components/Sparkline.jsx +55 -0
  318. package/lib/tui/components/StatusBar.jsx +133 -0
  319. package/lib/tui/components/Toast.jsx +52 -0
  320. package/lib/tui/hooks/useApi.js +44 -0
  321. package/lib/tui/hooks/useHealth.js +90 -0
  322. package/lib/tui/hooks/usePolledApi.js +86 -0
  323. package/lib/tui/hooks/useTerminalSize.js +46 -0
  324. package/lib/tui/index.js +56 -0
  325. package/lib/tui/intro/Intro.jsx +332 -0
  326. package/lib/tui/jsx-loader.js +32 -0
  327. package/lib/tui/keys.js +87 -0
  328. package/lib/tui/registry.js +49 -0
  329. package/lib/tui/theme.js +103 -0
  330. package/lib/tui/views/AdminView.jsx +398 -0
  331. package/lib/tui/views/AgentView.jsx +204 -0
  332. package/lib/tui/views/BackupView.jsx +225 -0
  333. package/lib/tui/views/BrainView.jsx +223 -0
  334. package/lib/tui/views/ChatView.jsx +468 -0
  335. package/lib/tui/views/FilesView.jsx +218 -0
  336. package/lib/tui/views/FleetView.jsx +234 -0
  337. package/lib/tui/views/GovernanceView.jsx +453 -0
  338. package/lib/tui/views/HealthView.jsx +216 -0
  339. package/lib/tui/views/InboxView.jsx +228 -0
  340. package/lib/tui/views/LucidDeck.jsx +727 -0
  341. package/lib/tui/views/MainMenu.jsx +123 -0
  342. package/lib/tui/views/MemoryView.jsx +377 -0
  343. package/lib/tui/views/ObjectivesView.jsx +230 -0
  344. package/lib/tui/views/OrgView.jsx +229 -0
  345. package/lib/tui/views/ToolView.jsx +221 -0
  346. package/lib/tui/views/VaultView.jsx +422 -0
  347. package/lib/tui/views/WorkflowView.jsx +329 -0
  348. package/package.json +72 -4
@@ -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,89 @@
1
+ /**
2
+ * Service health commands
3
+ */
4
+
5
+ import chalk from 'chalk';
6
+ import { formatOutput, printInfo } from '../core/output.js';
7
+ import { SERVICE_PORTS } from '../core/config.js';
8
+
9
+ async function runHealthCheck(name, ctx, program) {
10
+ const { config } = ctx;
11
+
12
+ const services = name
13
+ ? { [name]: SERVICE_PORTS[name] }
14
+ : SERVICE_PORTS;
15
+
16
+ if (name && !SERVICE_PORTS[name]) {
17
+ console.error(chalk.red(`Unknown service: ${name}`));
18
+ console.error(`Available: ${Object.keys(SERVICE_PORTS).join(', ')}`);
19
+ process.exit(2);
20
+ }
21
+
22
+ const results = [];
23
+
24
+ for (const [svc, port] of Object.entries(services)) {
25
+ const baseUrl = config.getServiceUrl(svc);
26
+ const start = Date.now();
27
+ let status = 'down';
28
+ let responseTime = '-';
29
+
30
+ try {
31
+ const controller = new AbortController();
32
+ const timer = setTimeout(() => controller.abort(), 5000);
33
+ const res = await fetch(`${baseUrl}/health`, { signal: controller.signal });
34
+ clearTimeout(timer);
35
+ const elapsed = Date.now() - start;
36
+ responseTime = `${elapsed}ms`;
37
+
38
+ if (res.ok) {
39
+ status = 'operational';
40
+ } else {
41
+ status = 'degraded';
42
+ }
43
+ } catch (err) {
44
+ if (err.name === 'AbortError') {
45
+ status = 'timeout';
46
+ }
47
+ // else stays 'down'
48
+ }
49
+
50
+ results.push({
51
+ service: svc,
52
+ port,
53
+ status,
54
+ response: responseTime,
55
+ });
56
+ }
57
+
58
+ formatOutput({
59
+ columns: ['service', 'port', 'status', 'response'],
60
+ headers: ['Service', 'Port', 'Status', 'Response'],
61
+ rows: results,
62
+ }, { format: program.opts().output });
63
+
64
+ const downCount = results.filter(r => r.status === 'down' || r.status === 'timeout').length;
65
+ if (downCount > 0) {
66
+ printInfo(`\n${downCount} service(s) unreachable.`);
67
+ }
68
+ }
69
+
70
+ export function registerServiceCommands(program, ctx) {
71
+ const service = program
72
+ .command('service')
73
+ .description('Check service health and status');
74
+
75
+ service
76
+ .command('health')
77
+ .description('Check health of all QuoxCORE services')
78
+ .argument('[name]', 'specific service to check (auth, collector, memory, files, orchestrator)')
79
+ .action(async (name) => runHealthCheck(name, ctx, program));
80
+
81
+ // Top-level alias — `quox health` is what deploy scripts / muscle memory
82
+ // reach for first; keep it a thin wrapper over `service health` so there
83
+ // is exactly one health-check implementation.
84
+ program
85
+ .command('health')
86
+ .description('Check health of all QuoxCORE services (alias for `service health`)')
87
+ .argument('[name]', 'specific service to check (auth, collector, memory, files, orchestrator)')
88
+ .action(async (name) => runHealthCheck(name, ctx, program));
89
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Org platform settings (feature flags) commands.
3
+ *
4
+ * quox settings list|get|set|history
5
+ *
6
+ * Backend: services/collector/server.js
7
+ * GET /api/v1/platform/settings/flags effective flags for the caller's org
8
+ * PATCH /api/v1/platform/settings/flags/:key { value } (human org admin)
9
+ * GET /api/v1/platform/settings/flags/history change audit trail (admin)
10
+ */
11
+
12
+ import { formatOutput, formatDetail } from '../core/output.js';
13
+ import { line as glyphLine, printEvent } from '../core/quoxglyph.js';
14
+ import { resolveEndpoint } from '../api/endpoints.js';
15
+
16
+ async function fetchFlags(client) {
17
+ const ep = resolveEndpoint('v1.settings.list');
18
+ const data = await client.get(ep.path, { service: ep.service });
19
+ return data.flags || [];
20
+ }
21
+
22
+ export function registerSettingsCommands(program, ctx) {
23
+ const { client, auth } = ctx;
24
+
25
+ const settings = program
26
+ .command('settings')
27
+ .description('View and change org platform settings (feature flags)');
28
+
29
+ settings
30
+ .command('list')
31
+ .description('List effective platform settings for the active org')
32
+ .action(async () => {
33
+ auth.requireAuth();
34
+ const flags = await fetchFlags(client);
35
+ formatOutput(
36
+ {
37
+ columns: ['key', 'value', 'source'],
38
+ headers: ['KEY', 'VALUE', 'SOURCE'],
39
+ rows: flags.map((f) => ({ key: f.key, value: f.value, source: f.source })),
40
+ },
41
+ { format: program.opts().output, raw: flags, emptyMessage: 'No settings.' }
42
+ );
43
+ });
44
+
45
+ settings
46
+ .command('get <key>')
47
+ .description('Show one platform setting')
48
+ .action(async (key) => {
49
+ auth.requireAuth();
50
+ const flags = await fetchFlags(client);
51
+ const flag = flags.find((f) => f.key === key);
52
+ if (!flag) throw new Error(`Unknown setting: ${key}. Run "quox settings list" for valid keys.`);
53
+ formatDetail(flag, { format: program.opts().output, title: key });
54
+ });
55
+
56
+ settings
57
+ .command('set <key> <value>')
58
+ .description('Change a platform setting for the active org (human org admin)')
59
+ .action(async (key, value) => {
60
+ auth.requireAuth();
61
+ const ep = resolveEndpoint('v1.settings.set', { key });
62
+ const data = await client.patch(ep.path, { service: ep.service, body: { value } });
63
+ if (program.opts().output === 'json') {
64
+ console.log(JSON.stringify(data, null, 2));
65
+ return;
66
+ }
67
+ printEvent(glyphLine('quox', `setting ${key}`, {
68
+ result: `${data.old_value ?? '(unset)'} -> ${data.new_value}`,
69
+ status: 'ok',
70
+ }));
71
+ });
72
+
73
+ settings
74
+ .command('history')
75
+ .description('Show the platform settings change history (admin)')
76
+ .option('--key <key>', 'filter to one setting')
77
+ .option('--limit <n>', 'max rows', '50')
78
+ .action(async (options) => {
79
+ auth.requireAuth();
80
+ const ep = resolveEndpoint('v1.settings.history');
81
+ const query = { limit: options.limit };
82
+ if (options.key) query.key = options.key;
83
+ const data = await client.get(ep.path, { service: ep.service, query });
84
+ const history = data.history || [];
85
+ formatOutput(
86
+ {
87
+ columns: ['when', 'key', 'change', 'by'],
88
+ headers: ['WHEN', 'KEY', 'CHANGE', 'BY'],
89
+ rows: history.map((h) => ({
90
+ when: h.created_at,
91
+ key: h.key,
92
+ change: `${h.old_value ?? '(unset)'} -> ${h.new_value}`,
93
+ by: h.actor_label || h.actor_id || '',
94
+ })),
95
+ },
96
+ { format: program.opts().output, raw: history, emptyMessage: 'No history.' }
97
+ );
98
+ });
99
+ }