@oxygen-agent/cli 1.1014.135 → 1.1014.618

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 (292) hide show
  1. package/README.md +1 -1
  2. package/dist/admin-primary-providers-render.js +27 -19
  3. package/dist/cli-output.d.ts +12 -0
  4. package/dist/cli-output.js +395 -0
  5. package/dist/cli-paths.d.ts +1 -0
  6. package/dist/cli-paths.js +3 -0
  7. package/dist/cli-profile-context.d.ts +3 -0
  8. package/dist/cli-profile-context.js +13 -0
  9. package/dist/command-manifest.d.ts +15 -0
  10. package/dist/command-manifest.js +23 -1
  11. package/dist/command-support.d.ts +438 -0
  12. package/dist/command-support.js +1610 -0
  13. package/dist/commands/action-column.d.ts +3 -0
  14. package/dist/commands/action-column.js +43 -0
  15. package/dist/commands/activation.d.ts +3 -0
  16. package/dist/commands/activation.js +24 -0
  17. package/dist/commands/admin.d.ts +3 -0
  18. package/dist/commands/admin.js +347 -0
  19. package/dist/commands/ads.d.ts +3 -0
  20. package/dist/commands/ads.js +62 -0
  21. package/dist/commands/agent.d.ts +3 -0
  22. package/dist/commands/agent.js +274 -0
  23. package/dist/commands/api-keys.d.ts +3 -0
  24. package/dist/commands/api-keys.js +77 -0
  25. package/dist/commands/approvals.d.ts +3 -0
  26. package/dist/commands/approvals.js +518 -0
  27. package/dist/commands/auth.d.ts +3 -0
  28. package/dist/commands/auth.js +230 -0
  29. package/dist/commands/billing.d.ts +3 -0
  30. package/dist/commands/billing.js +357 -0
  31. package/dist/commands/blueprints.d.ts +3 -0
  32. package/dist/commands/blueprints.js +312 -0
  33. package/dist/commands/budget.d.ts +3 -0
  34. package/dist/commands/budget.js +55 -0
  35. package/dist/commands/callables.d.ts +3 -0
  36. package/dist/commands/callables.js +111 -0
  37. package/dist/commands/capabilities.d.ts +3 -0
  38. package/dist/commands/capabilities.js +44 -0
  39. package/dist/commands/cells.d.ts +3 -0
  40. package/dist/commands/cells.js +94 -0
  41. package/dist/commands/columns.d.ts +3 -0
  42. package/dist/commands/columns.js +1628 -0
  43. package/dist/commands/commands.d.ts +3 -0
  44. package/dist/commands/commands.js +45 -0
  45. package/dist/commands/comments.d.ts +3 -0
  46. package/dist/commands/comments.js +188 -0
  47. package/dist/commands/companies.d.ts +3 -0
  48. package/dist/commands/companies.js +312 -0
  49. package/dist/commands/connections.d.ts +3 -0
  50. package/dist/commands/connections.js +39 -0
  51. package/dist/commands/context.d.ts +3 -0
  52. package/dist/commands/context.js +170 -0
  53. package/dist/commands/copilot.d.ts +3 -0
  54. package/dist/commands/copilot.js +565 -0
  55. package/dist/commands/crm.d.ts +3 -0
  56. package/dist/commands/crm.js +1669 -0
  57. package/dist/commands/custom-integrations.d.ts +3 -0
  58. package/dist/commands/custom-integrations.js +140 -0
  59. package/dist/commands/dashboards.d.ts +3 -0
  60. package/dist/commands/dashboards.js +240 -0
  61. package/dist/commands/db.d.ts +3 -0
  62. package/dist/commands/db.js +190 -0
  63. package/dist/commands/deliverability.d.ts +3 -0
  64. package/dist/commands/deliverability.js +90 -0
  65. package/dist/commands/directory.d.ts +3 -0
  66. package/dist/commands/directory.js +101 -0
  67. package/dist/commands/domains.d.ts +3 -0
  68. package/dist/commands/domains.js +604 -0
  69. package/dist/commands/egress.d.ts +3 -0
  70. package/dist/commands/egress.js +130 -0
  71. package/dist/commands/email.d.ts +3 -0
  72. package/dist/commands/email.js +49 -0
  73. package/dist/commands/engagement.d.ts +3 -0
  74. package/dist/commands/engagement.js +189 -0
  75. package/dist/commands/enrich-column.d.ts +3 -0
  76. package/dist/commands/enrich-column.js +149 -0
  77. package/dist/commands/enrichment.d.ts +3 -0
  78. package/dist/commands/enrichment.js +49 -0
  79. package/dist/commands/feedback.d.ts +3 -0
  80. package/dist/commands/feedback.js +96 -0
  81. package/dist/commands/feeds.d.ts +3 -0
  82. package/dist/commands/feeds.js +486 -0
  83. package/dist/commands/find.d.ts +3 -0
  84. package/dist/commands/find.js +159 -0
  85. package/dist/commands/followers.d.ts +3 -0
  86. package/dist/commands/followers.js +39 -0
  87. package/dist/commands/formulas.d.ts +3 -0
  88. package/dist/commands/formulas.js +43 -0
  89. package/dist/commands/home.d.ts +3 -0
  90. package/dist/commands/home.js +22 -0
  91. package/dist/commands/inbox.d.ts +3 -0
  92. package/dist/commands/inbox.js +779 -0
  93. package/dist/commands/integrations.d.ts +3 -0
  94. package/dist/commands/integrations.js +290 -0
  95. package/dist/commands/knowledge.d.ts +3 -0
  96. package/dist/commands/knowledge.js +874 -0
  97. package/dist/commands/lead-sourcing.d.ts +3 -0
  98. package/dist/commands/lead-sourcing.js +52 -0
  99. package/dist/commands/limits.d.ts +3 -0
  100. package/dist/commands/limits.js +54 -0
  101. package/dist/commands/linkedin.d.ts +3 -0
  102. package/dist/commands/linkedin.js +275 -0
  103. package/dist/commands/login.d.ts +3 -0
  104. package/dist/commands/login.js +347 -0
  105. package/dist/commands/logout.d.ts +3 -0
  106. package/dist/commands/logout.js +12 -0
  107. package/dist/commands/mailboxes.d.ts +3 -0
  108. package/dist/commands/mailboxes.js +1290 -0
  109. package/dist/commands/managed-inboxes.d.ts +3 -0
  110. package/dist/commands/managed-inboxes.js +361 -0
  111. package/dist/commands/messages.d.ts +3 -0
  112. package/dist/commands/messages.js +78 -0
  113. package/dist/commands/notetaker.d.ts +3 -0
  114. package/dist/commands/notetaker.js +79 -0
  115. package/dist/commands/observability.d.ts +3 -0
  116. package/dist/commands/observability.js +125 -0
  117. package/dist/commands/orgs.d.ts +3 -0
  118. package/dist/commands/orgs.js +237 -0
  119. package/dist/commands/people.d.ts +3 -0
  120. package/dist/commands/people.js +353 -0
  121. package/dist/commands/posts.d.ts +3 -0
  122. package/dist/commands/posts.js +139 -0
  123. package/dist/commands/profiles.d.ts +3 -0
  124. package/dist/commands/profiles.js +250 -0
  125. package/dist/commands/projects.d.ts +3 -0
  126. package/dist/commands/projects.js +94 -0
  127. package/dist/commands/prompts.d.ts +3 -0
  128. package/dist/commands/prompts.js +84 -0
  129. package/dist/commands/publishing.d.ts +3 -0
  130. package/dist/commands/publishing.js +1419 -0
  131. package/dist/commands/recipes.d.ts +3 -0
  132. package/dist/commands/recipes.js +120 -0
  133. package/dist/commands/reviews.d.ts +3 -0
  134. package/dist/commands/reviews.js +75 -0
  135. package/dist/commands/rows.d.ts +3 -0
  136. package/dist/commands/rows.js +27 -0
  137. package/dist/commands/runs.d.ts +3 -0
  138. package/dist/commands/runs.js +37 -0
  139. package/dist/commands/search.d.ts +3 -0
  140. package/dist/commands/search.js +182 -0
  141. package/dist/commands/senders.d.ts +3 -0
  142. package/dist/commands/senders.js +393 -0
  143. package/dist/commands/sequences.d.ts +3 -0
  144. package/dist/commands/sequences.js +2322 -0
  145. package/dist/commands/session.d.ts +3 -0
  146. package/dist/commands/session.js +109 -0
  147. package/dist/commands/signals.d.ts +3 -0
  148. package/dist/commands/signals.js +289 -0
  149. package/dist/commands/signup-leads.d.ts +3 -0
  150. package/dist/commands/signup-leads.js +46 -0
  151. package/dist/commands/skills.d.ts +3 -0
  152. package/dist/commands/skills.js +50 -0
  153. package/dist/commands/sourcing.d.ts +3 -0
  154. package/dist/commands/sourcing.js +27 -0
  155. package/dist/commands/status.d.ts +3 -0
  156. package/dist/commands/status.js +51 -0
  157. package/dist/commands/supabase.d.ts +3 -0
  158. package/dist/commands/supabase.js +128 -0
  159. package/dist/commands/support.d.ts +3 -0
  160. package/dist/commands/support.js +651 -0
  161. package/dist/commands/suppressions.d.ts +3 -0
  162. package/dist/commands/suppressions.js +577 -0
  163. package/dist/commands/table-ingestions.d.ts +3 -0
  164. package/dist/commands/table-ingestions.js +145 -0
  165. package/dist/commands/table-runs.d.ts +3 -0
  166. package/dist/commands/table-runs.js +277 -0
  167. package/dist/commands/tables.d.ts +3 -0
  168. package/dist/commands/tables.js +3492 -0
  169. package/dist/commands/tags.d.ts +3 -0
  170. package/dist/commands/tags.js +147 -0
  171. package/dist/commands/tools.d.ts +3 -0
  172. package/dist/commands/tools.js +246 -0
  173. package/dist/commands/update.d.ts +3 -0
  174. package/dist/commands/update.js +106 -0
  175. package/dist/commands/verify.d.ts +3 -0
  176. package/dist/commands/verify.js +27 -0
  177. package/dist/commands/viewers.d.ts +3 -0
  178. package/dist/commands/viewers.js +72 -0
  179. package/dist/commands/voice.d.ts +3 -0
  180. package/dist/commands/voice.js +351 -0
  181. package/dist/commands/whatsapp.d.ts +3 -0
  182. package/dist/commands/whatsapp.js +226 -0
  183. package/dist/commands/whoami.d.ts +3 -0
  184. package/dist/commands/whoami.js +110 -0
  185. package/dist/commands/worker.d.ts +3 -0
  186. package/dist/commands/worker.js +60 -0
  187. package/dist/commands/workflows.d.ts +3 -0
  188. package/dist/commands/workflows.js +1752 -0
  189. package/dist/commands/workspace.d.ts +3 -0
  190. package/dist/commands/workspace.js +94 -0
  191. package/dist/compact-list-flags.d.ts +7 -0
  192. package/dist/compact-list-flags.js +12 -0
  193. package/dist/crm-support-flags.js +14 -5
  194. package/dist/customer-operation-commands.js +8 -6
  195. package/dist/diagnostic-uploader.d.ts +3 -1
  196. package/dist/diagnostic-uploader.js +29 -8
  197. package/dist/help.d.ts +5 -0
  198. package/dist/help.js +22 -0
  199. package/dist/index.d.ts +98 -1
  200. package/dist/index.js +331 -30719
  201. package/dist/local-module-loader.d.ts +4 -0
  202. package/dist/local-module-loader.js +156 -0
  203. package/dist/visual-commands.js +9 -5
  204. package/node_modules/@oxygen/cli-ugc/dist/commands.js +2 -2
  205. package/node_modules/@oxygen/shared/dist/agent-session-schedule.d.ts +36 -0
  206. package/node_modules/@oxygen/shared/dist/agent-session-schedule.js +69 -0
  207. package/node_modules/@oxygen/shared/dist/agent-sessions.d.ts +14 -5
  208. package/node_modules/@oxygen/shared/dist/agent-sessions.js +14 -3
  209. package/node_modules/@oxygen/shared/dist/capability-discovery.js +175 -14
  210. package/node_modules/@oxygen/shared/dist/compact-list-result.d.ts +6 -0
  211. package/node_modules/@oxygen/shared/dist/compact-list-result.js +27 -0
  212. package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.d.ts +12 -1
  213. package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.js +5 -2
  214. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +4 -2
  215. package/node_modules/@oxygen/shared/dist/contracts/compact-list-reads.d.ts +57 -0
  216. package/node_modules/@oxygen/shared/dist/contracts/compact-list-reads.js +23 -0
  217. package/node_modules/@oxygen/shared/dist/contracts/crm-support.d.ts +52 -0
  218. package/node_modules/@oxygen/shared/dist/contracts/crm-support.js +43 -2
  219. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +17 -14
  220. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.js +1 -1
  221. package/node_modules/@oxygen/shared/dist/contracts/knowledge-proposal-reads.d.ts +49 -0
  222. package/node_modules/@oxygen/shared/dist/contracts/knowledge-proposal-reads.js +14 -0
  223. package/node_modules/@oxygen/shared/dist/contracts/knowledge-sync.js +1 -1
  224. package/node_modules/@oxygen/shared/dist/contracts/limits-read.js +60 -7
  225. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +9 -1
  226. package/node_modules/@oxygen/shared/dist/contracts/operations.js +13 -5
  227. package/node_modules/@oxygen/shared/dist/contracts/publishing-reads.js +1 -1
  228. package/node_modules/@oxygen/shared/dist/contracts/teammates.d.ts +16 -0
  229. package/node_modules/@oxygen/shared/dist/contracts/teammates.js +115 -0
  230. package/node_modules/@oxygen/shared/dist/cost-estimate-view.js +21 -3
  231. package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +31 -1
  232. package/node_modules/@oxygen/shared/dist/cost-estimate.js +56 -15
  233. package/node_modules/@oxygen/shared/dist/diagnostic-credentials.d.ts +2 -0
  234. package/node_modules/@oxygen/shared/dist/diagnostic-credentials.js +2 -0
  235. package/node_modules/@oxygen/shared/dist/diagnostic-lock.js +71 -44
  236. package/node_modules/@oxygen/shared/dist/diagnostic-spool.d.ts +18 -0
  237. package/node_modules/@oxygen/shared/dist/diagnostic-spool.js +348 -0
  238. package/node_modules/@oxygen/shared/dist/diagnostic-storage-failure.d.ts +10 -0
  239. package/node_modules/@oxygen/shared/dist/diagnostic-storage-failure.js +42 -0
  240. package/node_modules/@oxygen/shared/dist/diagnostic-storage.d.ts +1 -1
  241. package/node_modules/@oxygen/shared/dist/diagnostic-storage.js +59 -213
  242. package/node_modules/@oxygen/shared/dist/diagnostics.js +3 -2
  243. package/node_modules/@oxygen/shared/dist/email-dsn.d.ts +16 -5
  244. package/node_modules/@oxygen/shared/dist/email-dsn.js +97 -1
  245. package/node_modules/@oxygen/shared/dist/emailguard-placement-probe.js +5 -0
  246. package/node_modules/@oxygen/shared/dist/enrichment-intents.js +4 -5
  247. package/node_modules/@oxygen/shared/dist/file-import.js +5 -2
  248. package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +30 -0
  249. package/node_modules/@oxygen/shared/dist/hosted-ai.js +20 -0
  250. package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +6 -2
  251. package/node_modules/@oxygen/shared/dist/knowledge-seed-content.d.ts +17 -7
  252. package/node_modules/@oxygen/shared/dist/knowledge-seed-content.js +81 -17
  253. package/node_modules/@oxygen/shared/dist/langfuse.js +8 -4
  254. package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.d.ts +1 -1
  255. package/node_modules/@oxygen/shared/dist/mailbox-import.d.ts +6 -3
  256. package/node_modules/@oxygen/shared/dist/mailbox-import.js +9 -9
  257. package/node_modules/@oxygen/shared/dist/mcp-server-info.d.ts +8 -0
  258. package/node_modules/@oxygen/shared/dist/mcp-server-info.js +10 -0
  259. package/node_modules/@oxygen/shared/dist/object-storage.d.ts +10 -0
  260. package/node_modules/@oxygen/shared/dist/object-storage.js +14 -0
  261. package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +5 -0
  262. package/node_modules/@oxygen/shared/dist/plan-limits-view.js +17 -1
  263. package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +1 -1
  264. package/node_modules/@oxygen/shared/dist/pricing-sheet.js +1 -1
  265. package/node_modules/@oxygen/shared/dist/provider-selection.d.ts +15 -0
  266. package/node_modules/@oxygen/shared/dist/provider-selection.js +3 -0
  267. package/node_modules/@oxygen/shared/dist/repricing.d.ts +1 -1
  268. package/node_modules/@oxygen/shared/dist/repricing.js +2 -2
  269. package/node_modules/@oxygen/shared/dist/schedule-label.d.ts +6 -0
  270. package/node_modules/@oxygen/shared/dist/schedule-label.js +12 -0
  271. package/node_modules/@oxygen/shared/dist/sequence-failures.d.ts +1 -0
  272. package/node_modules/@oxygen/shared/dist/sequence-failures.js +20 -6
  273. package/node_modules/@oxygen/shared/dist/teammate-groups.d.ts +55 -0
  274. package/node_modules/@oxygen/shared/dist/teammate-groups.js +74 -0
  275. package/node_modules/@oxygen/shared/dist/teammate-messages.d.ts +52 -0
  276. package/node_modules/@oxygen/shared/dist/teammate-messages.js +88 -0
  277. package/node_modules/@oxygen/shared/dist/teammate-notes.d.ts +58 -0
  278. package/node_modules/@oxygen/shared/dist/teammate-notes.js +73 -0
  279. package/node_modules/@oxygen/shared/dist/teammate-routines.d.ts +95 -0
  280. package/node_modules/@oxygen/shared/dist/teammate-routines.js +266 -0
  281. package/node_modules/@oxygen/shared/dist/teammate-tasks.d.ts +56 -0
  282. package/node_modules/@oxygen/shared/dist/teammate-tasks.js +114 -0
  283. package/node_modules/@oxygen/shared/dist/teammates.d.ts +78 -0
  284. package/node_modules/@oxygen/shared/dist/teammates.js +140 -0
  285. package/node_modules/@oxygen/shared/dist/user-capability-routing.js +120 -0
  286. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  287. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  288. package/node_modules/@oxygen/shared/dist/visual-render.d.ts +9 -1
  289. package/node_modules/@oxygen/shared/dist/visual-render.js +12 -5
  290. package/node_modules/@oxygen/shared/dist/warmup-metrics.d.ts +5 -0
  291. package/node_modules/@oxygen/shared/dist/warmup-metrics.js +44 -0
  292. package/package.json +1 -1
