@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.
- package/README.md +1 -1
- package/dist/admin-primary-providers-render.js +27 -19
- package/dist/cli-output.d.ts +12 -0
- package/dist/cli-output.js +395 -0
- package/dist/cli-paths.d.ts +1 -0
- package/dist/cli-paths.js +3 -0
- package/dist/cli-profile-context.d.ts +3 -0
- package/dist/cli-profile-context.js +13 -0
- package/dist/command-manifest.d.ts +15 -0
- package/dist/command-manifest.js +23 -1
- package/dist/command-support.d.ts +438 -0
- package/dist/command-support.js +1610 -0
- package/dist/commands/action-column.d.ts +3 -0
- package/dist/commands/action-column.js +43 -0
- package/dist/commands/activation.d.ts +3 -0
- package/dist/commands/activation.js +24 -0
- package/dist/commands/admin.d.ts +3 -0
- package/dist/commands/admin.js +347 -0
- package/dist/commands/ads.d.ts +3 -0
- package/dist/commands/ads.js +62 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.js +274 -0
- package/dist/commands/api-keys.d.ts +3 -0
- package/dist/commands/api-keys.js +77 -0
- package/dist/commands/approvals.d.ts +3 -0
- package/dist/commands/approvals.js +518 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.js +230 -0
- package/dist/commands/billing.d.ts +3 -0
- package/dist/commands/billing.js +357 -0
- package/dist/commands/blueprints.d.ts +3 -0
- package/dist/commands/blueprints.js +312 -0
- package/dist/commands/budget.d.ts +3 -0
- package/dist/commands/budget.js +55 -0
- package/dist/commands/callables.d.ts +3 -0
- package/dist/commands/callables.js +111 -0
- package/dist/commands/capabilities.d.ts +3 -0
- package/dist/commands/capabilities.js +44 -0
- package/dist/commands/cells.d.ts +3 -0
- package/dist/commands/cells.js +94 -0
- package/dist/commands/columns.d.ts +3 -0
- package/dist/commands/columns.js +1628 -0
- package/dist/commands/commands.d.ts +3 -0
- package/dist/commands/commands.js +45 -0
- package/dist/commands/comments.d.ts +3 -0
- package/dist/commands/comments.js +188 -0
- package/dist/commands/companies.d.ts +3 -0
- package/dist/commands/companies.js +312 -0
- package/dist/commands/connections.d.ts +3 -0
- package/dist/commands/connections.js +39 -0
- package/dist/commands/context.d.ts +3 -0
- package/dist/commands/context.js +170 -0
- package/dist/commands/copilot.d.ts +3 -0
- package/dist/commands/copilot.js +565 -0
- package/dist/commands/crm.d.ts +3 -0
- package/dist/commands/crm.js +1669 -0
- package/dist/commands/custom-integrations.d.ts +3 -0
- package/dist/commands/custom-integrations.js +140 -0
- package/dist/commands/dashboards.d.ts +3 -0
- package/dist/commands/dashboards.js +240 -0
- package/dist/commands/db.d.ts +3 -0
- package/dist/commands/db.js +190 -0
- package/dist/commands/deliverability.d.ts +3 -0
- package/dist/commands/deliverability.js +90 -0
- package/dist/commands/directory.d.ts +3 -0
- package/dist/commands/directory.js +101 -0
- package/dist/commands/domains.d.ts +3 -0
- package/dist/commands/domains.js +604 -0
- package/dist/commands/egress.d.ts +3 -0
- package/dist/commands/egress.js +130 -0
- package/dist/commands/email.d.ts +3 -0
- package/dist/commands/email.js +49 -0
- package/dist/commands/engagement.d.ts +3 -0
- package/dist/commands/engagement.js +189 -0
- package/dist/commands/enrich-column.d.ts +3 -0
- package/dist/commands/enrich-column.js +149 -0
- package/dist/commands/enrichment.d.ts +3 -0
- package/dist/commands/enrichment.js +49 -0
- package/dist/commands/feedback.d.ts +3 -0
- package/dist/commands/feedback.js +96 -0
- package/dist/commands/feeds.d.ts +3 -0
- package/dist/commands/feeds.js +486 -0
- package/dist/commands/find.d.ts +3 -0
- package/dist/commands/find.js +159 -0
- package/dist/commands/followers.d.ts +3 -0
- package/dist/commands/followers.js +39 -0
- package/dist/commands/formulas.d.ts +3 -0
- package/dist/commands/formulas.js +43 -0
- package/dist/commands/home.d.ts +3 -0
- package/dist/commands/home.js +22 -0
- package/dist/commands/inbox.d.ts +3 -0
- package/dist/commands/inbox.js +779 -0
- package/dist/commands/integrations.d.ts +3 -0
- package/dist/commands/integrations.js +290 -0
- package/dist/commands/knowledge.d.ts +3 -0
- package/dist/commands/knowledge.js +874 -0
- package/dist/commands/lead-sourcing.d.ts +3 -0
- package/dist/commands/lead-sourcing.js +52 -0
- package/dist/commands/limits.d.ts +3 -0
- package/dist/commands/limits.js +54 -0
- package/dist/commands/linkedin.d.ts +3 -0
- package/dist/commands/linkedin.js +275 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +347 -0
- package/dist/commands/logout.d.ts +3 -0
- package/dist/commands/logout.js +12 -0
- package/dist/commands/mailboxes.d.ts +3 -0
- package/dist/commands/mailboxes.js +1290 -0
- package/dist/commands/managed-inboxes.d.ts +3 -0
- package/dist/commands/managed-inboxes.js +361 -0
- package/dist/commands/messages.d.ts +3 -0
- package/dist/commands/messages.js +78 -0
- package/dist/commands/notetaker.d.ts +3 -0
- package/dist/commands/notetaker.js +79 -0
- package/dist/commands/observability.d.ts +3 -0
- package/dist/commands/observability.js +125 -0
- package/dist/commands/orgs.d.ts +3 -0
- package/dist/commands/orgs.js +237 -0
- package/dist/commands/people.d.ts +3 -0
- package/dist/commands/people.js +353 -0
- package/dist/commands/posts.d.ts +3 -0
- package/dist/commands/posts.js +139 -0
- package/dist/commands/profiles.d.ts +3 -0
- package/dist/commands/profiles.js +250 -0
- package/dist/commands/projects.d.ts +3 -0
- package/dist/commands/projects.js +94 -0
- package/dist/commands/prompts.d.ts +3 -0
- package/dist/commands/prompts.js +84 -0
- package/dist/commands/publishing.d.ts +3 -0
- package/dist/commands/publishing.js +1419 -0
- package/dist/commands/recipes.d.ts +3 -0
- package/dist/commands/recipes.js +120 -0
- package/dist/commands/reviews.d.ts +3 -0
- package/dist/commands/reviews.js +75 -0
- package/dist/commands/rows.d.ts +3 -0
- package/dist/commands/rows.js +27 -0
- package/dist/commands/runs.d.ts +3 -0
- package/dist/commands/runs.js +37 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.js +182 -0
- package/dist/commands/senders.d.ts +3 -0
- package/dist/commands/senders.js +393 -0
- package/dist/commands/sequences.d.ts +3 -0
- package/dist/commands/sequences.js +2322 -0
- package/dist/commands/session.d.ts +3 -0
- package/dist/commands/session.js +109 -0
- package/dist/commands/signals.d.ts +3 -0
- package/dist/commands/signals.js +289 -0
- package/dist/commands/signup-leads.d.ts +3 -0
- package/dist/commands/signup-leads.js +46 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.js +50 -0
- package/dist/commands/sourcing.d.ts +3 -0
- package/dist/commands/sourcing.js +27 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +51 -0
- package/dist/commands/supabase.d.ts +3 -0
- package/dist/commands/supabase.js +128 -0
- package/dist/commands/support.d.ts +3 -0
- package/dist/commands/support.js +651 -0
- package/dist/commands/suppressions.d.ts +3 -0
- package/dist/commands/suppressions.js +577 -0
- package/dist/commands/table-ingestions.d.ts +3 -0
- package/dist/commands/table-ingestions.js +145 -0
- package/dist/commands/table-runs.d.ts +3 -0
- package/dist/commands/table-runs.js +277 -0
- package/dist/commands/tables.d.ts +3 -0
- package/dist/commands/tables.js +3492 -0
- package/dist/commands/tags.d.ts +3 -0
- package/dist/commands/tags.js +147 -0
- package/dist/commands/tools.d.ts +3 -0
- package/dist/commands/tools.js +246 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +106 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.js +27 -0
- package/dist/commands/viewers.d.ts +3 -0
- package/dist/commands/viewers.js +72 -0
- package/dist/commands/voice.d.ts +3 -0
- package/dist/commands/voice.js +351 -0
- package/dist/commands/whatsapp.d.ts +3 -0
- package/dist/commands/whatsapp.js +226 -0
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.js +110 -0
- package/dist/commands/worker.d.ts +3 -0
- package/dist/commands/worker.js +60 -0
- package/dist/commands/workflows.d.ts +3 -0
- package/dist/commands/workflows.js +1752 -0
- package/dist/commands/workspace.d.ts +3 -0
- package/dist/commands/workspace.js +94 -0
- package/dist/compact-list-flags.d.ts +7 -0
- package/dist/compact-list-flags.js +12 -0
- package/dist/crm-support-flags.js +14 -5
- package/dist/customer-operation-commands.js +8 -6
- package/dist/diagnostic-uploader.d.ts +3 -1
- package/dist/diagnostic-uploader.js +29 -8
- package/dist/help.d.ts +5 -0
- package/dist/help.js +22 -0
- package/dist/index.d.ts +98 -1
- package/dist/index.js +331 -30719
- package/dist/local-module-loader.d.ts +4 -0
- package/dist/local-module-loader.js +156 -0
- package/dist/visual-commands.js +9 -5
- package/node_modules/@oxygen/cli-ugc/dist/commands.js +2 -2
- package/node_modules/@oxygen/shared/dist/agent-session-schedule.d.ts +36 -0
- package/node_modules/@oxygen/shared/dist/agent-session-schedule.js +69 -0
- package/node_modules/@oxygen/shared/dist/agent-sessions.d.ts +14 -5
- package/node_modules/@oxygen/shared/dist/agent-sessions.js +14 -3
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +175 -14
- package/node_modules/@oxygen/shared/dist/compact-list-result.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/compact-list-result.js +27 -0
- package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.d.ts +12 -1
- package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.js +5 -2
- package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +4 -2
- package/node_modules/@oxygen/shared/dist/contracts/compact-list-reads.d.ts +57 -0
- package/node_modules/@oxygen/shared/dist/contracts/compact-list-reads.js +23 -0
- package/node_modules/@oxygen/shared/dist/contracts/crm-support.d.ts +52 -0
- package/node_modules/@oxygen/shared/dist/contracts/crm-support.js +43 -2
- package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +17 -14
- package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/knowledge-proposal-reads.d.ts +49 -0
- package/node_modules/@oxygen/shared/dist/contracts/knowledge-proposal-reads.js +14 -0
- package/node_modules/@oxygen/shared/dist/contracts/knowledge-sync.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/limits-read.js +60 -7
- package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +9 -1
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +13 -5
- package/node_modules/@oxygen/shared/dist/contracts/publishing-reads.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/teammates.d.ts +16 -0
- package/node_modules/@oxygen/shared/dist/contracts/teammates.js +115 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate-view.js +21 -3
- package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +31 -1
- package/node_modules/@oxygen/shared/dist/cost-estimate.js +56 -15
- package/node_modules/@oxygen/shared/dist/diagnostic-credentials.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-credentials.js +2 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-lock.js +71 -44
- package/node_modules/@oxygen/shared/dist/diagnostic-spool.d.ts +18 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-spool.js +348 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-storage-failure.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-storage-failure.js +42 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-storage.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/diagnostic-storage.js +59 -213
- package/node_modules/@oxygen/shared/dist/diagnostics.js +3 -2
- package/node_modules/@oxygen/shared/dist/email-dsn.d.ts +16 -5
- package/node_modules/@oxygen/shared/dist/email-dsn.js +97 -1
- package/node_modules/@oxygen/shared/dist/emailguard-placement-probe.js +5 -0
- package/node_modules/@oxygen/shared/dist/enrichment-intents.js +4 -5
- package/node_modules/@oxygen/shared/dist/file-import.js +5 -2
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +30 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +20 -0
- package/node_modules/@oxygen/shared/dist/knowledge-bootstrap.d.ts +6 -2
- package/node_modules/@oxygen/shared/dist/knowledge-seed-content.d.ts +17 -7
- package/node_modules/@oxygen/shared/dist/knowledge-seed-content.js +81 -17
- package/node_modules/@oxygen/shared/dist/langfuse.js +8 -4
- package/node_modules/@oxygen/shared/dist/mailbox-egress-ownership.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/mailbox-import.d.ts +6 -3
- package/node_modules/@oxygen/shared/dist/mailbox-import.js +9 -9
- package/node_modules/@oxygen/shared/dist/mcp-server-info.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/mcp-server-info.js +10 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +14 -0
- package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/plan-limits-view.js +17 -1
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +1 -1
- package/node_modules/@oxygen/shared/dist/provider-selection.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/provider-selection.js +3 -0
- package/node_modules/@oxygen/shared/dist/repricing.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/repricing.js +2 -2
- package/node_modules/@oxygen/shared/dist/schedule-label.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/schedule-label.js +12 -0
- package/node_modules/@oxygen/shared/dist/sequence-failures.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/sequence-failures.js +20 -6
- package/node_modules/@oxygen/shared/dist/teammate-groups.d.ts +55 -0
- package/node_modules/@oxygen/shared/dist/teammate-groups.js +74 -0
- package/node_modules/@oxygen/shared/dist/teammate-messages.d.ts +52 -0
- package/node_modules/@oxygen/shared/dist/teammate-messages.js +88 -0
- package/node_modules/@oxygen/shared/dist/teammate-notes.d.ts +58 -0
- package/node_modules/@oxygen/shared/dist/teammate-notes.js +73 -0
- package/node_modules/@oxygen/shared/dist/teammate-routines.d.ts +95 -0
- package/node_modules/@oxygen/shared/dist/teammate-routines.js +266 -0
- package/node_modules/@oxygen/shared/dist/teammate-tasks.d.ts +56 -0
- package/node_modules/@oxygen/shared/dist/teammate-tasks.js +114 -0
- package/node_modules/@oxygen/shared/dist/teammates.d.ts +78 -0
- package/node_modules/@oxygen/shared/dist/teammates.js +140 -0
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +120 -0
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/visual-render.d.ts +9 -1
- package/node_modules/@oxygen/shared/dist/visual-render.js +12 -5
- package/node_modules/@oxygen/shared/dist/warmup-metrics.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/warmup-metrics.js +44 -0
- 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,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
|
+
}
|