@oxygen-agent/cli 1.275.1 → 1.284.3

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 CHANGED
@@ -34,4 +34,4 @@ oxygen update
34
34
 
35
35
  For product documentation, visit https://oxygen-agent.com/docs. For support, visit https://oxygen-agent.com.
36
36
 
37
- Version: 1.275.1
37
+ Version: 1.284.3
@@ -0,0 +1,30 @@
1
+ import type { Command } from "commander";
2
+ export type CommandManifestFlag = {
3
+ flags: string;
4
+ description: string;
5
+ required: boolean;
6
+ };
7
+ export type CommandManifestArgument = {
8
+ name: string;
9
+ required: boolean;
10
+ description: string;
11
+ };
12
+ export type CommandManifestEntry = {
13
+ name: string;
14
+ group: string;
15
+ description: string;
16
+ arguments: CommandManifestArgument[];
17
+ flags: CommandManifestFlag[];
18
+ spends_credits: boolean;
19
+ mutates: boolean;
20
+ json_supported: boolean;
21
+ hidden: boolean;
22
+ };
23
+ export type CommandManifest = {
24
+ binary: string;
25
+ version: string;
26
+ total: number;
27
+ exit_codes: Record<string, string>;
28
+ commands: CommandManifestEntry[];
29
+ };
30
+ export declare function buildCommandManifest(program: Command, binaryName: string): CommandManifest;
@@ -0,0 +1,70 @@
1
+ import { CLI_EXIT_CODE_TABLE, OXYGEN_VERSION } from "@oxygen/shared";
2
+ const MUTATING_VERBS = new Set([
3
+ "add", "apply", "approve", "archive", "assign", "buy", "cancel", "clear",
4
+ "configure", "connect", "create", "delete", "disable", "dispatch", "draft",
5
+ "emit", "enable", "enroll", "grant", "import", "insert", "invite", "launch",
6
+ "login", "logout", "materialize", "merge", "migrate", "order", "pause",
7
+ "publish", "purchase", "push", "record", "register", "remove", "rename",
8
+ "reorder", "reply", "rerun", "reset", "resolve", "restore", "resume",
9
+ "retry", "retype", "revoke", "rotate", "run", "save", "schedule", "select",
10
+ "send", "set", "share", "start", "stop", "subscribe", "sync", "unpublish",
11
+ "unshare", "unsubscribe", "update", "upload", "upsert", "use", "withdraw",
12
+ "write",
13
+ ]);
14
+ export function buildCommandManifest(program, binaryName) {
15
+ const commands = [];
16
+ for (const child of program.commands) {
17
+ collectCommand(child, [], commands);
18
+ }
19
+ commands.sort((a, b) => a.name.localeCompare(b.name));
20
+ return {
21
+ binary: binaryName,
22
+ version: OXYGEN_VERSION,
23
+ total: commands.length,
24
+ exit_codes: Object.fromEntries(Object.entries(CLI_EXIT_CODE_TABLE).map(([code, meaning]) => [code, meaning])),
25
+ commands,
26
+ };
27
+ }
28
+ function collectCommand(command, ancestors, out) {
29
+ const path = [...ancestors, command.name()];
30
+ const children = command.commands;
31
+ // Group commands (e.g. `oxygen tables`) exist to hold subcommands; only leaf
32
+ // commands are invocable work, so only leaves become manifest entries.
33
+ if (children.length === 0) {
34
+ out.push(toManifestEntry(command, path));
35
+ return;
36
+ }
37
+ for (const child of children) {
38
+ collectCommand(child, path, out);
39
+ }
40
+ }
41
+ function toManifestEntry(command, path) {
42
+ const options = command.options.filter((option) => !option.hidden);
43
+ const flagStrings = options.map((option) => option.flags);
44
+ const leafVerb = path[path.length - 1] ?? "";
45
+ const verbPrefix = leafVerb.split("-")[0] ?? leafVerb;
46
+ return {
47
+ name: path.join(" "),
48
+ group: path[0] ?? "",
49
+ description: command.description(),
50
+ arguments: command.registeredArguments.map((argument) => ({
51
+ name: argument.name(),
52
+ required: argument.required,
53
+ description: argument.description,
54
+ })),
55
+ flags: options.map((option) => ({
56
+ flags: option.flags,
57
+ description: option.description,
58
+ required: option.mandatory,
59
+ })),
60
+ spends_credits: flagStrings.some((flags) => flags.includes("--approved") || flags.includes("--max-credits")),
61
+ mutates: MUTATING_VERBS.has(leafVerb) || MUTATING_VERBS.has(verbPrefix),
62
+ json_supported: flagStrings.some((flags) => flags.includes("--json")),
63
+ hidden: isHiddenCommand(command),
64
+ };
65
+ }
66
+ function isHiddenCommand(command) {
67
+ // Commander keeps hidden state private; read it defensively so a library
68
+ // bump degrades to hidden:false instead of a crash.
69
+ return Boolean(command._hidden);
70
+ }
package/dist/help.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function applyOxygenHelp(program: Command, binaryName: string): void;
package/dist/help.js ADDED
@@ -0,0 +1,79 @@
1
+ import { CLI_EXIT_CODE_TABLE } from "@oxygen/shared";
2
+ // Grouped `--help` for the ~70 top-level command groups: commander's default
3
+ // registration-order wall (login, auth, profiles, ... before any GTM noun) is
4
+ // the first thing every fresh agent reads, so order it by the OXYGEN OS layer
5
+ // a command belongs to and close with an agent quickstart + the reserved
6
+ // exit-code table. Commands missing from the map land in the last group so a
7
+ // new command never disappears from help.
8
+ const HELP_GROUPS = [
9
+ {
10
+ heading: "Get started & account:",
11
+ commands: [
12
+ "login", "auth", "profiles", "logout", "update", "api-keys", "whoami",
13
+ "onboarding", "status", "orgs", "commands", "skills", "session",
14
+ "support", "feedback",
15
+ ],
16
+ },
17
+ {
18
+ heading: "Knowledge (context, wiki, recipes):",
19
+ commands: ["context", "knowledge", "prompts", "templates", "blueprints"],
20
+ },
21
+ {
22
+ heading: "Data (tables, CRM, dashboards):",
23
+ commands: [
24
+ "tables", "rows", "cells", "columns", "action-column", "enrich-column",
25
+ "enrichment", "table-runs", "table-ingestions", "runs", "crm",
26
+ "dashboards", "projects", "signup-leads",
27
+ ],
28
+ },
29
+ {
30
+ heading: "Sourcing & search:",
31
+ commands: ["sourcing", "lead-sourcing", "search", "companies", "people", "find", "tools"],
32
+ },
33
+ {
34
+ heading: "Outbound & channels:",
35
+ commands: [
36
+ "sequences", "senders", "inbox", "messages", "mailboxes",
37
+ "managed-inboxes", "deliverability", "domains", "email", "schedules",
38
+ "suppressions", "egress", "linkedin", "connections", "followers",
39
+ "viewers", "posts", "engagement", "whatsapp", "publishing", "notetaker",
40
+ "reviews",
41
+ ],
42
+ },
43
+ {
44
+ heading: "Automation, billing & admin:",
45
+ commands: [
46
+ "workflows", "integrations", "custom-integrations", "billing", "budget",
47
+ "observability", "worker", "db", "admin", "directory",
48
+ ],
49
+ },
50
+ ];
51
+ const FALLBACK_HEADING = "Other commands:";
52
+ export function applyOxygenHelp(program, binaryName) {
53
+ const headingByCommand = new Map();
54
+ for (const group of HELP_GROUPS) {
55
+ for (const name of group.commands)
56
+ headingByCommand.set(name, group.heading);
57
+ }
58
+ for (const command of program.commands) {
59
+ command.helpGroup(headingByCommand.get(command.name()) ?? FALLBACK_HEADING);
60
+ }
61
+ const exitCodeLine = Object.entries(CLI_EXIT_CODE_TABLE)
62
+ .map(([code, meaning]) => `${code} ${meaning.split(" (")[0]}`)
63
+ .join(" · ");
64
+ program.addHelpText("afterAll", [
65
+ "",
66
+ "Agent quickstart:",
67
+ ` 1. ${binaryName} login connect this terminal (or set OXYGEN_API_KEY)`,
68
+ ` 2. ${binaryName} onboarding start --json load workspace context + the skill that teaches the GTM loops`,
69
+ ` 3. ${binaryName} commands --json full machine-readable command grammar (this CLI's self-index)`,
70
+ "",
71
+ "Conventions:",
72
+ " --json prints a stable envelope {ok, data|error, meta}; errors always print it.",
73
+ " Commands that spend credits or write to external platforms are gated: live runs",
74
+ " require --approved --max-credits <n> and fail with a typed error until approved.",
75
+ " State-changing responses include a web_url deep-link to inspect the result.",
76
+ "",
77
+ `Exit codes: ${exitCodeLine}`,
78
+ ].join("\n"));
79
+ }
@@ -10,6 +10,7 @@ type RequestOptions = {
10
10
  traceId?: string;
11
11
  fetch?: typeof fetch;
12
12
  selectedOrganization?: string;
13
+ idempotencyKey?: string;
13
14
  };
