quoxcli 0.4.0 → 0.5.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 (175) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +2 -2
  3. package/bin/quox.js +19 -1
  4. package/bin/quoxon.js +6 -1
  5. package/lib/api/endpoints/admin.js +7 -0
  6. package/lib/api/endpoints/agent-attach.js +7 -0
  7. package/lib/api/endpoints/compliance.js +25 -0
  8. package/lib/api/endpoints/inbox.js +6 -0
  9. package/lib/api/endpoints/inboxq.js +19 -0
  10. package/lib/api/endpoints/index.js +2 -0
  11. package/lib/api/endpoints/mon.js +122 -0
  12. package/lib/api/endpoints/tools.js +13 -0
  13. package/lib/api/endpoints/v1.js +13 -0
  14. package/lib/commands/admin.js +35 -0
  15. package/lib/commands/agentAttach.js +15 -0
  16. package/lib/commands/approvals.js +193 -3
  17. package/lib/commands/ask.js +1 -1
  18. package/lib/commands/auth.js +64 -11
  19. package/lib/commands/backup.js +11 -4
  20. package/lib/commands/capabilities.js +3 -2
  21. package/lib/commands/chat.js +15 -0
  22. package/lib/commands/claude.js +4 -1
  23. package/lib/commands/compliance.js +109 -0
  24. package/lib/commands/connect.js +69 -0
  25. package/lib/commands/dev.js +6 -8
  26. package/lib/commands/doctor.js +22 -3
  27. package/lib/commands/evidence.js +2 -1
  28. package/lib/commands/gripe.js +24 -13
  29. package/lib/commands/inbox.js +37 -3
  30. package/lib/commands/inboxq.js +147 -0
  31. package/lib/commands/kuma.js +78 -5
  32. package/lib/commands/licenceLinkView.js +311 -0
  33. package/lib/commands/linkedin.js +19 -0
  34. package/lib/commands/loop.js +30 -3
  35. package/lib/commands/lucid.js +1 -0
  36. package/lib/commands/mail.js +161 -20
  37. package/lib/commands/memory.js +68 -4
  38. package/lib/commands/memorySync.js +158 -0
  39. package/lib/commands/mon.js +55 -144
  40. package/lib/commands/notes.js +29 -7
  41. package/lib/commands/notesSeal.js +259 -0
  42. package/lib/commands/objective.js +5 -2
  43. package/lib/commands/offload.js +2 -3
  44. package/lib/commands/plan.js +17 -0
  45. package/lib/commands/provider.js +29 -8
  46. package/lib/commands/qlarity.js +32 -1
  47. package/lib/commands/run.js +2 -1
  48. package/lib/commands/server.js +37 -0
  49. package/lib/commands/setup.js +127 -75
  50. package/lib/commands/skills.js +4 -1
  51. package/lib/commands/slot.js +49 -9
  52. package/lib/commands/status.js +92 -44
  53. package/lib/commands/tool.js +30 -0
  54. package/lib/commands/vault.js +6 -3
  55. package/lib/commands/walkthrough.js +14 -2
  56. package/lib/commands/watch.js +11 -2
  57. package/lib/commands/wazuh.js +824 -0
  58. package/lib/commands/wazuhIntake.js +317 -0
  59. package/lib/commands/wazuhReport.js +192 -0
  60. package/lib/commands/workflow.js +47 -4
  61. package/lib/commands/x.js +50 -0
  62. package/lib/core/announce.js +86 -0
  63. package/lib/core/auth.js +20 -2
  64. package/lib/core/callMe.js +118 -0
  65. package/lib/core/client.js +27 -1
  66. package/lib/core/commandRegistrars.js +8 -0
  67. package/lib/core/config.js +28 -2
  68. package/lib/core/confirm.js +68 -7
  69. package/lib/core/crossOrgApprovals.js +69 -0
  70. package/lib/core/errors.js +52 -1
  71. package/lib/core/governedSession.js +135 -5
  72. package/lib/core/inboxResolution.js +3 -2
  73. package/lib/core/loginGuard.js +73 -0
  74. package/lib/core/offload.js +37 -0
  75. package/lib/core/org-override.js +5 -1
  76. package/lib/core/output.js +1 -1
  77. package/lib/core/profileTrust.js +182 -0
  78. package/lib/core/quoxglyph.js +22 -1
  79. package/lib/core/router.js +14 -0
  80. package/lib/core/stepUpPrompt.js +99 -0
  81. package/lib/core/version.js +1 -1
  82. package/lib/core/walkthrough.js +9 -5
  83. package/lib/dragon/select.js +2 -2
  84. package/lib/quoxon/EXTENSIONS.md +142 -19
  85. package/lib/quoxon/api.js +185 -34
  86. package/lib/quoxon/app.js +1032 -152
  87. package/lib/quoxon/approvalBatch.js +117 -0
  88. package/lib/quoxon/approvalFacts.js +56 -2
  89. package/lib/quoxon/approvalSentence.js +8 -4
  90. package/lib/quoxon/approvalsView.js +69 -11
  91. package/lib/quoxon/autoOpen.js +94 -0
  92. package/lib/quoxon/backgroundWork.js +59 -0
  93. package/lib/quoxon/bang.js +1 -1
  94. package/lib/quoxon/capabilities.js +7 -2
  95. package/lib/quoxon/commandShort.js +7 -0
  96. package/lib/quoxon/commands.js +40 -10
  97. package/lib/quoxon/connect.js +5 -3
  98. package/lib/quoxon/deckLazy.js +58 -0
  99. package/lib/quoxon/destructive.js +35 -0
  100. package/lib/quoxon/dnd.js +71 -0
  101. package/lib/quoxon/dragonCompanion.js +37 -7
  102. package/lib/quoxon/dragonMoments.js +78 -0
  103. package/lib/quoxon/echo.js +1 -0
  104. package/lib/quoxon/events.js +43 -7
  105. package/lib/quoxon/extensions.js +398 -38
  106. package/lib/quoxon/gripeQueue.js +135 -0
  107. package/lib/quoxon/inboxView.js +125 -22
  108. package/lib/quoxon/keys.js +2 -2
  109. package/lib/quoxon/loopCard.js +5 -1
  110. package/lib/quoxon/loopCards.js +17 -8
  111. package/lib/quoxon/loopStream.js +20 -9
  112. package/lib/quoxon/mailView.js +71 -0
  113. package/lib/quoxon/main.js +20 -5
  114. package/lib/quoxon/markdown.js +1 -1
  115. package/lib/quoxon/model.js +15 -0
  116. package/lib/quoxon/neverAnswered.js +20 -0
  117. package/lib/quoxon/objects/away.js +43 -5
  118. package/lib/quoxon/onboarding/connectServer.js +162 -0
  119. package/lib/quoxon/onboarding/copy.js +6 -0
  120. package/lib/quoxon/onboarding/index.js +5 -43
  121. package/lib/quoxon/onboarding/probe.js +20 -1
  122. package/lib/quoxon/onboarding/view.js +16 -3
  123. package/lib/quoxon/orgSwitch.js +22 -0
  124. package/lib/quoxon/print.js +2 -0
  125. package/lib/quoxon/profileSwitch.js +7 -0
  126. package/lib/quoxon/promptHistory.js +3 -1
  127. package/lib/quoxon/render.js +63 -14
  128. package/lib/quoxon/roster.js +10 -5
  129. package/lib/quoxon/rosterSessions.js +44 -0
  130. package/lib/quoxon/screenContext.js +1 -1
  131. package/lib/quoxon/secretShape.js +86 -0
  132. package/lib/quoxon/slash/agents.js +2 -1
  133. package/lib/quoxon/slash/argSources.js +9 -2
  134. package/lib/quoxon/slash/args.js +4 -2
  135. package/lib/quoxon/slash/broadcast.js +18 -5
  136. package/lib/quoxon/slash/broadcastRun.js +13 -2
  137. package/lib/quoxon/slash/cliLeaves.json +63 -1
  138. package/lib/quoxon/slash/hatWalk.js +1 -1
  139. package/lib/quoxon/slash/palette.js +22 -1
  140. package/lib/quoxon/slash/paletteController.js +10 -4
  141. package/lib/quoxon/slash/restart.js +7 -4
  142. package/lib/quoxon/slash/stepUp.js +5 -5
  143. package/lib/quoxon/slash/stepUpCmds.js +74 -1
  144. package/lib/quoxon/slashSpinner.js +35 -0
  145. package/lib/quoxon/slotAttach.js +8 -0
  146. package/lib/quoxon/slotChat.js +17 -0
  147. package/lib/quoxon/slotFormat.js +1 -1
  148. package/lib/quoxon/slotOutbox.js +113 -0
  149. package/lib/quoxon/suggestCommand.js +1 -1
  150. package/lib/quoxon/term.js +25 -1
  151. package/lib/quoxon/transcript.js +2 -1
  152. package/lib/quoxon/welcome.js +1 -1
  153. package/lib/quoxon/whatsnew.js +1 -1
  154. package/lib/quoxon/whatsnew.jsonl +36 -0
  155. package/lib/server/installFlow.js +168 -0
  156. package/lib/server/status.js +15 -1
  157. package/lib/toolOutputShape.js +55 -0
  158. package/package.json +3 -2
  159. package/vendor/dragonlab/SOURCE +3 -3
  160. package/vendor/dragonlab/design/quoxperticus/v2/quoxperticus-master.svg +59 -0
  161. package/vendor/dragonlab/design/quoxperticus/v3/character.mjs +43 -0
  162. package/vendor/dragonlab/design/quoxperticus/v4/character.mjs +57 -0
  163. package/vendor/dragonlab/design/quoxperticus/v4/review.mjs +35 -0
  164. package/vendor/dragonlab/design/quoxperticus/v4/watch.mjs +56 -0
  165. package/vendor/dragonlab/design/vulcan/v1/character.mjs +34 -0
  166. package/vendor/dragonlab/design/vulcan/v1/vulcan-master.svg +57 -0
  167. package/vendor/dragonlab/design/vulcan/v2/character.mjs +39 -0
  168. package/vendor/dragonlab/design/vulcan/v3/character.mjs +41 -0
  169. package/vendor/dragonlab/design/vulcan/v3/review.mjs +35 -0
  170. package/vendor/dragonlab/design/vulcan/v3/watch.mjs +56 -0
  171. package/vendor/dragonlab/factory/svg-rig-contour.mjs +64 -0
  172. package/vendor/dragonlab/integration/companions-v1/characters.mjs +20 -0
  173. package/vendor/dragonlab/integration/companions-v1/quoxperticus.mjs +2 -0
  174. package/vendor/dragonlab/integration/companions-v1/view.mjs +1 -1
  175. package/vendor/dragonlab/integration/companions-v1/vulcan.mjs +2 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,61 @@
