@oxygen-agent/cli 1.720.7 → 1.739.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.
- package/README.md +1 -1
- package/dist/command-manifest.js +27 -12
- package/dist/index.js +227 -63
- package/dist/skills.js +2 -2
- package/node_modules/@oxygen/shared/dist/cli-result.js +2 -0
- package/node_modules/@oxygen/shared/dist/crm-activity-events.d.ts +20 -0
- package/node_modules/@oxygen/shared/dist/crm-activity-events.js +5 -0
- package/node_modules/@oxygen/shared/dist/future-signup-events.d.ts +27 -0
- package/node_modules/@oxygen/shared/dist/future-signup-events.js +90 -0
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.d.ts +320 -0
- package/node_modules/@oxygen/shared/dist/future-signup-lifecycle-projection.js +1233 -0
- package/node_modules/@oxygen/shared/dist/index.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/index.js +6 -0
- package/node_modules/@oxygen/shared/dist/notetaker-events.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/notetaker-events.js +23 -0
- package/node_modules/@oxygen/shared/dist/plain-support-events.d.ts +40 -0
- package/node_modules/@oxygen/shared/dist/plain-support-events.js +43 -0
- package/node_modules/@oxygen/shared/dist/redaction.js +6 -1
- package/node_modules/@oxygen/shared/dist/user-capability-routing.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +23 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/dist/webhook-headers.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/webhook-headers.js +48 -0
- package/node_modules/@oxygen/shared/dist/workspace-event-catalog.d.ts +31 -1
- package/node_modules/@oxygen/shared/dist/workspace-event-catalog.js +112 -1
- package/node_modules/@oxygen/workflows/dist/graph/expression.js +7 -5
- package/node_modules/@oxygen/workflows/dist/graph/lint.d.ts +9 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +409 -7
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +513 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +104 -1
- package/node_modules/@oxygen/workflows/dist/graph/params.d.ts +2 -0
- package/node_modules/@oxygen/workflows/dist/graph/params.js +15 -0
- package/node_modules/@oxygen/workflows/dist/graph/remap.js +12 -3
- package/node_modules/@oxygen/workflows/dist/graph/topology.d.ts +9 -0
- package/node_modules/@oxygen/workflows/dist/graph/topology.js +33 -1
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +123 -16
- package/node_modules/@oxygen/workflows/dist/graph/types.js +45 -0
- package/node_modules/@oxygen/workflows/dist/index.d.ts +94 -2
- package/node_modules/@oxygen/workflows/dist/index.js +169 -26
- package/node_modules/@oxygen/workflows/dist/portable.js +3 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
9
9
|
import { Command, CommanderError, Option } from "commander";
|
|
10
10
|
import { applyOxygenHelp } from "./help.js";
|
|
11
11
|
import { buildCommandManifest, getCommandManifestEntry, searchCommandManifest, suggestCommandNames, } from "./command-manifest.js";
|
|
12
|
-
import { AGENCY_DIRECTORY_REGIONS, AGENCY_DIRECTORY_SERVICES, COLLAB_GATE_KINDS, COLLAB_GATE_PROSE, COLLAB_SUBJECT_KINDS, COLLAB_SUBJECT_KINDS_PROSE, COLLAB_SUBJECT_LABELS, COLLAB_SUBJECT_PROSE, GATE_KIND_SUBJECTS, describeWorkflowStatusChange, formatCellForDisplay, formatPublicBudgetScopes, SUBJECT_PATH_FORMS_PROSE, formatSubjectPath, exitCodeForOxygenError, parseSubjectPath, parseSubjectRef, parseWorkflowStatusChange, isVersionGreater, isVersionLess, MAX_MCP_TOOL_NAME_LENGTH, OXYGEN_CAPABILITY_ROUTES, OXYGEN_VERSION, OxygenError, getCapabilityRouteMatch,
|
|
12
|
+
import { AGENCY_DIRECTORY_REGIONS, AGENCY_DIRECTORY_SERVICES, COLLAB_GATE_KINDS, COLLAB_GATE_PROSE, COLLAB_SUBJECT_KINDS, COLLAB_SUBJECT_KINDS_PROSE, COLLAB_SUBJECT_LABELS, COLLAB_SUBJECT_PROSE, GATE_KIND_SUBJECTS, describeWorkflowStatusChange, formatCellForDisplay, formatPublicBudgetScopes, SUBJECT_PATH_FORMS_PROSE, formatSubjectPath, exitCodeForOxygenError, parseSubjectPath, parseSubjectRef, parseWorkflowStatusChange, isVersionGreater, isVersionLess, MAX_MCP_TOOL_NAME_LENGTH, OXYGEN_CAPABILITY_ROUTES, OXYGEN_VERSION, OxygenError, getCapabilityRouteMatch, inferUserCapabilityRoute, parseKnowledgePageMarkdown, PLAN_LIMITS, serializeCapabilityRoute, sleep, success, TAG_KINDS_PROSE, toFailure, workflowMcpToolName, } from "@oxygen/shared";
|
|
13
13
|
import { TAG_COLORS } from "@oxygen/shared/select-options";
|
|
14
14
|
import { inferImportColumnLabels, inferRowsFileFormat, normalizeImportColumnKey, normalizeRowsForNewTable, normalizeRowsFormat, parseRowsFileBuffer, parseXlsxWorkbookBuffer, } from "@oxygen/shared/file-import";
|
|
15
15
|
import { MAILBOX_IMPORT_FILE_MAX_BYTES as SHARED_MAILBOX_IMPORT_FILE_MAX_BYTES, MAILBOX_IMPORT_ROW_LIMIT as SHARED_MAILBOX_IMPORT_ROW_LIMIT, normalizeMailboxImportFile as normalizeSharedMailboxImportFile, normalizeMailboxImportVendor as normalizeSharedMailboxImportVendor, normalizeMailboxWorkbookRows, parseMailboxImportText, summarizeMailboxImportValidation as summarizeSharedMailboxImportValidation, } from "@oxygen/shared/mailbox-import";
|
|
@@ -736,6 +736,17 @@ function formatWorkspaceEventsList(data) {
|
|
|
736
736
|
lines.push(` trigger: ${source}.${eventName} · ${workspaceEventFilterText(event)}`);
|
|
737
737
|
if (typeof event.description === "string")
|
|
738
738
|
lines.push(` ${event.description}`);
|
|
739
|
+
const readiness = isRecord(event.readiness) ? event.readiness : null;
|
|
740
|
+
if (readiness?.status === "needs_setup") {
|
|
741
|
+
const reason = typeof readiness.reason === "string" ? readiness.reason : "Setup is required before this event can fire.";
|
|
742
|
+
lines.push(` readiness: setup required — ${reason}`);
|
|
743
|
+
const action = isRecord(readiness.action) ? readiness.action : null;
|
|
744
|
+
if (action && typeof action.command === "string")
|
|
745
|
+
lines.push(` next: ${action.command}`);
|
|
746
|
+
}
|
|
747
|
+
else if (readiness?.status === "ready") {
|
|
748
|
+
lines.push(" readiness: ready");
|
|
749
|
+
}
|
|
739
750
|
}
|
|
740
751
|
}
|
|
741
752
|
if (events.length > WORKSPACE_EVENTS_HUMAN_LIMIT) {
|
|
@@ -760,6 +771,15 @@ function formatWorkspaceEventDetail(data) {
|
|
|
760
771
|
lines.push(`Kind: ${event.kind}`);
|
|
761
772
|
if (typeof event.description === "string")
|
|
762
773
|
lines.push(`When it fires: ${event.description}`);
|
|
774
|
+
const readiness = isRecord(event.readiness) ? event.readiness : null;
|
|
775
|
+
if (readiness?.status === "ready")
|
|
776
|
+
lines.push("Readiness: ready");
|
|
777
|
+
if (readiness?.status === "needs_setup") {
|
|
778
|
+
lines.push(`Readiness: setup required${typeof readiness.reason === "string" ? ` — ${readiness.reason}` : ""}`);
|
|
779
|
+
const action = isRecord(readiness.action) ? readiness.action : null;
|
|
780
|
+
if (action && typeof action.command === "string")
|
|
781
|
+
lines.push(`Next: ${action.command}`);
|
|
782
|
+
}
|
|
763
783
|
if (isRecord(data.trigger)) {
|
|
764
784
|
lines.push("", "Trigger:", ...JSON.stringify(data.trigger, null, 2).split("\n").map((line) => ` ${line}`));
|
|
765
785
|
}
|
|
@@ -2487,7 +2507,7 @@ export function createProgram() {
|
|
|
2487
2507
|
const query = queryParts.join(" ");
|
|
2488
2508
|
return {
|
|
2489
2509
|
query,
|
|
2490
|
-
route: serializeCapabilityRoute(
|
|
2510
|
+
route: serializeCapabilityRoute(inferUserCapabilityRoute(query)),
|
|
2491
2511
|
hint: "Hydrate one exact CLI command with `oxygen commands get <exact-command> --json`, or one MCP tool with `oxygen_capabilities_schema`.",
|
|
2492
2512
|
};
|
|
2493
2513
|
});
|
|
@@ -2695,7 +2715,108 @@ export function createProgram() {
|
|
|
2695
2715
|
}));
|
|
2696
2716
|
}), { hidden: true })
|
|
2697
2717
|
.addCommand(new Command("admin")
|
|
2698
|
-
.description("
|
|
2718
|
+
.description("Operate the live Plain support queue as OXYGEN staff. Public replies are previewed first and sent as the shared Oxygen Support identity.")
|
|
2719
|
+
.addCommand(new Command("list")
|
|
2720
|
+
.description("List live Plain Threads across customer Tenants (staff only).")
|
|
2721
|
+
.option("--status <status>", "Filter by status.")
|
|
2722
|
+
.option("--limit <n>", "Max tickets to return.")
|
|
2723
|
+
.option("--json", "Print a JSON envelope.")
|
|
2724
|
+
.action(async (options) => {
|
|
2725
|
+
await handleAsyncAction("support admin list", options, () => requestOxygen(withSupportListQuery("/api/cli/admin/support/tickets", options)));
|
|
2726
|
+
}))
|
|
2727
|
+
.addCommand(new Command("get")
|
|
2728
|
+
.description("Show one live Plain Thread, its routing metadata, and customer-visible messages (staff only).")
|
|
2729
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2730
|
+
.option("--json", "Print a JSON envelope.")
|
|
2731
|
+
.action(async (ticketId, options) => {
|
|
2732
|
+
await handleAsyncAction("support admin get", options, () => requestOxygen(`/api/cli/admin/support/tickets/${encodeURIComponent(ticketId)}`));
|
|
2733
|
+
}))
|
|
2734
|
+
.addCommand(new Command("open")
|
|
2735
|
+
.description("Return the exact Plain Inbox URL for one live Thread (staff only).")
|
|
2736
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2737
|
+
.option("--json", "Print a JSON envelope.")
|
|
2738
|
+
.action(async (ticketId, options) => {
|
|
2739
|
+
await handleAsyncAction("support admin open", options, () => requestOxygen(`/api/cli/admin/support/tickets/${encodeURIComponent(ticketId)}`)
|
|
2740
|
+
.then((data) => supportAdminOpenResult(data)));
|
|
2741
|
+
}))
|
|
2742
|
+
.addCommand(new Command("claim")
|
|
2743
|
+
.description("Claim a live Plain Thread and move it to In progress without overwriting another assignee (staff only).")
|
|
2744
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2745
|
+
.option("--agent", "Claim as the authenticated Oxygen Support machine user instead of the signed-in human.")
|
|
2746
|
+
.option("--json", "Print a JSON envelope.")
|
|
2747
|
+
.action(async (ticketId, options) => {
|
|
2748
|
+
await handleAsyncAction("support admin claim", options, () => requestOxygen(`/api/cli/admin/support/tickets/${encodeURIComponent(ticketId)}/update`, {
|
|
2749
|
+
method: "POST",
|
|
2750
|
+
body: { action: "claim", as_machine_user: options.agent === true },
|
|
2751
|
+
}));
|
|
2752
|
+
}))
|
|
2753
|
+
.addCommand(new Command("priority")
|
|
2754
|
+
.description("Set the native Plain priority for a live Thread (staff only).")
|
|
2755
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2756
|
+
.requiredOption("--priority <priority>", "urgent | high | normal | low")
|
|
2757
|
+
.option("--json", "Print a JSON envelope.")
|
|
2758
|
+
.action(async (ticketId, options) => {
|
|
2759
|
+
await handleSupportAdminUpdateRequest("priority", ticketId, options, {
|
|
2760
|
+
priority: readOption(options.priority),
|
|
2761
|
+
});
|
|
2762
|
+
}))
|
|
2763
|
+
.addCommand(new Command("label-add")
|
|
2764
|
+
.description("Add an active Plain label by external ID (staff only).")
|
|
2765
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2766
|
+
.requiredOption("--label <externalId>", "Plain label external ID, for example oxygen_request_bug.")
|
|
2767
|
+
.option("--json", "Print a JSON envelope.")
|
|
2768
|
+
.action(async (ticketId, options) => {
|
|
2769
|
+
await handleSupportAdminUpdateRequest("label_add", ticketId, options, {
|
|
2770
|
+
label_external_id: readOption(options.label),
|
|
2771
|
+
});
|
|
2772
|
+
}))
|
|
2773
|
+
.addCommand(new Command("label-remove")
|
|
2774
|
+
.description("Remove a Plain label by external ID (staff only).")
|
|
2775
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2776
|
+
.requiredOption("--label <externalId>", "Plain label external ID.")
|
|
2777
|
+
.option("--json", "Print a JSON envelope.")
|
|
2778
|
+
.action(async (ticketId, options) => {
|
|
2779
|
+
await handleSupportAdminUpdateRequest("label_remove", ticketId, options, {
|
|
2780
|
+
label_external_id: readOption(options.label),
|
|
2781
|
+
});
|
|
2782
|
+
}))
|
|
2783
|
+
.addCommand(new Command("note")
|
|
2784
|
+
.description("Add a team-only internal note to a live Plain Thread (staff only).")
|
|
2785
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2786
|
+
.requiredOption("--body <body>", "Internal note body.")
|
|
2787
|
+
.option("--json", "Print a JSON envelope.")
|
|
2788
|
+
.action(async (ticketId, options) => {
|
|
2789
|
+
await handleSupportAdminUpdateRequest("note", ticketId, options, {
|
|
2790
|
+
body: readOption(options.body),
|
|
2791
|
+
});
|
|
2792
|
+
}))
|
|
2793
|
+
.addCommand(new Command("draft")
|
|
2794
|
+
.description("Stage a reply as a team-only Plain note; this never sends to the customer (staff and agents).")
|
|
2795
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2796
|
+
.requiredOption("--body <body>", "Proposed customer reply.")
|
|
2797
|
+
.option("--json", "Print a JSON envelope.")
|
|
2798
|
+
.action(async (ticketId, options) => {
|
|
2799
|
+
await handleSupportAdminUpdateRequest("draft", ticketId, options, {
|
|
2800
|
+
body: readOption(options.body),
|
|
2801
|
+
});
|
|
2802
|
+
}))
|
|
2803
|
+
.addCommand(new Command("reply")
|
|
2804
|
+
.description("Preview or send a public reply on the Thread's native Plain channel (human staff only; sent as Oxygen Support).")
|
|
2805
|
+
.argument("<ticketId>", "Plain Thread ID (th_...).")
|
|
2806
|
+
.requiredOption("--body <body>", "Customer-visible reply body.")
|
|
2807
|
+
.option("--confirm-ref <ref>", "Send only when this exactly matches the previewed Plain ref (for example T-10). Omit to preview without sending.")
|
|
2808
|
+
.option("--json", "Print a JSON envelope.")
|
|
2809
|
+
.action(async (ticketId, options) => {
|
|
2810
|
+
await handleAsyncAction("support admin reply", options, () => requestOxygen(`/api/cli/admin/support/tickets/${encodeURIComponent(ticketId)}/messages`, {
|
|
2811
|
+
method: "POST",
|
|
2812
|
+
body: {
|
|
2813
|
+
body: readOption(options.body),
|
|
2814
|
+
...(readOption(options.confirmRef)
|
|
2815
|
+
? { confirm_ref: readOption(options.confirmRef) }
|
|
2816
|
+
: {}),
|
|
2817
|
+
},
|
|
2818
|
+
}));
|
|
2819
|
+
}))
|
|
2699
2820
|
.addCommand(new Command("file")
|
|
2700
2821
|
.description("Retired legacy write; always returns the Plain-cutover error (staff only).")
|
|
2701
2822
|
.requiredOption("--organization <organization>", "Target organization id or slug the ticket belongs to.")
|
|
@@ -2721,15 +2842,7 @@ export function createProgram() {
|
|
|
2721
2842
|
organization: readOption(options.organization),
|
|
2722
2843
|
},
|
|
2723
2844
|
}));
|
|
2724
|
-
}))
|
|
2725
|
-
.addCommand(new Command("list")
|
|
2726
|
-
.description("Read the legacy OXYGEN ticket archive across organizations; Plain is the live queue (staff only).")
|
|
2727
|
-
.option("--status <status>", "Filter by status.")
|
|
2728
|
-
.option("--limit <n>", "Max tickets to return.")
|
|
2729
|
-
.option("--json", "Print a JSON envelope.")
|
|
2730
|
-
.action(async (options) => {
|
|
2731
|
-
await handleAsyncAction("support admin list", options, () => requestOxygen(withSupportListQuery("/api/cli/admin/support/tickets", options)));
|
|
2732
|
-
}))
|
|
2845
|
+
}), { hidden: true })
|
|
2733
2846
|
.addCommand(new Command("events")
|
|
2734
2847
|
.description("Read-only poll of legacy OXYGEN archive events for cutover compatibility; never use it as the live queue (staff only).")
|
|
2735
2848
|
.option("--after <cursor>", "If has_more=true, pass next_cursor to continue that scan. If false, persist the non-null watermark_cursor for later polls. A watermark poll intentionally replays the settled 24h window, not only unseen events; dedupe every page by (ticket_ref, version).")
|
|
@@ -2752,26 +2865,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
2752
2865
|
`)
|
|
2753
2866
|
.action(async (options) => {
|
|
2754
2867
|
await handleAsyncAction("support admin events", options, () => requestOxygen(withSupportEventsQuery("/api/cli/admin/support/events", options)));
|
|
2755
|
-
}))
|
|
2756
|
-
.addCommand(new Command("get")
|
|
2757
|
-
.description("Show one legacy archived ticket snapshot; Plain is authoritative for live cases (staff only).")
|
|
2758
|
-
.argument("<ticketId>", "Ticket UUID.")
|
|
2759
|
-
.option("--if-version <version>", "Require the exact decimal version returned by support admin events; stale versions return a conflict without exposing the thread.")
|
|
2760
|
-
.option("--json", "Print a JSON envelope.")
|
|
2761
|
-
.action(async (ticketId, options) => {
|
|
2762
|
-
await handleAsyncAction("support admin get", options, () => requestOxygen(withSupportAdminGetQuery(`/api/cli/admin/support/tickets/${encodeURIComponent(ticketId)}`, options)));
|
|
2763
|
-
}))
|
|
2764
|
-
.addCommand(new Command("reply")
|
|
2765
|
-
.description("Retired legacy write; always returns the Plain-cutover error.")
|
|
2766
|
-
.argument("<ticketId>", "Ticket UUID.")
|
|
2767
|
-
.requiredOption("--body <body>", "Reply body.")
|
|
2768
|
-
.option("--json", "Print a JSON envelope.")
|
|
2769
|
-
.action(async (ticketId, options) => {
|
|
2770
|
-
await handleAsyncAction("support admin reply", options, () => requestOxygen(`/api/cli/admin/support/tickets/${encodeURIComponent(ticketId)}/messages`, {
|
|
2771
|
-
method: "POST",
|
|
2772
|
-
body: { body: readOption(options.body) },
|
|
2773
|
-
}));
|
|
2774
|
-
}))
|
|
2868
|
+
}), { hidden: true })
|
|
2775
2869
|
.addCommand(new Command("resolve")
|
|
2776
2870
|
.description("Retired legacy write; always returns the Plain-cutover error (staff only).")
|
|
2777
2871
|
.argument("<ticketId>", "Ticket UUID.")
|
|
@@ -2782,7 +2876,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
2782
2876
|
method: "POST",
|
|
2783
2877
|
body: { resolution: readOption(options.resolution) },
|
|
2784
2878
|
}));
|
|
2785
|
-
}))
|
|
2879
|
+
}), { hidden: true })
|
|
2786
2880
|
.addCommand(new Command("workflow")
|
|
2787
2881
|
.description("Retired legacy write; Plain notes and downstream engineering work own triage (staff only).")
|
|
2788
2882
|
.argument("<ticketId>", "Ticket UUID.")
|
|
@@ -2795,7 +2889,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
2795
2889
|
.option("--json", "Print a JSON envelope.")
|
|
2796
2890
|
.action(async (ticketId, options) => {
|
|
2797
2891
|
await handleSupportAdminWorkflowAction(ticketId, options);
|
|
2798
|
-
}))
|
|
2892
|
+
}), { hidden: true })
|
|
2799
2893
|
.addCommand(new Command("update")
|
|
2800
2894
|
.description("Retired legacy write; Plain owns status, assignee, and notes (staff only).")
|
|
2801
2895
|
.argument("<ticketId>", "Ticket UUID.")
|
|
@@ -2806,7 +2900,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
2806
2900
|
.option("--json", "Print a JSON envelope.")
|
|
2807
2901
|
.action(async (ticketId, options) => {
|
|
2808
2902
|
await handleSupportAdminUpdateAction(ticketId, options);
|
|
2809
|
-
})
|
|
2903
|
+
}), { hidden: true }));
|
|
2810
2904
|
program
|
|
2811
2905
|
.command("feedback")
|
|
2812
2906
|
.description("Send feedback or a bug report to the OXYGEN team. This is the same immediate, zero-credit canonical Plain Thread write as `support file`; transcripts are never attached unless explicitly requested.")
|
|
@@ -9577,7 +9671,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
9577
9671
|
}));
|
|
9578
9672
|
program
|
|
9579
9673
|
.command("integrations")
|
|
9580
|
-
.description("
|
|
9674
|
+
.description("Connect third-party tools such as PostHog, Slack, and HubSpot, and manage raw provider-webhook subscriptions. Start with `integrations list` to confirm any provider is supported, see its auth mode, and distinguish free connection setup from per-action credit estimates. Trigger-ready workspace events live under `workflows events list`. Local mailbox exports use `mailboxes import`, not this group.")
|
|
9581
9675
|
.addCommand(new Command("events")
|
|
9582
9676
|
.description("Inspect raw provider event definitions and manage subscriptions that feed workflows; use `workflows events list` for the trigger-ready workspace catalog.")
|
|
9583
9677
|
.addCommand(new Command("list")
|
|
@@ -9680,9 +9774,10 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
9680
9774
|
await handleAsyncAction("integrations list", options, () => requestOxygen("/api/cli/integrations/composio/list"));
|
|
9681
9775
|
}))
|
|
9682
9776
|
.addCommand(new Command("connect")
|
|
9683
|
-
.description("Connect an integration. OAuth toolkits return a redirect URL; API-key integrations accept --api-key. Run without
|
|
9777
|
+
.description("Connect an integration. OAuth toolkits return a redirect URL; API-key integrations accept --api-key. Run without credentials first for a 0-credit preview of the provider's exact fields (mode: needs_api_key): no credential is submitted, nothing is stored, and no provider call is made. Use repeatable --credential name=value when a provider also requires a host, subdomain, or other named value. To keep a secret out of shell history and process arguments, open the preview's web_url and submit it in Connections; --api-key is for controlled noninteractive use. Credential submission creates or replaces the connection immediately and needs no separate --approved flag. LinkedIn and WhatsApp return a Unipile hosted-auth URL instead (`oxygen senders connect` / `oxygen whatsapp connect` are the canonical paths).")
|
|
9684
9778
|
.argument("<integration_id>", "Integration id, such as 'slack' or 'serpapi'.")
|
|
9685
|
-
.option("--api-key <value>", "API key for
|
|
9779
|
+
.option("--api-key <value>", "API key for controlled noninteractive use. Literal values can appear in shell history and process arguments; prefer the preview web_url for manual entry.")
|
|
9780
|
+
.option("--credential <name=value>", "Named provider credential field. Repeat for multi-field integrations (for example, PostHog: --credential subdomain=us).", collectRepeatable, [])
|
|
9686
9781
|
.option("--account-id <id>", "Provider account id for integrations that span multiple accounts (e.g. Cloudflare when the token can access more than one account).")
|
|
9687
9782
|
.option("--country <code>", "ISO 3166-1 alpha-2 country where the LinkedIn account owner normally signs in (required for `connect linkedin`; picks the egress proxy).")
|
|
9688
9783
|
.option("--pairing-phone-number <e164>", "WhatsApp only: pair by code sent to this E.164 number instead of showing a QR.")
|
|
@@ -9690,6 +9785,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
9690
9785
|
.action(async (integrationId, options) => {
|
|
9691
9786
|
await handleAsyncAction("integrations connect", options, () => {
|
|
9692
9787
|
const apiKey = readOption(options.apiKey)?.trim();
|
|
9788
|
+
const credentials = readNamedCredentials(options.credential);
|
|
9693
9789
|
const accountId = readOption(options.accountId);
|
|
9694
9790
|
const country = readOption(options.country)?.trim();
|
|
9695
9791
|
const pairingPhoneNumber = readOption(options.pairingPhoneNumber)?.trim();
|
|
@@ -9698,6 +9794,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
9698
9794
|
body: {
|
|
9699
9795
|
integration_id: integrationId,
|
|
9700
9796
|
...(apiKey ? { api_key: apiKey } : {}),
|
|
9797
|
+
...(credentials ? { credentials } : {}),
|
|
9701
9798
|
...(accountId ? { account_id: accountId } : {}),
|
|
9702
9799
|
...(country ? { country } : {}),
|
|
9703
9800
|
...(pairingPhoneNumber ? { pairing_phone_number: pairingPhoneNumber } : {}),
|
|
@@ -12761,7 +12858,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
12761
12858
|
});
|
|
12762
12859
|
})));
|
|
12763
12860
|
program.addCommand(new Command("managed-inboxes")
|
|
12764
|
-
.description("Managed sending inboxes bought through OXYGEN: subscribe a domain + N InboxKit mailboxes (google/microsoft/azure) as a recurring monthly Oxygen-credit purchase, add inboxes to a domain you already own, inspect/verify, and cancel. OXYGEN Warm-up defaults on when available: its quoted recurring credits and exact future addresses are part of the order approval, and the worker activates it after provisioning without a second warmup approval. Subscribe/add-inboxes/cancel are approval-gated (preview → re-run with --approved --quote). The inbox vendor is chosen for you; --vendor pins one.")
|
|
12861
|
+
.description("Managed sending inboxes bought through OXYGEN: subscribe a domain + N InboxKit mailboxes (google/microsoft/azure) as a recurring monthly Oxygen-credit purchase, add inboxes to a domain you already own, inspect/verify, and cancel. OXYGEN Warm-up defaults on when available: its quoted recurring credits and exact future addresses are part of the order approval, and the worker activates it after provisioning without a second warmup approval. Warm-up does not prove native-send authorization: verify each exact address with `mailboxes oauth-health --json`. Subscribe/add-inboxes/cancel are approval-gated (preview → re-run with --approved --quote). The inbox vendor is chosen for you; --vendor pins one.")
|
|
12765
12862
|
.addCommand(new Command("verify")
|
|
12766
12863
|
.description("Check that OXYGEN, STRIPE, and the VENDOR agree about what this org is buying. The truth about a managed inbox lives in three systems — what the customer asked for, what they are charged, and what is actually running — and a 200 from any one of them proves nothing. Reports every disagreement with WHO IS LOSING MONEY while it stands (customer_overbilled first, then oxygen_pays). Read-only, no writes, 0 Oxygen credits.")
|
|
12767
12864
|
.option("--json", "Print a JSON envelope.")
|
|
@@ -12775,7 +12872,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
12775
12872
|
await handleAsyncAction("managed-inboxes registrant", options, () => requestOxygen("/api/cli/managed-inboxes/registrant"));
|
|
12776
12873
|
}))
|
|
12777
12874
|
.addCommand(new Command("subscribe")
|
|
12778
|
-
.description("Subscribe a NEW domain + mailboxes as a managed monthly inbox subscription. WITHOUT --approved this prints a priced PREVIEW with a quote_id (nothing ordered, nothing charged); re-run with --approved --quote <id> to place the order. OXYGEN Warm-up defaults on for Google, Microsoft, and Azure when available: preview and approved responses carry `warmup_activation` with the exact `mailboxes` and `scope=warmup_only`; it is null when warmup is disabled or unavailable. For InboxKit,
|
|
12875
|
+
.description("Subscribe a NEW domain + mailboxes as a managed monthly inbox subscription. WITHOUT --approved this prints a priced PREVIEW with a quote_id (nothing ordered, nothing charged); re-run with --approved --quote <id> to place the order. OXYGEN Warm-up defaults on for Google, Microsoft, and Azure when available: preview and approved responses carry `warmup_activation` with the exact `mailboxes` and `scope=warmup_only`; it is null when warmup is disabled or unavailable. For InboxKit, Google carries `transport=inboxkit_managed_google_handoff`; Microsoft/Azure carries `transport=inboxkit_sequencer_export`. Both carry `mailbox_credentials_required=false` and `native_send_oauth_separate=true`, so managed warm-up needs no customer-supplied OAuth or mailbox password. Google retrieves the existing managed credential only during activation and never persists or returns it; Microsoft/Azure uses native export. Native sending authorization is separate and may still be required before OXYGEN can send. Verify each exact address with `mailboxes oauth-health --json`; InboxKit's unattended Google domain approval usually lands within minutes and the worker keeps retrying, so a waiting row is not a reason to ask the customer to sign in. This order authorizes automatic OXYGEN Warm-up activation after provisioning, so no second `mailboxes warmup enable` approval is needed. Prices come from the vendor's LIVE rate card and LIVE domain price, and are refused if they sit below vendor cost. To add mailboxes to a domain you ALREADY own use `managed-inboxes add-inboxes` — this command always registers a new domain and fails on one you own.")
|
|
12779
12876
|
.argument("[domain]", "Sending domain to register + host the mailboxes (e.g. send.acme.com). May also be passed as --domain.")
|
|
12780
12877
|
.option("--domain <domain>", "Sending domain (alternative to the positional argument).")
|
|
12781
12878
|
.requiredOption("--provider <provider>", "Mailbox PLATFORM: google, microsoft, or azure. (google/microsoft cap at 5 mailboxes per domain; azure allows 100.)")
|
|
@@ -12842,7 +12939,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
12842
12939
|
});
|
|
12843
12940
|
}))
|
|
12844
12941
|
.addCommand(new Command("add-inboxes")
|
|
12845
|
-
.description("Add mailboxes to a managed domain you ALREADY own — no new domain is registered and there is NO domain registration charge, only the extra inboxes' monthly rate (plus their add-ons). WITHOUT --approved this prints a priced PREVIEW with a quote_id and orders nothing; re-run with --approved --quote <id> to place the order. When the standing OXYGEN Warm-up add-on is enabled and available, preview and approved responses carry `warmup_activation` for only the exact newly added `mailboxes`, with `scope=warmup_only`; it is null when warmup is disabled or unavailable. For InboxKit,
|
|
12942
|
+
.description("Add mailboxes to a managed domain you ALREADY own — no new domain is registered and there is NO domain registration charge, only the extra inboxes' monthly rate (plus their add-ons). WITHOUT --approved this prints a priced PREVIEW with a quote_id and orders nothing; re-run with --approved --quote <id> to place the order. When the standing OXYGEN Warm-up add-on is enabled and available, preview and approved responses carry `warmup_activation` for only the exact newly added `mailboxes`, with `scope=warmup_only`; it is null when warmup is disabled or unavailable. For InboxKit, Google carries `transport=inboxkit_managed_google_handoff`; Microsoft/Azure carries `transport=inboxkit_sequencer_export`. Both carry `mailbox_credentials_required=false` and `native_send_oauth_separate=true`, so managed warm-up needs no customer-supplied OAuth or mailbox password. Google retrieves the existing managed credential only during activation and never persists or returns it; Microsoft/Azure uses native export. Native sending authorization is separate and may still be required before OXYGEN can send. Verify each exact address with `mailboxes oauth-health --json`; InboxKit's unattended Google domain approval usually lands within minutes and the worker keeps retrying, so a waiting row is not a reason to ask the customer to sign in. The quote authorizes automatic OXYGEN Warm-up activation after provisioning, so do not run a second warmup approval. Capped per domain by the platform the domain was bought on (5 google/microsoft, 100 azure) COUNTING the inboxes already on it. To register a NEW domain use `managed-inboxes subscribe` instead.")
|
|
12846
12943
|
.argument("[domain]", "A managed domain this workspace already owns (e.g. send.acme.com). May also be passed as --domain.")
|
|
12847
12944
|
.option("--domain <domain>", "The managed inbox domain (alternative to the positional argument).")
|
|
12848
12945
|
.option("--mailboxes <json>", "JSON array of mailboxes: [{\"username\",\"first_name\",\"last_name\",\"profile_picture_url\"}]. The vendor stamps the names on each mailbox, so real names belong here. profile_picture_url is optional and must be a PUBLIC, PERMANENT https URL the vendor can fetch at provisioning; `oxygen managed-inboxes upload-avatar <path>` returns one.")
|
|
@@ -13005,7 +13102,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
13005
13102
|
});
|
|
13006
13103
|
})));
|
|
13007
13104
|
program.addCommand(new Command("mailboxes")
|
|
13008
|
-
.description("Native email sending pool: register/refresh Google/Microsoft mailboxes (including secure local credential-file transfer), pause/disable inboxes, connect EmailGuard monitoring, and inspect/control OXYGEN Warm-up. Fresh managed InboxKit Google/Microsoft/Azure orders activate exact
|
|
13105
|
+
.description("Native email sending pool: register/refresh Google/Microsoft mailboxes (including secure local credential-file transfer), pause/disable inboxes, connect EmailGuard monitoring, and inspect/control OXYGEN Warm-up. Fresh managed InboxKit Google/Microsoft/Azure orders activate exact-scope warm-up automatically after provisioning under their approved default-on add-on. Google reuses the managed credential just in time; Microsoft/Azure uses native export. Standalone warm-up plans and credit caps are for BYOK/imported mailboxes, managed opt-outs, or later separate enrollment. OXYGEN Warm-up never owns campaign dispatch. Safe import preflight: run `oxygen mailboxes compatibility --catalog-only --json`, then `oxygen mailboxes import --file <path> --validate-only --json` (add the credential source flags shown by import help when the file contains credentials).")
|
|
13009
13106
|
.addCommand(new Command("list")
|
|
13010
13107
|
.description("List the org's sending mailboxes with provider, status, warmup state, source (managed = bought through Oxygen, byok = bring-your-own), and a pool overview (including counts by source). To see only Google/Microsoft mailboxes that still need OAuth connection and the right remedy for each, use `oxygen mailboxes oauth-health --json`.")
|
|
13011
13108
|
.option("--status <status>", "Filter by status: active, paused, or disabled.")
|
|
@@ -13032,7 +13129,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
13032
13129
|
await handleAsyncAction("mailboxes get", options, () => requestOxygen(`/api/cli/mailboxes/${encodeURIComponent(mailbox)}`));
|
|
13033
13130
|
}))
|
|
13034
13131
|
.addCommand(new Command("delete")
|
|
13035
|
-
.description("Preview or approve removal of exact mailboxes from Oxygen at 0 credits. Deletion immediately removes them from sending but never deletes the underlying Google Workspace or Microsoft 365 accounts. It preserves conversation/message/delivery history and stops
|
|
13132
|
+
.description("Preview or approve removal of exact mailboxes from Oxygen at 0 credits. Deletion immediately removes them from sending but never deletes the underlying Google Workspace or Microsoft 365 accounts. It preserves conversation/message/delivery history and stops any separately listed 1,000-credit connected-mailbox commitment for future renewals; that line is currently built but not charged, and it is not the 3,000-credit OXYGEN Warm-up subscription. The current period is not refunded. Listed active/paused sequences keep their status but lose these senders and are not automatically paused. Managed mailboxes and live warmup/monitoring add-ons fail closed with their exact address-scoped teardown steps.")
|
|
13036
13133
|
.requiredOption("--mailboxes <list>", "Comma-separated mailbox ids or addresses (maximum 500).")
|
|
13037
13134
|
.option("--approved", "Execute the fresh preview. Requires --plan-hash and --confirmation.")
|
|
13038
13135
|
.option("--plan-hash <hash>", "Fresh preview plan_hash.")
|
|
@@ -13089,7 +13186,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
13089
13186
|
});
|
|
13090
13187
|
}))
|
|
13091
13188
|
.addCommand(new Command("import")
|
|
13092
|
-
.description("Register (or refresh) sending mailboxes in bulk. Use --validate-only with a local file for a non-mutating, no-network preflight; without it, import is a 0-credit state mutation whose upsert is idempotent by mailbox address. CSV/JSON/JSONL/XLSX identity files from any vendor use --file plus optional --vendor. Compatible Google app-password exports use --from credentials --vendor <source>. Connected Zapmail inventories use --from zapmail. In credential files, app_password is Google-only; Microsoft password fields are discarded locally. OAuth grants, MFA/TOTP seeds, and delegation keys never transfer: fresh managed InboxKit Google
|
|
13189
|
+
.description("Register (or refresh) sending mailboxes in bulk. Use --validate-only with a local file for a non-mutating, no-network preflight; without it, import is a 0-credit state mutation whose upsert is idempotent by mailbox address. CSV/JSON/JSONL/XLSX identity files from any vendor use --file plus optional --vendor. Compatible Google app-password exports use --from credentials --vendor <source>. Connected Zapmail inventories use --from zapmail. In credential files, app_password is Google-only; Microsoft password fields are discarded locally. OAuth grants, MFA/TOTP seeds, and delegation keys never transfer from customer files: fresh managed InboxKit Google orders reuse the vendor-held credential just in time for automatic warmup, while fresh managed Microsoft/Azure orders use native InboxKit Sequencer export; opted-out/separate managed enrollment uses standalone `warmup enable`; eligible non-InboxKit Microsoft warmup uses the Outlook OAuth fallback (`mailboxes warmup microsoft`); EmailGuard remains vendor_blocked for Microsoft. Google app passwords supplied through an authorized credential import are sent only in the request body, encrypted server-side, and never returned.")
|
|
13093
13190
|
.addHelpText("after", [
|
|
13094
13191
|
"",
|
|
13095
13192
|
"Ordinary identity file contract (CSV / JSON / JSONL / XLSX):",
|
|
@@ -13300,7 +13397,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
13300
13397
|
});
|
|
13301
13398
|
}))
|
|
13302
13399
|
.addCommand(new Command("connect-oauth")
|
|
13303
|
-
.description("Connect Google/Microsoft mailboxes to OXYGEN native send. Preview by default; pass --approved only after reviewing exact candidates. --vendor oxygen requires an exact address list and never discovers a domain directory. Microsoft supports --authorization-mode tenant (one administrator approval, up to 500 exact selected addresses, recommended for dedicated sending tenants) or individual (one account sign-in per mailbox, max 10 per run, recommended for ordinary company/personal mailboxes). Microsoft's application grant is tenant-wide at the provider; OXYGEN—not Microsoft—enforces the selected-address boundary at verification and send time unless the tenant separately configures Exchange Application RBAC. Connecting authorization does not change whether a mailbox is active or disabled. Google uses individual OAuth. Source tokens, passwords, authenticator seeds, and one-time codes never transfer. Managed authorization paths remain available through their vendor options. All paths cost 0 Oxygen credits.")
|
|
13400
|
+
.description("Connect Google/Microsoft mailboxes to OXYGEN native send. Preview by default; pass --approved only after reviewing exact candidates. --vendor oxygen requires an exact address list and never discovers a domain directory. Microsoft supports --authorization-mode tenant (one administrator approval, up to 500 exact selected addresses, recommended for dedicated sending tenants) or individual (one account sign-in per mailbox, max 10 per run, recommended for ordinary company/personal mailboxes). For Microsoft's built-in roles, tenant mode requires a Global Administrator or Privileged Role Administrator because it requests the Microsoft Graph application Mail.Send and Mail.Read permissions; Application Administrator and Cloud Application Administrator cannot consent to Microsoft Graph application roles. Microsoft's application grant is tenant-wide at the provider; OXYGEN—not Microsoft—enforces the selected-address boundary at verification and send time unless the tenant separately configures Exchange Application RBAC. For non-managed Microsoft mailboxes, native-send approval does not authorize OXYGEN Warm-up: every mailbox still needs its own `mailboxes warmup microsoft` Outlook consent before standalone enrollment. Connecting authorization does not change whether a mailbox is active or disabled. Google uses individual OAuth. Source tokens, passwords, authenticator seeds, and one-time codes never transfer. Managed authorization paths remain available through their vendor options. All paths cost 0 Oxygen credits.")
|
|
13304
13401
|
.addHelpText("after", "\nDocs: https://oxygen-agent.com/docs/providers/mailboxes\n")
|
|
13305
13402
|
.option("--provider <provider>", "Mailbox provider to provision: google or microsoft.")
|
|
13306
13403
|
.option("--vendor <vendor>", "Authorization path: oxygen for imported/manual mailboxes, zapmail (default), or inboxkit.")
|
|
@@ -13440,10 +13537,10 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
13440
13537
|
});
|
|
13441
13538
|
}))
|
|
13442
13539
|
.addCommand(new Command("warmup")
|
|
13443
|
-
.description("OXYGEN Warm-up for the sending pool — the weeks of low-volume conversational mail that make a new inbox look trusted before you cold-send from it. It is managed and credit-billed at 3,000 credits per warming inbox per month ($3). Fresh managed InboxKit Google/Microsoft/Azure orders with the default-on warm-up add-on activate automatically after provisioning under the approved order quote; do not run a second `warmup enable`. Standalone preview → approval is for BYOK/imported mailboxes, a managed opt-out, or later separate enrollment. InboxKit auto-export stays off because Oxygen submits only the exact authorized UIDs, and any non-cancelled InboxKit warm-up blocks
|
|
13540
|
+
.description("OXYGEN Warm-up for the sending pool — the weeks of low-volume conversational mail that make a new inbox look trusted before you cold-send from it. It is managed and credit-billed at 3,000 credits per warming inbox per month ($3). Fresh managed InboxKit Google/Microsoft/Azure orders with the default-on warm-up add-on activate automatically after provisioning under the approved order quote; do not run a second `warmup enable`. Google reuses the InboxKit-held credential only during activation and never stores or returns it; Microsoft/Azure uses exact native export. Standalone preview → approval is for BYOK/imported mailboxes, a managed opt-out, or later separate enrollment. InboxKit auto-export stays off for Microsoft/Azure because Oxygen submits only the exact authorized UIDs, and any non-cancelled InboxKit warm-up blocks either handoff so one mailbox cannot warm twice. OXYGEN Warm-up never owns campaign dispatch: native OXYGEN Sequences still own campaigns and sends. Eligible non-InboxKit Microsoft mailboxes use the separate `mailboxes warmup microsoft` OAuth fallback. Inboxes already warming on the retired TrulyInbox rail keep reporting and are wound down there with `warmup disable`; they are never silently moved to the current managed rail.")
|
|
13444
13541
|
.addHelpText("after", "\nDocs: https://oxygen-agent.com/docs/providers/mailboxes\n")
|
|
13445
13542
|
.addCommand(new Command("enable")
|
|
13446
|
-
.description("STANDALONE ENROLLMENT: enroll BYOK/imported mailboxes, a managed order that opted out, or mailboxes being added to warmup later. Fresh managed InboxKit Google/Microsoft/Azure orders whose approved quote included warmup activate automatically after provisioning and do not need this command. Targets the whole pool unless --mailboxes is given; one synchronous preview/approval is capped at 220 mailboxes, so split larger pools into explicit batches. Without --approved this is a 0-credit, no-provider-write priced preview: nothing is
|
|
13543
|
+
.description("STANDALONE ENROLLMENT: enroll BYOK/imported mailboxes, a managed order that opted out, or mailboxes being added to warmup later. Fresh managed InboxKit Google/Microsoft/Azure orders whose approved quote included warmup activate automatically after provisioning and do not need this command. Targets the whole pool unless --mailboxes is given; one synchronous preview/approval is capped at 220 mailboxes, so split larger pools into explicit batches. Without --approved this is a 0-credit, no-provider-write priced preview: nothing is handed off, enrolled, or billed. The current public price is 3,000 credits per warming mailbox-month; the preview returns the exact first-cycle ceiling. Preview one inbox with `oxygen mailboxes warmup enable --mailboxes sender@example.com --json`. Execute by echoing its --plan and --max-credits with --approved. Eligible managed Google targets use the InboxKit-held credential just in time without storing or returning it; Microsoft/Azure targets use native InboxKit Sequencer export with exact UIDs and auto-export off. Any non-cancelled InboxKit warmup must be cancelled before either handoff (pausing is not enough). Only eligible non-InboxKit Microsoft mailboxes need `mailboxes warmup microsoft`. OXYGEN Warm-up retains no campaign authority — OXYGEN Sequences own enrollment and dispatch. New enrollments only ever land on the managed OXYGEN rail; the retired TrulyInbox rail is refused here and only accepts teardown.")
|
|
13447
13544
|
.addHelpText("after", "\nDocs: https://oxygen-agent.com/docs/providers/mailboxes\n")
|
|
13448
13545
|
.option("--mailboxes <list>", "Comma-separated mailbox ids or addresses to warm. Omit to warm the whole pool.")
|
|
13449
13546
|
.option("--approved", "Execute the PRICED enrollment. Without it the response is a priced preview and nothing is enrolled or billed.")
|
|
@@ -13831,7 +13928,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
13831
13928
|
}));
|
|
13832
13929
|
})))
|
|
13833
13930
|
.addCommand(new Command("sync")
|
|
13834
|
-
.description("Refresh
|
|
13931
|
+
.description("Refresh Cloudflare-backed domains only: zone pages, registrar metadata, RDAP age backfill, and DNS health. Vendor-managed bundle domains are skipped here and read live with `domains get` / `domains dns`. Partial (paginated) syncs auto-continue until complete.")
|
|
13835
13932
|
.option("--full", "Restart the sweep from the first zone page instead of resuming the cursor.")
|
|
13836
13933
|
.option("--json", "Print a JSON envelope.")
|
|
13837
13934
|
.action(async (options) => {
|
|
@@ -14251,7 +14348,7 @@ Run completion:
|
|
|
14251
14348
|
});
|
|
14252
14349
|
}))
|
|
14253
14350
|
.addCommand(new Command("apply")
|
|
14254
|
-
.description("Compile and
|
|
14351
|
+
.description("Compile and save a workflow definition; --draft appends an editable revision without publishing it. Apply costs 0 credits, calls no graph nodes, creates no run, and preserves disabled status; call or enable separately to execute.")
|
|
14255
14352
|
.requiredOption("--file <path>", "Workflow module or manifest JSON file.")
|
|
14256
14353
|
.option("--approved", "Authorize autonomous tool calls for this revision.")
|
|
14257
14354
|
.option("--max-credits <n>", "Optional explicit positive ceiling for each autonomous delivery; omit to use the plan-tier default.")
|
|
@@ -14394,19 +14491,20 @@ Run completion:
|
|
|
14394
14491
|
}), options));
|
|
14395
14492
|
}))
|
|
14396
14493
|
.addCommand(new Command("call")
|
|
14397
|
-
.description("Enqueue a workflow run asynchronously
|
|
14494
|
+
.description("Enqueue a workflow run asynchronously and directly without simulating its trigger. Follow the returned run_id with `oxygen workflows tail <run_id>`.")
|
|
14398
14495
|
.argument("[workflow]", "Workflow id, slug, or name.")
|
|
14399
14496
|
.option("--workflow <workflow>", "Workflow id, slug, or name.")
|
|
14400
14497
|
.option("--workflow-id <workflow_id>", "Workflow id or slug.")
|
|
14401
14498
|
.option("--workflow-name <workflow_name>", "Workflow name.")
|
|
14402
14499
|
.option("--input-json <json>", "Workflow input object. Defaults to {}.")
|
|
14403
|
-
.requiredOption("--mode <mode>", "Execution mode
|
|
14500
|
+
.requiredOption("--mode <mode>", "Execution mode. smoke-test/dry-run: 0 credits, no paid provider calls or external writes; internal reads still execute. live: may spend/write and requires approval + cap.")
|
|
14404
14501
|
.option("--idempotency-key <key>", "Optional idempotency key.")
|
|
14405
14502
|
.option("--max-credits <n>", "Required credit ceiling for live calls.")
|
|
14406
14503
|
.option("--approved", "Required for live calls after inspecting a dry run.")
|
|
14407
14504
|
.option("--revision <n>", "Run a specific saved version instead of the live one. Dry-run and smoke-test only: publish a version to run it live.")
|
|
14408
14505
|
.option("--include-bundle", "Include durable recipe bundles in JSON output.")
|
|
14409
14506
|
.option("--json", "Print a JSON envelope.")
|
|
14507
|
+
.addHelpText("after", "\nSafety: smoke_test and dry_run share one boundary: 0 credits, no paid provider calls, no external writes. Oxygen internal reads use current workspace data, and oxygen.http_json_request may make a real outbound GET. Every mode creates an inspectable Workflow run record.\n")
|
|
14410
14508
|
.action(async (workflowArg, options) => {
|
|
14411
14509
|
const maxCredits = readPositiveNumber(options.maxCredits);
|
|
14412
14510
|
const revisionVersion = readPositiveNumber(options.revision);
|
|
@@ -14427,11 +14525,35 @@ Run completion:
|
|
|
14427
14525
|
}))
|
|
14428
14526
|
.addCommand(new Command("webhooks")
|
|
14429
14527
|
.description("Workflow webhook trigger utilities.")
|
|
14528
|
+
.addCommand(new Command("rotate")
|
|
14529
|
+
.description("Replace a workflow webhook's shared secret. Previews by default; the old sender secret stops working immediately after approval.")
|
|
14530
|
+
.argument("<workflow>", "Webhook workflow id, slug, or name.")
|
|
14531
|
+
.option("--approved", "Confirm replacement after reviewing the preview. The new secret is shown once.")
|
|
14532
|
+
.option("--json", "Print a JSON envelope.")
|
|
14533
|
+
.action((workflow, options) => handleAsyncAction("workflows webhooks rotate", options, () => requestOxygen("/api/cli/workflows/webhooks/rotate", {
|
|
14534
|
+
method: "POST",
|
|
14535
|
+
body: {
|
|
14536
|
+
workflow,
|
|
14537
|
+
...(options.approved ? { approved: true } : {}),
|
|
14538
|
+
},
|
|
14539
|
+
}))))
|
|
14540
|
+
.addCommand(new Command("replay")
|
|
14541
|
+
.description("Create a new dry run from one authenticated delivery's exact original revision and payload. Never resends the webhook; uses 0 credits, no paid provider calls, and no external writes. Internal reads and outbound HTTP GETs can still execute.")
|
|
14542
|
+
.argument("<delivery_id>", "Delivery UUID from `oxygen workflows webhooks deliveries`.")
|
|
14543
|
+
.option("--request-key <key>", "Stable key for safely retrying the same replay request.")
|
|
14544
|
+
.option("--json", "Print a JSON envelope.")
|
|
14545
|
+
.action((deliveryId, options) => handleAsyncAction("workflows webhooks replay", options, () => requestOxygen("/api/cli/workflows/webhooks/deliveries/replay", {
|
|
14546
|
+
method: "POST",
|
|
14547
|
+
body: {
|
|
14548
|
+
delivery_id: deliveryId,
|
|
14549
|
+
request_key: readOption(options.requestKey) ?? randomUUID(),
|
|
14550
|
+
},
|
|
14551
|
+
}))))
|
|
14430
14552
|
.addCommand(new Command("deliveries")
|
|
14431
14553
|
.description("List workflow webhook deliveries: what arrived, whether it verified, and the run it started.")
|
|
14432
14554
|
.argument("[trigger_id]", "Optional webhook trigger name, such as lead-created.")
|
|
14433
14555
|
.option("--workflow-id <id>", "Filter to deliveries that started a run of this workflow.")
|
|
14434
|
-
.option("--outcome <outcome>", "Filter by rejected (
|
|
14556
|
+
.option("--outcome <outcome>", "Filter by rejected (sender authentication failed), blocked (authenticated, no run), or ran.")
|
|
14435
14557
|
.option("--limit <n>", "Maximum deliveries to return. Defaults to 50.")
|
|
14436
14558
|
.option("--json", "Print a JSON envelope.")
|
|
14437
14559
|
.action((triggerId, options) => handleAsyncAction("workflows webhooks deliveries", options, () => requestOxygen(workflowWebhookDeliveriesPath({
|
|
@@ -14485,7 +14607,7 @@ Run completion:
|
|
|
14485
14607
|
.addCommand(new Command("events")
|
|
14486
14608
|
.description("Workflow event trigger utilities.")
|
|
14487
14609
|
.addCommand(new Command("list")
|
|
14488
|
-
.description("Search and filter every app-native and connected event this workspace can use as a workflow trigger.")
|
|
14610
|
+
.description("Search and filter every registered app-native and connected event this workspace can use as a workflow trigger, including setup-required events.")
|
|
14489
14611
|
.option("--search <text>", "Match labels, catalog ids, descriptions, endpoints, filters, or payload fields.")
|
|
14490
14612
|
.option("--source <source>", "Match one event source exactly, such as sequencer or crm.")
|
|
14491
14613
|
.option("--kind <kind>", "Match builtin, integration, or existing events.")
|
|
@@ -14506,6 +14628,11 @@ Kinds:
|
|
|
14506
14628
|
existing Raw source/event retained from an existing workflow trigger;
|
|
14507
14629
|
no preset filter is required.
|
|
14508
14630
|
|
|
14631
|
+
Readiness:
|
|
14632
|
+
Events that need setup remain selectable and draft-saveable. Their readiness
|
|
14633
|
+
reason and exact setup action are returned here; publish/enable stays blocked
|
|
14634
|
+
until the prerequisite is ready.
|
|
14635
|
+
|
|
14509
14636
|
Example trigger:
|
|
14510
14637
|
{"type":"event","source":"crm","event":"activity_created",
|
|
14511
14638
|
"filters":[{"path":"activity_type","op":"eq","value":"meeting_booked"}]}
|
|
@@ -14629,8 +14756,8 @@ Full trigger schema: oxygen workflows schema --subject trigger --json
|
|
|
14629
14756
|
});
|
|
14630
14757
|
}))
|
|
14631
14758
|
.addCommand(new Command("run")
|
|
14632
|
-
.description("
|
|
14633
|
-
.argument("<run_id>", "Workflow run UUID.")
|
|
14759
|
+
.description("Read one workflow run, including waiting and awaiting_approval pause state. This command changes nothing.")
|
|
14760
|
+
.argument("<run_id>", "Workflow run UUID returned as data.run.id by workflows call/tail; not the separate provenance run id.")
|
|
14634
14761
|
.option("--include-bundle", "Include durable recipe bundles in JSON output.")
|
|
14635
14762
|
.option("--json", "Print a JSON envelope.")
|
|
14636
14763
|
.action(async (runId, options) => {
|
|
@@ -15539,7 +15666,7 @@ function waitForWorkflowRun(runId, options) {
|
|
|
15539
15666
|
parked: true,
|
|
15540
15667
|
...(resumeAt ? { resumes_at: resumeAt } : {}),
|
|
15541
15668
|
message: resumeAt
|
|
15542
|
-
? `Run is parked until ${resumeAt}
|
|
15669
|
+
? `Run is parked until ${resumeAt} and resumes automatically — no action needed.`
|
|
15543
15670
|
: "Run is parked and resumes automatically — no action needed.",
|
|
15544
15671
|
}
|
|
15545
15672
|
: {}),
|
|
@@ -21008,6 +21135,30 @@ function ansi(enabled) {
|
|
|
21008
21135
|
function collectRepeatable(value, previous) {
|
|
21009
21136
|
return [...previous, value];
|
|
21010
21137
|
}
|
|
21138
|
+
function readNamedCredentials(entries) {
|
|
21139
|
+
if (!entries || entries.length === 0)
|
|
21140
|
+
return undefined;
|
|
21141
|
+
const credentials = {};
|
|
21142
|
+
for (const entry of entries) {
|
|
21143
|
+
const separator = entry.indexOf("=");
|
|
21144
|
+
if (separator <= 0) {
|
|
21145
|
+
throw new Error("--credential must use name=value (for example, --credential subdomain=us).");
|
|
21146
|
+
}
|
|
21147
|
+
const name = entry.slice(0, separator).trim();
|
|
21148
|
+
const value = entry.slice(separator + 1).trim();
|
|
21149
|
+
if (!/^[A-Za-z][A-Za-z0-9_.-]{0,99}$/.test(name)) {
|
|
21150
|
+
throw new Error("--credential has an invalid field name.");
|
|
21151
|
+
}
|
|
21152
|
+
if (!value) {
|
|
21153
|
+
throw new Error(`--credential ${name}= requires a non-empty value.`);
|
|
21154
|
+
}
|
|
21155
|
+
if (Object.hasOwn(credentials, name)) {
|
|
21156
|
+
throw new Error(`--credential field '${name}' was provided more than once.`);
|
|
21157
|
+
}
|
|
21158
|
+
credentials[name] = value;
|
|
21159
|
+
}
|
|
21160
|
+
return credentials;
|
|
21161
|
+
}
|
|
21011
21162
|
// Shared query string for `posts comments` / `posts reactions` (post social_id +
|
|
21012
21163
|
// optional comment scope, pagination, sort, and account selection).
|
|
21013
21164
|
function buildPostEngagementQuery(options) {
|
|
@@ -21134,13 +21285,6 @@ function withSupportEventsQuery(path, options) {
|
|
|
21134
21285
|
const query = params.toString();
|
|
21135
21286
|
return query ? `${path}?${query}` : path;
|
|
21136
21287
|
}
|
|
21137
|
-
function withSupportAdminGetQuery(path, options) {
|
|
21138
|
-
const ifVersion = readOption(options.ifVersion);
|
|
21139
|
-
if (!ifVersion)
|
|
21140
|
-
return path;
|
|
21141
|
-
const params = new URLSearchParams({ if_version: ifVersion });
|
|
21142
|
-
return `${path}?${params.toString()}`;
|
|
21143
|
-
}
|
|
21144
21288
|
// Workflow text flags (--verify-notes/--plan/--draft) bypass readOption: an
|
|
21145
21289
|
// explicitly-passed empty string clears the field server-side, while an absent
|
|
21146
21290
|
// flag leaves it untouched, so "" must survive to the request body.
|
|
@@ -21189,6 +21333,26 @@ function buildSupportAdminUpdateBody(options) {
|
|
|
21189
21333
|
}
|
|
21190
21334
|
return body;
|
|
21191
21335
|
}
|
|
21336
|
+
function supportAdminOpenResult(data) {
|
|
21337
|
+
if (!isRecord(data) || !isRecord(data.ticket)) {
|
|
21338
|
+
throw new OxygenError("invalid_response", "The support admin response did not include a Plain Thread.", { exitCode: 1 });
|
|
21339
|
+
}
|
|
21340
|
+
const plainUrl = data.ticket.plain_url;
|
|
21341
|
+
if (typeof plainUrl !== "string" || !plainUrl.startsWith("https://app.plain.com/")) {
|
|
21342
|
+
throw new OxygenError("invalid_response", "The support admin response did not include a valid Plain Inbox URL.", { exitCode: 1 });
|
|
21343
|
+
}
|
|
21344
|
+
return {
|
|
21345
|
+
thread_id: data.ticket.id ?? null,
|
|
21346
|
+
thread_ref: data.ticket.ref ?? null,
|
|
21347
|
+
plain_url: plainUrl,
|
|
21348
|
+
};
|
|
21349
|
+
}
|
|
21350
|
+
async function handleSupportAdminUpdateRequest(action, ticketId, options, fields) {
|
|
21351
|
+
await handleAsyncAction(`support admin ${action}`, options, () => requestOxygen(`/api/cli/admin/support/tickets/${encodeURIComponent(ticketId)}/update`, {
|
|
21352
|
+
method: "POST",
|
|
21353
|
+
body: { action, ...fields },
|
|
21354
|
+
}));
|
|
21355
|
+
}
|
|
21192
21356
|
async function handleSupportAdminWorkflowAction(ticketId, options) {
|
|
21193
21357
|
try {
|
|
21194
21358
|
const body = buildSupportAdminWorkflowBody(options);
|
package/dist/skills.js
CHANGED
|
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir, tmpdir } from "node:os";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
|
-
import { OxygenError,
|
|
5
|
+
import { OxygenError, inferUserCapabilityRoute, serializeCapabilityRoute, toFailure, } from "@oxygen/shared";
|
|
6
6
|
import { defaultApiUrl, loadCredentials, normalizeApiUrl } from "./credentials.js";
|
|
7
7
|
import { requestOxygen } from "./http-client.js";
|
|
8
8
|
import { isRecord, readOption } from "./util.js";
|
|
@@ -98,7 +98,7 @@ export async function searchAgentSkills(query, options, runtime = {}) {
|
|
|
98
98
|
}
|
|
99
99
|
const normalized = query.trim();
|
|
100
100
|
const tokens = tokenizeSkillQuery(normalized);
|
|
101
|
-
const route =
|
|
101
|
+
const route = inferUserCapabilityRoute(normalized);
|
|
102
102
|
const requestedLimit = Number.parseInt(readOption(options.limit) ?? "5", 10);
|
|
103
103
|
const limit = Number.isFinite(requestedLimit) ? Math.min(10, Math.max(1, requestedLimit)) : 5;
|
|
104
104
|
const matches = index.skills
|
|
@@ -70,6 +70,8 @@ export function exitCodeForErrorCode(code) {
|
|
|
70
70
|
}
|
|
71
71
|
if (code === "not_found" || code.endsWith("_not_found"))
|
|
72
72
|
return 4;
|
|
73
|
+
if (code.endsWith("_requires_approval"))
|
|
74
|
+
return 7;
|
|
73
75
|
if (code === "timeout" || code.endsWith("_timeout"))
|
|
74
76
|
return 8;
|
|
75
77
|
if (code.startsWith("invalid_")
|