14
15
  export declare function requestOxygen<T>(// skipcq: JS-R1005
15
16
  path: string, options?: RequestOptions): Promise<T>;
@@ -42,6 +42,17 @@ path, options = {}) {
42
42
  if (options.body) {
43
43
  headers["Content-Type"] = "application/json";
44
44
  }
45
+ // Every mutating request carries an idempotency key. Routes that support
46
+ // replay dedupe on it; others ignore the header. On a timeout the key is
47
+ // echoed in error details so a re-run (OXYGEN_IDEMPOTENCY_KEY=<key>) can
48
+ // retry without duplicating the write once the route supports replay.
49
+ const method = options.method ?? "GET";
50
+ const idempotencyKey = method === "GET"
51
+ ? undefined
52
+ : options.idempotencyKey ?? process.env.OXYGEN_IDEMPOTENCY_KEY ?? randomUUID();
53
+ if (idempotencyKey) {
54
+ headers["Idempotency-Key"] = idempotencyKey;
55
+ }
45
56
  let response;
46
57
  const timeoutMs = resolveRequestTimeoutMs(options.timeoutMs);
47
58
  const timeout = createTimeoutSignal(timeoutMs);
@@ -64,6 +75,7 @@ path, options = {}) {
64
75
  path,
65
76
  timeout_ms: timeoutMs,
66
77
  trace_id: traceId,
78
+ ...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),
67
79
  },
68
80
  exitCode: 1,
69
81
  });
@@ -72,6 +84,7 @@ path, options = {}) {
72
84
  details: {
73
85
  api_url: apiUrl,
74
86
  reason: error instanceof Error ? error.message : "unknown",
87
+ ...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),
75
88
  },
76
89
  exitCode: 1,
77
90
  });