1
+ # Changelog
2
+
3
+ User-visible changes to the `quoxcli` package. Items marked *(unproven)* ship in
4
+ the package and pass their tests, but nobody has run them end to end from the
5
+ published package yet.
6
+
7
+ ## 0.5.0 (unreleased)
8
+
9
+ ### New commands
10
+ - `quox wazuh`: first-class Wazuh commands. `agents`, `fim`, `sca`, `rootcheck`
11
+ and `coverage` are reads. `vulns --priority` ranks CVEs by CISA KEV, EPSS,
12
+ CVSS and how many hosts each one hits (driven live across the fleet before
13
+ this release). Fleet-wide reads cover up to 200 active agents (it was a
14
+ silent 25 before).
15
+ - `quox wazuh report generate | list | show | verify`: signed vulnerability
16
+ reports. `verify` works offline against a pinned PEM or a raw key.
17
+ *(unproven from the package)*
18
+ - `quox wazuh connect`, `quox wazuh hooks create | list | rotate | delete | install`,
19
+ `quox wazuh incidents [--open] [--since] | show | close | diagnose | escalate`
20
+ and `quox wazuh triage`. Writes go through one approval per call.
21
+ *(unproven from the package)*
22
+ - `quox compliance reports list | show` (read): framework, period, score,
23
+ status per control and sampled evidence ids. `quox compliance jobs list`, and
24
+ jobs can now be named. *(unproven from the package)*
25
+ - `quox tool installed` and `quox tool uninstall`, to read back and undo a
26
+ `quox tool install`. *(unproven from the package)*
27
+ - `quox approvals approve | deny | list` by id or short tail, `quox approvals withdraw`
28
+ (the requester takes its own hold back), and `quox approvals call-me [status|on|off]`
29
+ (needs the quox-voice licence).
30
+ - `quox connect <url>` with certificate pinning per profile (`--trust-cert`,
31
+ `--fingerprint`), and `quox server install` (with `--dry-run`, `--yes`, `--json`).
32
+ - `quox notes seal init | status | new | open | edit | print`: ENQRYPT sealed
33
+ notes. Encryption happens on your machine and the server stores only the
34
+ envelope. *(unproven from the package)*
35
+ - `quox admin users verify-email <email> --reason` (step-up PIN).
36
+ - `quox memory sync`: Claude file memory is saved to Quox memory too
37
+ (idempotent; credential-shaped text is refused).
38
+ - `quox inbox unread <id...>`, `quox x whoami | accounts`, `quox linkedin whoami`,
39
+ `quox workflow create | apply | import-n8n --activate` (a plain create is not armed).
40
+
41
+ ### Terminal UI (quoxon)
42
+ - New approvals and questions open by themselves as the walkthrough card.
43
+ - Approve verbs ask for the step-up PIN on a terminal; destructive approvals
44
+ need the target name typed.
45
+ - Do not disturb and `/sleep` survive restarts; `/morningreport` gives a grouped
46
+ digest of what arrived while you were away.
47
+ - `/mail` reads triaged email (read-only), `/outbox` queues `/say` sends that
48
+ failed on connectivity and sends them in order once you are back online.
49
+ - Other-org approvals show on the bar, with a hint about how many more are waiting.
50
+ - A secret paste guard asks before it sends text that looks like a credential,
51
+ and redacts it from history.
52
+ - Extensions hot-reload, and extension host contract v1 is out (panels, tabs,
53
+ settings).
54
+
55
+ ### Fixes
56
+ - An explicit `-p <profile>` with no session now fails, instead of quietly
57
+ using the service key.
58
+ - `quox workflow update` finds engine workflows. `quox run list` fills in its
59
+ columns again.
60
+ - Watch and loop streams back off and give up cleanly, instead of polling forever.
61
+ - Docs links point at quox.ai/cli.
package/README.md CHANGED
@@ -12,7 +12,7 @@ npm install -g quoxcli
12
12
 