@@ -0,0 +1,125 @@
1
+ import { readPositiveInt } from "../cli-values.js";
2
+ import { requestOxygen } from "../http-client.js";
3
+ import { readOption } from "../util.js";
4
+ import { Command } from "commander";
5
+ // The observability console caps each source at --limit (default 25, max 100) and
6
+ // reports which hit the cap in sources[].capped. The human (non --json) output is
7
+ // a JSON dump that buries that boolean, so surface it as a one-line stderr notice:
8
+ // the counts shown are only the returned page, and more may sit behind a capped
9
+ // source. Not pagination — raising --limit or filtering --source is the fix.
10
+ function writeObservabilityCapsNotice(data) {
11
+ if (!data || typeof data !== "object" || Array.isArray(data))
12
+ return;
13
+ const record = data;
14
+ const sources = Array.isArray(record.sources) ? record.sources : [];
15
+ const cappedSources = sources
16
+ .filter((source) => Boolean(source) && typeof source === "object" && !Array.isArray(source)
17
+ && source.capped === true)
18
+ .map((source) => String(source.source));
19
+ if (cappedSources.length === 0)
20
+ return;
21
+ const returned = typeof record.returned === "number"
22
+ ? record.returned
23
+ : Array.isArray(record.items) ? record.items.length : 0;
24
+ process.stderr.write(`note: showing ${returned} item(s); some sources hit the per-source cap (${cappedSources.join(", ")}) — raise --limit (max 100) or filter --source to see more.\n`);
25
+ }
26
+ export function registerObservabilityFamily(program, handleAsyncAction) {
27
+ program
28
+ .command("observability")
29
+ .description("Workspace observability: redacted operation events, plus the cross-primitive runs lens and approvals inbox (list-only — decisions route to the owning primitive).")
30
+ .addCommand(new Command("events")
31
+ .description("List recent redacted OXYGEN operation events and failures. Customer support triage and support-event history live in Plain Inbox.")
32
+ // Keep in sync with OBSERVABILITY_STATUS_FILTERS in
33
+ // apps/web/src/lib/observability.ts and the MCP tool enum in
34
+ // packages/mcp-server/src/tools/observability.ts. The API rejects any
35
+ // other value with invalid_request so a typo fails loudly.
36
+ .option("--status <status>", "Filter by success, error, completed, failed, completed_with_errors, queued, skipped, or blocked.")
37
+ // Keep --type / --source values in sync with OBSERVABILITY_TYPE_FILTERS /
38
+ // OBSERVABILITY_SOURCE_FILTERS in apps/web/src/lib/observability.ts and
39
+ // the MCP tool enums. The API rejects any other value with
40
+ // invalid_request so a typo fails loudly.
41
+ .option("--type <type>", "Filter by operation or provider_request.")
42
+ .option("--source <source>", "Filter by cli, web, mcp, workflow, or provider.")
43
+ .option("--trace-id <trace_id>", "Filter by trace id.")
44
+ .option("--run-id <run_id>", "Filter by workspace run id.")
45
+ .option("--limit <n>", "Maximum events to return. Defaults to 50.")
46
+ .option("--json", "Print a JSON envelope.")
47
+ .action(async (options) => {
48
+ await handleAsyncAction("observability events", options, () => {
49
+ const params = new URLSearchParams();
50
+ const status = readOption(options.status);
51
+ const type = readOption(options.type);
52
+ const source = readOption(options.source);
53
+ const traceId = readOption(options.traceId);
54
+ const runId = readOption(options.runId);
55
+ const limit = readPositiveInt(options.limit);
56
+ if (status)
57
+ params.set("status", status);
58
+ if (type)
59
+ params.set("type", type);
60
+ if (source)
61
+ params.set("source", source);
62
+ if (traceId)
63
+ params.set("trace_id", traceId);
64
+ if (runId)
65
+ params.set("run_id", runId);
66
+ if (limit)
67
+ params.set("limit", String(limit));
68
+ const suffix = params.toString() ? `?${params.toString()}` : "";
69
+ return requestOxygen(`/api/cli/observability/events${suffix}`);
70
+ });
71
+ }))
72
+ .addCommand(new Command("runs")
73
+ .description("List active and failed runs across workflows and table action/ingestion runs — the cross-primitive runs lens. Read-only; each failed item carries the command that resolves it.")
74
+ // Keep --status / --source values in sync with OBSERVABILITY_CONSOLE_STATES
75
+ // and OBSERVABILITY_RUN_SOURCES in apps/web/src/lib/observability-console.ts
76
+ // and the MCP tool enums. The API rejects any other value with
77
+ // invalid_request so a typo fails loudly.
78
+ .option("--status <statuses>", "Comma-separated normalized states: running, queued, waiting_approval, failed, completed, canceled. Defaults to active + failed.")
79
+ .option("--source <sources>", "Comma-separated sources: workflow, table_run.")
80
+ .option("--limit <n>", "Maximum items per source (default 25, max 100). Counts reflect the returned page only.")
81
+ .option("--json", "Print a JSON envelope.")
82
+ .action(async (options) => {
83
+ await handleAsyncAction("observability runs", options, async () => {
84
+ const params = new URLSearchParams();
85
+ const status = readOption(options.status);
86
+ const source = readOption(options.source);
87
+ const limit = readPositiveInt(options.limit);
88
+ if (status)
89
+ params.set("status", status);
90
+ if (source)
91
+ params.set("source", source);
92
+ if (limit)
93
+ params.set("limit", String(limit));
94
+ const suffix = params.toString() ? `?${params.toString()}` : "";
95
+ const data = await requestOxygen(`/api/cli/observability/runs${suffix}`);
96
+ if (!options.json)
97
+ writeObservabilityCapsNotice(data);
98
+ return data;
99
+ });
100
+ }))
101
+ .addCommand(new Command("approvals")
102
+ .description("List everything waiting for a human decision across workflows, publishing, message reviews, inbox reply drafts, and collaboration approval requests — the cross-primitive approvals inbox. Read-only: each item includes the exact command to resolve it and a web link to decide.")
103
+ // Keep --source values in sync with OBSERVABILITY_APPROVAL_SOURCES in
104
+ // apps/web/src/lib/observability-console.ts and the MCP tool enum. The API
105
+ // rejects any other value with invalid_request so a typo fails loudly.
106
+ .option("--source <sources>", "Comma-separated sources: workflow, publishing, message_review, inbox_draft, collab_request.")
107
+ .option("--limit <n>", "Maximum items per source (default 25, max 100). Counts reflect the returned page only.")
108
+ .option("--json", "Print a JSON envelope.")
109
+ .action(async (options) => {
110
+ await handleAsyncAction("observability approvals", options, async () => {
111
+ const params = new URLSearchParams();
112
+ const source = readOption(options.source);
113
+ const limit = readPositiveInt(options.limit);
114
+ if (source)
115
+ params.set("source", source);
116
+ if (limit)
117
+ params.set("limit", String(limit));
118
+ const suffix = params.toString() ? `?${params.toString()}` : "";
119
+ const data = await requestOxygen(`/api/cli/observability/approvals${suffix}`);
120
+ if (!options.json)
121
+ writeObservabilityCapsNotice(data);
122
+ return data;
123
+ });
124
+ }));
125
+ }
@@ -0,0 +1,3 @@
1
+ import { ExtractedCommandActionHandler } from "../command-support.js";
2
+ import { Command } from "commander";
3
+ export declare function registerOrgsFamily(program: Command, handleAsyncAction: ExtractedCommandActionHandler): void;
@@ -0,0 +1,237 @@
1
+ import { emitCliFailure, emitSuccess, writeJson } from "../cli-output.js";
2
+ import { formatCollabWriteResult, handleCollabAction, persistOrganizationSelection, readPositiveNumber, resolveActiveProfileWithSource } from "../command-support.js";
3
+ import { addContractFlags, readContractFlags } from "../contract-flags.js";
4
+ import { requestOxygen } from "../http-client.js";
5
+ import { isRecord, readOption } from "../util.js";
6
+ import { OxygenError, success } from "@oxygen/shared/cli-result";
7
+ import { BILLING_OWNER_READ_CONTRACT } from "@oxygen/shared/contracts/billing-owner-read";
8
+ import { Command } from "commander";
9
+ // An `oxy_live_` key can only ever answer for the one workspace it is bound to,
10
+ // so `orgs list` returns a single row and `orgs use` refuses — both truthfully,
11
+ // and both indistinguishable from "this account has one workspace" if you only
12
+ // read stdout. The server says which credential answered and whether it can
13
+ // switch; mirror that on stderr, the same split writeDryRunNotice uses, so the
14
+ // machine-read envelope stays clean. This is the line that would have stopped a
15
+ // user from minting a 21st key that also could not switch.
16
+ function writeCredentialNotice(credential) {
17
+ if (!credential || credential.switchable !== false)
18
+ return;
19
+ if (typeof credential.message === "string" && credential.message.trim()) {
20
+ process.stderr.write(`${credential.message}\n`);
21
+ }
22
+ }
23
+ async function handleOrgsListAction(options) {
24
+ try {
25
+ const data = await requestOxygen("/api/cli/orgs");
26
+ emitSuccess("orgs list", data, options);
27
+ writeCredentialNotice(data.credential);
28
+ }
29
+ catch (error) {
30
+ emitCliFailure("orgs list", error);
31
+ }
32
+ }
33
+ async function handleOrgUseAction(organization, options, command) {
34
+ try {
35
+ // Credentials stay ambient here: an OXYGEN_API_KEY in the environment
36
+ // outranks the stored profile inside requestOxygen, and passing the profile
37
+ // explicitly would silently select with the wrong one.
38
+ const data = await requestOxygen("/api/cli/orgs/select", {
39
+ method: "POST",
40
+ body: { organization },
41
+ });
42
+ const context = await resolveActiveProfileWithSource();
43
+ const profile = context.resolution.exists ? context.resolution.name : null;
44
+ const selectionPersisted = await persistOrganizationSelection({
45
+ profile,
46
+ credentials: context.resolution.credentials,
47
+ organization: data.currentOrganization,
48
+ });
49
+ // OXYGEN_ORG outranks the stored selection on every later command (the
50
+ // oxygen-dev wrapper exports one), so a selection it contradicts is saved
51
+ // but never used. Reported 2026-09-22: three "successful" selects in a row.
52
+ const envOrganization = process.env.OXYGEN_ORG?.trim();
53
+ const selected = data.currentOrganization;
54
+ const environmentOverride = envOrganization && selected
55
+ && envOrganization !== selected.id && envOrganization !== selected.slug
56
+ ? {
57
+ variable: "OXYGEN_ORG",
58
+ organization: envOrganization,
59
+ message: `OXYGEN_ORG=${envOrganization} is set, so later commands still use that workspace. Unset it, set it to ${selected.slug ?? selected.id}, or pass --org.`,
60
+ }
61
+ : null;
62
+ const result = {
63
+ ...data,
64
+ profile,
65
+ selection_persisted: selectionPersisted,
66
+ ...(environmentOverride ? { environment_override: environmentOverride } : {}),
67
+ };
68
+ if (options.json) {
69
+ writeJson(success(command, result));
70
+ }
71
+ else {
72
+ writeJson(result);
73
+ }
74
+ writeCredentialNotice(data.credential);
75
+ if (environmentOverride)
76
+ process.stderr.write(`${environmentOverride.message}\n`);
77
+ }
78
+ catch (error) {
79
+ emitCliFailure(command, error);
80
+ }
81
+ }
82
+ // Why each organization cannot pay for another workspace, in the user's words.
83
+ // The codes are the server's; the sentences exist so a blocked `billing-link`
84
+ // says what to fix instead of printing an enum.
85
+ const BILLING_OWNER_INELIGIBLE_REASONS = {
86
+ not_admin: "You are not an admin of that organization.",
87
+ no_plan: "It has no active paid plan.",
88
+ trialing: "It is on a trial, and a trial only covers the workspace it started in.",
89
+ already_linked: "Its own billing is already covered by another organization.",
90
+ };
91
+ // `--owner` is optional because the case that matters is the plainest one: the
92
+ // user pays for exactly one organization and wants this workspace on it. The
93
+ // eligibility rules (admin seat, active paid plan, no trial) stay on the server
94
+ // — the CLI only picks when the answer is unambiguous, and turns "which one?"
95
+ // into a typed failure rather than a guess.
96
+ async function resolveSoleBillingOwnerRef() {
97
+ const candidates = readBillingOwnerCandidates(await requestOxygen("/api/cli/orgs/billing-owners"));
98
+ const eligible = candidates.filter((candidate) => candidate.eligible);
99
+ if (eligible.length === 1)
100
+ return eligible[0].id;
101
+ if (eligible.length > 1) {
102
+ throw new OxygenError("missing_billing_owner", `${eligible.length} of your organizations can pay for this workspace, so Oxygen will not choose for you. Re-run with --owner <organization>.`, {
103
+ details: {
104
+ eligible_billing_owners: eligible.map((candidate) => ({
105
+ organization_id: candidate.id,
106
+ name: candidate.name,
107
+ })),
108
+ next_step: `oxygen orgs billing-link --owner ${eligible[0].id}`,
109
+ },
110
+ });
111
+ }
112
+ throw new OxygenError("no_eligible_billing_owner", candidates.length === 0
113
+ ? "You do not belong to another organization, so there is no existing plan to put this workspace on. Start one at https://oxygen-agent.com/billing."
114
+ : "None of your other organizations can pay for this workspace. Only an organization you administer that is on an active paid plan can cover another workspace — a trial cannot.", {
115
+ details: {
116
+ candidates: candidates.map((candidate) => ({
117
+ organization_id: candidate.id,
118
+ name: candidate.name,
119
+ ineligible_reason: candidate.ineligible_reason,
120
+ reason: candidate.ineligible_reason
121
+ ? BILLING_OWNER_INELIGIBLE_REASONS[candidate.ineligible_reason] ?? null
122
+ : null,
123
+ })),
124
+ next_step: "Start a plan on the organization that should pay at https://oxygen-agent.com/billing, then re-run `oxygen orgs billing-link`.",
125
+ },
126
+ });
127
+ }
128
+ // Both spellings of the reason field are read, the same way the billing-link
129
+ // route accepts either spelling of its body keys: the rows come straight out of
130
+ // the billing-ownership library, and a casing change there must not silently
131
+ // downgrade "we picked your plan" into "you have no eligible plan".
132
+ function readBillingOwnerCandidates(payload) {
133
+ const rows = isRecord(payload) && Array.isArray(payload.candidates) ? payload.candidates : [];
134
+ const candidates = [];
135
+ for (const row of rows) {
136
+ if (!isRecord(row) || typeof row.id !== "string" || !row.id)
137
+ continue;
138
+ const reason = row.ineligible_reason ?? row.ineligibleReason;
139
+ candidates.push({
140
+ id: row.id,
141
+ name: typeof row.name === "string" ? row.name : row.id,
142
+ eligible: row.eligible === true,
143
+ ineligible_reason: typeof reason === "string" ? reason : null,
144
+ });
145
+ }
146
+ return candidates;
147
+ }
148
+ export function registerOrgsFamily(program, handleAsyncAction) {
149
+ const billingOwnerReadFlags = { organization: { flag: "expected-organization", valueName: "<organization>" } };
150
+ program
151
+ .command("orgs")
152
+ .description("Switch between the organizations (workspaces) you belong to, and share one paid plan across them. If this workspace has no plan of its own, `orgs billing-link` puts it on a plan you already pay for somewhere else instead of buying a second subscription.")
153
+ .addCommand(new Command("list")
154
+ .description("List organizations available to the current CLI identity.")
155
+ .option("--json", "Print a JSON envelope.")
156
+ .action(async (options) => {
157
+ await handleOrgsListAction(options);
158
+ }))
159
+ .addCommand(new Command("use")
160
+ .description("Select the active organization for this CLI profile. Apps connected with OAuth (ChatGPT, Claude…) keep their own default workspace: `oxygen connectors`.")
161
+ .argument("<organization>", "Organization id, Clerk org id, or slug returned by orgs list.")
162
+ .option("--json", "Print a JSON envelope.")
163
+ .action(async (organization, options) => {
164
+ await handleOrgUseAction(organization, options, "orgs use");
165
+ }))
166
+ .addCommand(new Command("select")
167
+ .description("Alias for orgs use: this CLI profile only. For ChatGPT, Claude or another connected app, use `oxygen connectors set`.")
168
+ .argument("<organization>", "Organization id, Clerk org id, or slug returned by orgs list.")
169
+ .option("--json", "Print a JSON envelope.")
170
+ .action(async (organization, options) => {
171
+ await handleOrgUseAction(organization, options, "orgs select");
172
+ }))
173
+ .addCommand(addContractFlags(new Command("billing-owners"), BILLING_OWNER_READ_CONTRACT, billingOwnerReadFlags)
174
+ .description("Read-only, 0 Oxygen credits; does not reconcile credits. List the organizations that could pay for this workspace: which of the organizations you belong to can cover another workspace's credits, and for each of the rest, why it cannot.")
175
+ .option("--json", "Print a JSON envelope.")
176
+ .action(async (options) => {
177
+ await handleAsyncAction("orgs billing-owners", options, () => {
178
+ const fields = readContractFlags(options, BILLING_OWNER_READ_CONTRACT, billingOwnerReadFlags);
179
+ const query = new URLSearchParams(Object.entries(fields).map(([key, value]) => [key, String(value)]));
180
+ return requestOxygen(`/api/cli/orgs/billing-owners${query.size ? `?${query}` : ""}`);
181
+ });
182
+ }))
183
+ .addCommand(new Command("billing-link")
184
+ .description("Cover a workspace with another organization's paid plan instead of buying a second subscription. Omit --owner when exactly one owner is eligible; orgs billing-owners lists them. You must administer both organizations, the workspace must not hold its own live subscription, and an org-bound key can link only its workspace. The owner funds credits; data access stays unchanged. billing estimate reports sender_billing: credit_reservations uses the owner's credits, including when plan_tier is free; legacy sending_seats uses the owner's paid plan and seats. Connecting a sending account, BYOK and Instagram/TikTok/YouTube/Facebook publishing still need a paid plan. billing usage --group-by workspace reports each linked workspace's spend.")
185
+ .option("--owner <organization>", "Billing owner organization id, Clerk org id, or slug. Optional — when omitted, Oxygen uses your one eligible organization, and refuses to pick if there is more than one.")
186
+ .option("--organization <organization>", "Workspace organization to link. Defaults to the active organization.")
187
+ .option("--organization-id <id>", "Alias for --organization.")
188
+ .option("--monthly-credit-cap <credits>", "Optional monthly credit cap for this workspace. Omit it to leave any existing cap untouched.")
189
+ .option("--json", "Print a JSON envelope.")
190
+ .action(async (options) => {
191
+ await handleAsyncAction("orgs billing-link", options, async () => {
192
+ const billingOwner = readOption(options.owner) ?? await resolveSoleBillingOwnerRef();
193
+ return requestOxygen("/api/cli/orgs/billing-link", {
194
+ method: "POST",
195
+ body: {
196
+ billing_owner_organization_id: billingOwner,
197
+ ...(readOption(options.organization) || readOption(options.organizationId)
198
+ ? { organization_id: readOption(options.organization) ?? readOption(options.organizationId) }
199
+ : {}),
200
+ // The key is omitted, never sent as null, when the flag is
201
+ // absent: the server treats a present key as an explicit write,
202
+ // so sending it unconditionally would wipe an agency's
203
+ // per-client cap on every re-link.
204
+ ...(readOption(options.monthlyCreditCap)
205
+ ? { monthly_credit_cap: readPositiveNumber(options.monthlyCreditCap) }
206
+ : {}),
207
+ },
208
+ });
209
+ });
210
+ }))
211
+ .addCommand(new Command("billing-unlink")
212
+ .description("Return a workspace to its own billing owner and credit balance. Its own subscription state, plan and sender_billing model determine which actions need a plan.")
213
+ .option("--organization <organization>", "Workspace organization to unlink. Defaults to the active organization.")
214
+ .option("--organization-id <id>", "Alias for --organization.")
215
+ .option("--json", "Print a JSON envelope.")
216
+ .action(async (options) => {
217
+ await handleAsyncAction("orgs billing-unlink", options, () => requestOxygen("/api/cli/orgs/billing-unlink", {
218
+ method: "POST",
219
+ body: {
220
+ ...(readOption(options.organization) || readOption(options.organizationId)
221
+ ? { organization_id: readOption(options.organization) ?? readOption(options.organizationId) }
222
+ : {}),
223
+ },
224
+ }));
225
+ }))
226
+ .addCommand(new Command("member-role")
227
+ .description("Move a member between the admin and member workspace roles. ADMIN ONLY. This is the one Clerk membership change Oxygen makes — inviting people is still done in workspace settings. Use it to demote an admin before `approvals client grant`: a workspace admin's role overrides the restricted client role, so the grant is refused until they are a plain member.")
228
+ .argument("<email>", "Workspace member's email.")
229
+ .requiredOption("--role <role>", "admin or member.")
230
+ .option("--json", "Print a JSON envelope.")
231
+ .action(async (email, options) => {
232
+ await handleCollabAction("orgs member-role", options, () => requestOxygen("/api/cli/collab/members/workspace-role", {
233
+ method: "POST",
234
+ body: { email, role: readOption(options.role) },
235
+ }), formatCollabWriteResult);
236
+ }));
237
+ }
@@ -0,0 +1,3 @@
1
+ import { ExtractedCommandActionHandler } from "../command-support.js";
2
+ import { Command } from "commander";
3
+ export declare function registerPeopleFamily(program: Command, handleAsyncAction: ExtractedCommandActionHandler): void;