13
13
  Needs Node.js 20 or later. This installs two commands: `quox`, and `quoxon` (an alias that opens the conversation).
14
14
 
15
- Docs: https://cli.quox.ai/docs/start/
15
+ Docs: https://quox.ai/cli/docs/quickstart/
16
16
 
17
17
  ## Prerequisite: a running QuoxCORE
18
18
 
@@ -142,4 +142,4 @@ export NODE_EXTRA_CA_CERTS=/path/to/your-ca.pem
142
142
  Quox Community Source Licence 1.0. This is source-available software, not open source. You may self-host and inspect it freely; redistribution and repackaging are restricted. Read the full text in the `LICENSE` file or at https://github.com/quoxai/quoxcore.
143
143
 
144
144
  Website: https://quox.ai
145
- CLI home: https://cli.quox.ai
145
+ CLI home: https://quox.ai/cli
package/bin/quox.js CHANGED
@@ -11,6 +11,14 @@ if (!process.env.QUOX_SESSION_ID && process.env.QUOX_SESSION_ID_FILE) {
11
11
  try { process.env.QUOX_SESSION_ID = (await import('node:fs')).readFileSync(process.env.QUOX_SESSION_ID_FILE, 'utf8').trim(); } catch { /* turn ended: no session */ }
12
12
  }
13
13
 
14
+ // ONBOARD-P1: a profile that trusts its server's own certificate (quox connect) makes this run trust it too, via a
15
+ // one-time re-run with NODE_EXTRA_CA_CERTS set for this process tree only. Before anything else loads.
16
+ {
17
+ const { reexecWithProfileTrust } = await import('../lib/core/profileTrust.js');
18
+ const code = reexecWithProfileTrust(process.argv.slice(2), process.env);
19
+ if (code !== null) process.exit(code);
20
+ }
21
+
14
22
  // CORTEX-BENCH: Node's fetch parses HTTP with a WebAssembly module, and V8
15
23
  // recompiles it at the optimising tier on a background thread; Node waits for
16
24
  // that compile before exiting, adding 350-700ms to every short command after
@@ -167,7 +175,17 @@ if (process.argv.includes('--no-color') || process.env.NO_COLOR) {
167
175
  const config = loadConfig(resolveProfileFlagFromArgv(process.argv.slice(2)));
168
176
  const auth = createAuth(config);
169
177
  const client = createClient(config, auth);
170
- const ctx = { config, auth, client, program };
178
+ // A client on the SAME user's token for another org the user belongs to (minted per org, window scope, never moves the
179
+ // profile's org). Lazy: nothing is minted unless a verb asks (approvals --org, the "more waiting in <org>" hint).
180
+ let _orgClients = null;
181
+ const clientForOrg = async (orgId) => {
182
+ if (!_orgClients) {
183
+ const { createWindowClient } = await import('../lib/quoxon/windowOrg.js');
184
+ _orgClients = createWindowClient({ config, auth, createClient }).clientForOrg;
185
+ }
186
+ return _orgClients(orgId);
187
+ };
188
+ const ctx = { config, auth, client, program, clientForOrg };
171
189
 
172
190
  // Register all command groups
173
191
  // Command groups, in registration order. CORTEX-BENCH (quox#7xx CLI startup):
package/bin/quoxon.js CHANGED
@@ -1,6 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import '../lib/core/prodEnv.js'; // first: production React unless NODE_ENV is set (before anything loads React)
3
- import { main } from '../lib/quoxon/main.js';
3
+ {
4
+ const { reexecWithProfileTrust } = await import('../lib/core/profileTrust.js');
5
+ const code = reexecWithProfileTrust(process.argv.slice(2), process.env);
6
+ if (code !== null) process.exit(code);
7
+ }
8
+ const { main } = await import('../lib/quoxon/main.js');
4
9
 
5
10
  main().catch((err) => {
6
11
  console.error(err?.message || err);
@@ -17,6 +17,13 @@ export const admin = {
17
17
  path: () => '/api/admin/agent-identities',
18
18
  tags: ['admin-write'],
19
19
  },
20
+ // ADMIN-VERIFY-EMAIL: instance-admin + human + step-up (scope override). Served by services/auth/routes/admin.js.
21
+ 'admin.users.verify_email': {
22
+ method: 'POST',
23
+ service: 'auth',
24
+ path: () => '/api/admin/users/verify-email',
25
+ tags: ['admin-write'],
26
+ },
20
27
  // Instance basics after setup (app URL etc). Served by services/auth/routes/admin-instance.js.
21
28
  'admin.instance.basics.get': {
22
29
  method: 'GET',
@@ -27,6 +27,13 @@ export const agentAttach = {
27
27
  path: ({ leaseId }) => `/api/agent-attach/lease/${encodeURIComponent(leaseId)}/release`,
28
28
  tags: ['live-write'],
29
29
  },
30
+ // Org-wide list of live attaches (org owner/admin). Feeds `quox agent attaches` and the quoxon /roster.
31
+ 'agent-attach.list': {
32
+ method: 'GET',
33
+ service: 'auth',
34
+ path: () => '/api/agent-attach',
35
+ tags: ['live-safe'],
36
+ },
30
37
  'agent-attach.status': {
31
38
  method: 'GET',
32
39
  service: 'auth',
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 'compliance.*' endpoints: scheduled compliance report jobs and generated reports.
3
+ * See cli/lib/api/endpoints/index.js for aggregation + the fragment convention.
4
+ */
5
+
6
+ export const compliance = {
7
+ 'compliance.jobs.list': {
8
+ method: 'GET',
9
+ service: 'collector',
10
+ path: () => `/compliance/jobs`,
11
+ tags: ['live-safe'],
12
+ },
13
+ 'compliance.reports.list': {
14
+ method: 'GET',
15
+ service: 'collector',
16
+ path: () => `/compliance/reports`,
17
+ tags: ['live-safe'],
18
+ },
19
+ 'compliance.reports.get': {
20
+ method: 'GET',
21
+ service: 'collector',
22
+ path: ({ id }) => `/compliance/reports/${encodeURIComponent(id)}`,
23
+ tags: ['live-safe'],
24
+ },
25
+ };
@@ -30,6 +30,12 @@ export const inbox = {
30
30
  path: ({ orgId, itemId }) => `/api/orgs/${orgId}/inbox/${itemId}/respond`,
31
31
  tags: ['live-write'],
32
32
  },
33
+ 'inbox.withdraw': {
34
+ method: 'POST',
35
+ service: 'auth',
36
+ path: ({ orgId, itemId }) => `/api/orgs/${orgId}/inbox/${itemId}/withdraw`,
37
+ tags: ['live-write'],
38
+ },
33
39
  // quox ask: a session files a decision request (one inbox item) and waits for the human's answer.
34
40
  'inbox.decision.create': {
35
41
  method: 'POST',
@@ -26,6 +26,19 @@ export const inboxq = {
26
26
  path: ({ accountId }) => `/api/v1/inboxq/accounts/${accountId}`,
27
27
  tags: ['live-write'],
28
28
  },
29
+ // quox#1504: per-account TLS certificate pin (human org admin only).
30
+ 'inboxq.accounts.tlsPin': {
31
+ method: 'PUT',
32
+ service: 'collector',
33
+ path: ({ accountId }) => `/api/v1/inboxq/accounts/${encodeURIComponent(accountId)}/tls-pin`,
34
+ tags: ['live-write'],
35
+ },
36
+ 'inboxq.syncStatus': {
37
+ method: 'GET',
38
+ service: 'collector',
39
+ path: () => `/api/v1/inboxq/sync-status`,
40
+ tags: ['live-safe'],
41
+ },
29
42
  'inboxq.messages.get': {
30
43
  method: 'GET',
31
44
  service: 'collector',
@@ -99,6 +112,12 @@ export const inboxq = {
99
112
  path: ({ watchId }) => `/api/v1/inboxq/watches/${watchId}/active`,
100
113
  tags: ['live-write'],
101
114
  },
115
+ 'inboxq.watches.delete': {
116
+ method: 'DELETE',
117
+ service: 'collector',
118
+ path: ({ watchId }) => `/api/v1/inboxq/watches/${watchId}`,
119
+ tags: ['live-write'],
120
+ },
102
121
  'inboxq.proposals.list': {
103
122
  method: 'GET',
104
123
  service: 'collector',
@@ -74,6 +74,7 @@ import { budgets } from './budgets.js';
74
74
  import { calendarq } from './calendarq.js';
75
75
  import { catalog } from './catalog.js';
76
76
  import { chat } from './chat.js';
77
+ import { compliance } from './compliance.js';
77
78
  import { consent } from './consent.js';
78
79
  import { conversations } from './conversations.js';
79
80
  import { customRoles } from './custom-roles.js';
@@ -166,6 +167,7 @@ export const ENDPOINTS = {
166
167
  ...calendarq,
167
168
  ...catalog,
168
169
  ...chat,
170
+ ...compliance,
169
171
  ...consent,
170
172
  ...conversations,
171
173
  ...customRoles,
@@ -163,6 +163,12 @@ export const mon = {
163
163
  path: ({ id } = {}) => `/api/v1/kuma/monitors/${encodeURIComponent(id)}/resume`,
164
164
  tags: ['live-write'],
165
165
  },
166
+ 'mon.kuma.monitors.wire-alerts': {
167
+ method: 'POST',
168
+ service: 'collector',
169
+ path: ({ id } = {}) => `/api/v1/kuma/monitors/${encodeURIComponent(id)}/wire-alerts`,
170
+ tags: ['live-write'],
171
+ },
166
172
  'mon.kuma.monitors.delete': {
167
173
  method: 'DELETE',
168
174
  service: 'collector',
@@ -318,12 +324,36 @@ export const mon = {
318
324
  path: () => `/api/v1/wazuh/agents`,
319
325
  tags: ['live-safe'],
320
326
  },
327
+ 'mon.wazuh.read': {
328
+ method: 'GET',
329
+ service: 'collector',
330
+ path: ({ tool }) => `/api/v1/wazuh/read/${tool}`,
331
+ tags: ['live-safe'],
332
+ },
321
333
  'mon.wazuh.alerts.list': {
322
334
  method: 'GET',
323
335
  service: 'collector',
324
336
  path: () => `/api/v1/wazuh/alerts`,
325
337
  tags: ['live-safe'],
326
338
  },
339
+ 'mon.wazuh.alerts.triage': {
340
+ method: 'GET',
341
+ service: 'collector',
342
+ path: () => `/api/v1/wazuh/alerts/triage`,
343
+ tags: ['live-safe'],
344
+ },
345
+ 'mon.wazuh.coverage': {
346
+ method: 'GET',
347
+ service: 'collector',
348
+ path: () => `/api/v1/wazuh/coverage`,
349
+ tags: ['live-safe'],
350
+ },
351
+ 'mon.wazuh.credential.import-instance-env': {
352
+ method: 'POST',
353
+ service: 'collector',
354
+ path: () => `/api/v1/wazuh/credential/import-instance-env`,
355
+ tags: ['live-write'],
356
+ },
327
357
  'mon.wazuh.deploy-agent': {
328
358
  method: 'POST',
329
359
  service: 'collector',
@@ -348,6 +378,98 @@ export const mon = {
348
378
  path: () => `/api/v1/wazuh/manager/status`,
349
379
  tags: ['live-safe'],
350
380
  },
381
+ 'mon.wazuh.reports.create': {
382
+ method: 'POST',
383
+ service: 'collector',
384
+ path: () => `/api/v1/wazuh/reports`,
385
+ tags: ['live-safe'],
386
+ },
387
+ 'mon.wazuh.reports.list': {
388
+ method: 'GET',
389
+ service: 'collector',
390
+ path: () => `/api/v1/wazuh/reports`,
391
+ tags: ['live-safe'],
392
+ },
393
+ 'mon.wazuh.reports.get': {
394
+ method: 'GET',
395
+ service: 'collector',
396
+ path: ({ id } = {}) => `/api/v1/wazuh/reports/${encodeURIComponent(id)}`,
397
+ tags: ['live-safe'],
398
+ },
399
+ 'mon.wazuh.reports.verify': {
400
+ method: 'GET',
401
+ service: 'collector',
402
+ path: ({ id } = {}) => `/api/v1/wazuh/reports/${encodeURIComponent(id)}/verify`,
403
+ tags: ['live-safe'],
404
+ },
405
+ // WAZ P6: Wazuh integratord alert hooks (org admin) and the incidents they open.
406
+ 'mon.wazuh.hooks.list': {
407
+ method: 'GET',
408
+ service: 'collector',
409
+ path: () => `/api/v1/wazuh/hooks`,
410
+ tags: ['live-safe'],
411
+ },
412
+ 'mon.wazuh.hooks.create': {
413
+ method: 'POST',
414
+ service: 'collector',
415
+ path: () => `/api/v1/wazuh/hooks`,
416
+ tags: ['live-write'],
417
+ },
418
+ 'mon.wazuh.hooks.rotate': {
419
+ method: 'POST',
420
+ service: 'collector',
421
+ path: ({ id } = {}) => `/api/v1/wazuh/hooks/${encodeURIComponent(id)}/rotate`,
422
+ tags: ['live-write'],
423
+ },
424
+ 'mon.wazuh.hooks.delete': {
425
+ method: 'DELETE',
426
+ service: 'collector',
427
+ path: ({ id } = {}) => `/api/v1/wazuh/hooks/${encodeURIComponent(id)}`,
428
+ tags: ['live-write'],
429
+ },
430
+ 'mon.wazuh.incidents.list': {
431
+ method: 'GET',
432
+ service: 'collector',
433
+ path: ({ open, since, limit } = {}) => {
434
+ const q = [];
435
+ if (open) q.push('open=1');
436
+ if (since) q.push(`since=${encodeURIComponent(since)}`);
437
+ if (limit) q.push(`limit=${encodeURIComponent(limit)}`);
438
+ return `/api/v1/wazuh/incidents${q.length ? `?${q.join('&')}` : ''}`;
439
+ },
440
+ tags: ['live-safe'],
441
+ },
442
+ 'mon.wazuh.incidents.get': {
443
+ method: 'GET',
444
+ service: 'collector',
445
+ path: ({ id } = {}) => `/api/v1/wazuh/incidents/${encodeURIComponent(id)}`,
446
+ tags: ['live-safe'],
447
+ },
448
+ 'mon.wazuh.incidents.close': {
449
+ method: 'POST',
450
+ service: 'collector',
451
+ path: ({ id } = {}) => `/api/v1/wazuh/incidents/${encodeURIComponent(id)}/close`,
452
+ tags: ['live-write'],
453
+ },
454
+ // WAZ P7: read-only host diagnosis (off-list reads are held by the ssh_exec approval gate) and hand-off to QuoxSecurity.
455
+ 'mon.wazuh.incidents.diagnose': {
456
+ method: 'POST',
457
+ service: 'collector',
458
+ path: ({ id } = {}) => `/api/v1/wazuh/incidents/${encodeURIComponent(id)}/diagnose`,
459
+ tags: ['live-write'],
460
+ },
461
+ 'mon.wazuh.incidents.escalate': {
462
+ method: 'POST',
463
+ service: 'collector',
464
+ path: ({ id } = {}) => `/api/v1/wazuh/incidents/${encodeURIComponent(id)}/escalate`,
465
+ tags: ['live-write'],
466
+ },
467
+ 'mon.wazuh.vulnerabilities.priority': {
468
+ method: 'GET',
469
+ service: 'collector',
470
+ path: () => `/api/v1/wazuh/vulnerabilities/priority`,
471
+ tags: ['live-safe'],
472
+ },
351
473
  'mon.wazuh.vulnerabilities.list': {
352
474
  method: 'GET',
353
475
  service: 'collector',
@@ -29,6 +29,19 @@ export const tools = {
29
29
  path: () => `/api/tools/install-to-scope`,
30
30
  tags: ['live-write'],
31
31
  },
32
+ // CLI-COV 2026-11-05: read and undo what `tool install` wrote (tool_installations, not the org's tool definitions)
33
+ 'tools.installed': {
34
+ method: 'GET',
35
+ service: 'auth',
36
+ path: () => `/api/tools/installed`,
37
+ tags: ['live-safe'],
38
+ },
39
+ 'tools.uninstall-from-scope': {
40
+ method: 'DELETE',
41
+ service: 'auth',
42
+ path: ({ toolId }) => `/api/tools/install-to-scope/${encodeURIComponent(toolId)}`,
43
+ tags: ['live-write'],
44
+ },
32
45
  'tools.list': {
33
46
  method: 'GET',
34
47
  service: 'auth',
@@ -365,6 +365,19 @@ export const v1 = {
365
365
  path: ({ id }) => `/api/v1/approvals/grants/${encodeURIComponent(id)}/revoke`,
366
366
  tags: ['live-write'],
367
367
  },
368
+ // ApprovalsQ call-me policy (2026-11-03): human org admin only; flips the stale-approval phone watcher.
369
+ 'v1.approvals.call-me.get': {
370
+ method: 'GET',
371
+ service: 'collector',
372
+ path: () => `/api/v1/approvalsq/call-me`,
373
+ tags: ['live-safe'],
374
+ },
375
+ 'v1.approvals.call-me.put': {
376
+ method: 'PUT',
377
+ service: 'collector',
378
+ path: () => `/api/v1/approvalsq/call-me`,
379
+ tags: ['live-write'],
380
+ },
368
381
  'v1.tools.list': {
369
382
  method: 'GET',
370
383
  service: 'collector',
@@ -4,6 +4,9 @@
4
4
 
5
5
  import { formatOutput, formatDetail, printSuccess } from '../core/output.js';
6
6
  import { resolveEndpoint } from '../api/endpoints.js';
7
+ import { withStepUp } from '../core/stepUpPrompt.js';
8
+ import { announce, announceFailure } from '../core/announce.js';
9
+ import { resolveOrgId } from './helpers.js';
7
10
 
8
11
  export function registerAdminCommands(program, ctx) {
9
12
  const { client, auth } = ctx;
@@ -154,6 +157,38 @@ export function registerAdminCommands(program, ctx) {
154
157
  console.log(data.token);
155
158
  });
156
159
 
160
+ // ADMIN-VERIFY-EMAIL: accounts with no real mailbox (test/service accounts) can never click the
161
+ // emailed link, so they never see org invites. Instance-admin, human, step-up PIN (hidden prompt).
162
+ const users = admin
163
+ .command('users')
164
+ .description('Instance user administration');
165
+
166
+ users
167
+ .command('verify-email')
168
+ .description('Mark a user email verified (instance admin; asks for your step-up PIN; audited)')
169
+ .argument('<email>', 'the email address to mark verified')
170
+ .requiredOption('--reason <text>', 'why this address is verified without a mailbox (recorded in the audit log)')
171
+ .option('--user-id <id>', 'refuse unless the email belongs to this user id')
172
+ .action(async (email, opts) => {
173
+ auth.requireAuth();
174
+ const reason = String(opts.reason || '').trim();
175
+ if (!reason) throw new Error('--reason must not be empty');
176
+ const orgId = resolveOrgId(auth, program);
177
+ const ep = resolveEndpoint('admin.users.verify_email');
178
+ let data;
179
+ try {
180
+ data = await withStepUp(
181
+ (headers) => client.post(ep.path, { service: ep.service, headers, body: { email, reason, ...(opts.userId ? { user_id: opts.userId } : {}) } }),
182
+ { client, orgId, scope: 'override', io: ctx.stepUpIO, what: 'Verifying an email address' },
183
+ );
184
+ } catch (err) {
185
+ announceFailure('quox', `admin verify-email ${email}`, err);
186
+ throw err;
187
+ }
188
+ announce('quox', `admin verify-email ${email}`, { result: `verified · user ${data.user_id}` });
189
+ printSuccess(`Email ${data.email} is now verified. Reason recorded in the audit log.`);
190
+ });
191
+
157
192
  // Email / SMTP (QCPRO-SMTP): instance email onboarding parity
158
193
  const email = admin
159
194
  .command('email')
@@ -1322,6 +1322,21 @@ export function registerAgentAttachCommands(agent, ctx, program) {
1322
1322
  if (program.opts().output === 'json') formatDetail(data, { format: 'json' });
1323
1323
  });
1324
1324
 
1325
+ agent
1326
+ .command('attaches')
1327
+ .description('List every live agent attach in the org (agent, splinter, session, lease, heartbeat, expiry)')
1328
+ .action(async () => {
1329
+ auth.requireAuth();
1330
+ const ep = resolveEndpoint('agent-attach.list');
1331
+ const data = await client.get(ep.path, { service: ep.service });
1332
+ const rows = Array.isArray(data?.attaches) ? data.attaches : [];
1333
+ if (program.opts().output === 'json') return formatDetail(data, { format: 'json' });
1334
+ if (!rows.length) return printInfo('No live agent attaches in this org.');
1335
+ for (const a of rows) {
1336
+ printInfo(`${a.agent_id} ${a.splinter || 'S1'} · ${a.session_label || '(no label)'} · lease ${a.lease_id} · heartbeat ${fmtAge(a.last_heartbeat_at)} · expires ${a.expires_at || '?'}`);
1337
+ }
1338
+ });
1339
+
1325
1340
  agent
1326
1341
  .command('attach-heartbeat')
1327
1342
  .description("Heartbeat this session's attach lease, rotating the stored token if one is issued")