@oxygen-agent/cli 1.1011.405 → 1.1011.446

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +1 -1
  2. package/dist/command-manifest.js +8 -4
  3. package/dist/connectors-commands.js +3 -0
  4. package/dist/contract-flags.js +22 -9
  5. package/dist/customer-operation-commands.d.ts +5 -0
  6. package/dist/customer-operation-commands.js +101 -0
  7. package/dist/database-query-flags.d.ts +2 -0
  8. package/dist/database-query-flags.js +6 -0
  9. package/dist/help.d.ts +3 -1
  10. package/dist/help.js +33 -0
  11. package/dist/http-client.d.ts +2 -0
  12. package/dist/http-client.js +41 -1
  13. package/dist/index.js +470 -174
  14. package/dist/knowledge-repository-commands.js +12 -14
  15. package/dist/mailbox-ramp-notice.d.ts +11 -0
  16. package/dist/mailbox-ramp-notice.js +53 -0
  17. package/dist/settings-commands.js +5 -4
  18. package/dist/skills.d.ts +5 -4
  19. package/dist/skills.js +106 -79
  20. package/dist/tool-catalog-detail-command.d.ts +7 -0
  21. package/dist/tool-catalog-detail-command.js +19 -0
  22. package/dist/tool-catalog-search-window.d.ts +6 -0
  23. package/dist/tool-catalog-search-window.js +12 -0
  24. package/dist/transcript.js +1 -0
  25. package/node_modules/@oxygen/shared/dist/bounded-read-result.d.ts +1 -1
  26. package/node_modules/@oxygen/shared/dist/bounded-read-result.js +2 -2
  27. package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +17 -1
  28. package/node_modules/@oxygen/shared/dist/capability-discovery.js +359 -34
  29. package/node_modules/@oxygen/shared/dist/connection-approval.d.ts +2 -0
  30. package/node_modules/@oxygen/shared/dist/connection-approval.js +2 -0
  31. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.d.ts +2 -0
  32. package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +110 -0
  33. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.d.ts +4 -0
  34. package/node_modules/@oxygen/shared/dist/contracts/crm-attributes.js +39 -0
  35. package/node_modules/@oxygen/shared/dist/contracts/crm-enrichment-read.d.ts +21 -0
  36. package/node_modules/@oxygen/shared/dist/contracts/crm-enrichment-read.js +15 -0
  37. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.d.ts +2 -0
  38. package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +131 -0
  39. package/node_modules/@oxygen/shared/dist/contracts/database.d.ts +30 -0
  40. package/node_modules/@oxygen/shared/dist/contracts/database.js +29 -0
  41. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.d.ts +43 -0
  42. package/node_modules/@oxygen/shared/dist/contracts/knowledge-content-reads.js +20 -0
  43. package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.d.ts +12 -0
  44. package/node_modules/@oxygen/shared/dist/contracts/linkedin-invites-list.js +8 -0
  45. package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +48 -2
  46. package/node_modules/@oxygen/shared/dist/contracts/operations.js +67 -7
  47. package/node_modules/@oxygen/shared/dist/contracts/table-grid-query.d.ts +4 -0
  48. package/node_modules/@oxygen/shared/dist/contracts/table-grid-query.js +6 -0
  49. package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.d.ts +21 -0
  50. package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.js +27 -0
  51. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-detail.d.ts +19 -0
  52. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-detail.js +10 -0
  53. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-search-window.d.ts +12 -0
  54. package/node_modules/@oxygen/shared/dist/contracts/tool-catalog-search-window.js +7 -0
  55. package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.d.ts +1 -0
  56. package/node_modules/@oxygen/shared/dist/contracts/workspace-pages.js +12 -0
  57. package/node_modules/@oxygen/shared/dist/email-campaign-ramp.d.ts +104 -0
  58. package/node_modules/@oxygen/shared/dist/email-campaign-ramp.js +250 -0
  59. package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +1 -2
  60. package/node_modules/@oxygen/shared/dist/plan-limits.js +2 -2
  61. package/node_modules/@oxygen/shared/dist/provider-input-value.d.ts +5 -0
  62. package/node_modules/@oxygen/shared/dist/provider-input-value.js +10 -0
  63. package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
  64. package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
  65. package/node_modules/@oxygen/shared/package.json +3 -6
  66. package/package.json +1 -1
  67. package/node_modules/@oxygen/shared/dist/copilot-skills.d.ts +0 -15
  68. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +0 -31
  69. package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +0 -41
  70. package/node_modules/@oxygen/shared/dist/copilot-skills.js +0 -6
@@ -0,0 +1,110 @@
1
+ /** Browser resource operations, using the same scoped handlers on every surface. */
2
+ import { arr, bool, enumOf, int, nullable, num, obj, str } from "./schema.js";
3
+ const text = (description) => str(description ? { description } : {});
4
+ const json = { type: "object", additionalProperties: true };
5
+ const strings = () => arr(str());
6
+ const objects = () => arr(json);
7
+ const dataType = () => enumOf(["text", "numeric", "boolean", "jsonb", "timestamptz"]);
8
+ const selection = obj({
9
+ mode: enumOf(["all", "limit", "row_ids", "filter", "all_matching"]),
10
+ limit: int({ minimum: 1 }), row_ids: strings(), filters: objects(),
11
+ filter: text("Serialized browser filter tree."), search: text(), sort: text(), crm_object: text(),
12
+ range: obj({ offset: int({ minimum: 0 }), limit: int({ minimum: 1 }) }, { required: ["offset", "limit"] }),
13
+ }, { required: ["mode"] });
14
+ const actions = arr(obj({ type: enumOf(["tool_column"]), column: text(), definition: json }, { required: ["column"] }));
15
+ const viewFields = { name: text(), view_type: text(), config: json, group_by: text(), is_default: bool(), position: int({ minimum: 0 }) };
16
+ const crmLinks = arr(obj({ object: text(), row_id: text(), role: text() }, { required: ["object", "row_id"] }));
17
+ function operation(id, route, method, cli, mcp, pack, summary, fields = {}, required = [], options = {}) {
18
+ return {
19
+ id, route, method, parameters: [...route.matchAll(/\{(\w+)\}/g)].map((match) => match[1]),
20
+ contract: obj(fields, { required }),
21
+ cli: { path: cli.split(" "), summary },
22
+ mcp: { name: mcp, title: `Oxygen: ${cli}`, description: summary, pack },
23
+ mutates: method !== "GET", spends: false, ...options,
24
+ };
25
+ }
26
+ const table = "/api/cli/tables/{table}";
27
+ const column = `${table}/columns/{column}`;
28
+ const run = `${table}/runs/{run}`;
29
+ const upload = { content_base64: text(), file_name: text(), content_type: text(), intent: enumOf(["preview", "import"]), format: enumOf(["csv", "json", "xlsx"]), sheet: text(), table: text(), create_name: text(), project: text(), upsert_key: text(), source_key_map: json };
30
+ const multipart = { contentField: "content_base64", nameField: "file_name", typeField: "content_type", fileField: "file" };
31
+ // Both surfaces already register these exact canonical actions under their established names.
32
+ const nativeCli = { generated: { cli: false, mcp: false } };
33
+ export const BROWSER_OPERATIONS = [
34
+ operation("tables.resource.export_csv", `${table}/export/csv`, "GET", "tables export-csv", "oxygen_table_export_csv", "tables", "Download every row in the exact filtered Table view as streamed CSV. CLI writes --output; MCP returns an authenticated download resource link. Unsupported live lookup filters fail before download; rows and bytes are not capped or truncated.", { f: text("Serialized filter tree."), s: text("Serialized sort state."), q: text(), fields: text("Comma-separated exact column keys.") }, [], { binary: { stream: true, resourceParameter: "table" } }),
35
+ operation("billing.allowance.view", "/api/cli/billing/allowance/view", "GET", "billing allowance view", "oxygen_billing_allowance_view", "billing", "Read the complete current-cycle allowance view, including billing-owner display and partner-bonus eligibility."),
36
+ operation("billing.commitments.view", "/api/cli/billing/commitments/view", "GET", "billing commitments view", "oxygen_billing_commitments_view", "billing", "Read the complete fixed-credit commitment view and linked billing-owner display, without enumerating a child's sibling resources."),
37
+ operation("billing.invoices.view", "/api/cli/billing/invoices/view", "GET", "billing invoices view", "oxygen_billing_invoices_view", "billing", "Read the browser invoice-history projection, including exact native timestamps, amounts and signed Stripe invoice links."),
38
+ operation("billing.summary", "/api/cli/billing/summary", "GET", "billing summary", "oxygen_billing_summary", "billing", "Read the complete non-writing wallet, plan, seat-capacity and billing-owner summary."),
39
+ operation("billing.usage.view", "/api/cli/billing/usage/view", "GET", "billing usage view", "oxygen_billing_usage_view", "billing", "Read the full selected-window credit-spend view, including permitted linked workspace rollups.", { days: enumOf(["7", "30", "90"]), workspace: text() }),
40
+ operation("integrations.managed_endpoints", "/api/cli/integrations/composio/endpoints", "GET", "integrations managed-endpoints", "oxygen_integration_managed_endpoints", "integrations", "Read a managed integration's full action catalog and parameter schemas.", { integration_id: text() }, ["integration_id"]),
41
+ operation("integrations.openrouter_models", "/api/cli/integrations/openrouter/models", "GET", "integrations openrouter-models", "oxygen_openrouter_models", "integrations", "Read model and pricing metadata using this workspace's connected OpenRouter account; credentials are never returned."),
42
+ operation("integrations.status", "/api/cli/integrations/status", "GET", "integrations status", "oxygen_integration_status", "integrations", "Read active connection counts, sender connection state and the workspace's usable BYOK plan entitlement."),
43
+ operation("pricing.customer_credit_prices", "/api/cli/pricing/customer-credit-prices", "GET", "pricing customer-credit-prices", "oxygen_customer_credit_prices", "workspace", "Read the customer credit-price catalog, quantity decisions and price provenance used by the Table picker."),
44
+ operation("sequences.contacts.rows", "/api/cli/sequences/{sequence}/contacts/rows", "GET", "sequences contacts rows", "oxygen_sequence_contacts_rows", "sequences", "Read the full contact grid projection with exact contact-state filter and native synthetic status columns.", { contact_state: enumOf(["all", "not_contacted", "contacted", "connected", "replied", "positive_reply"]), limit: int({ minimum: 1, maximum: 2000 }), cursor: text(), offset: int({ minimum: 0 }), f: text(), s: text() }),
45
+ operation("sequences.leads.rows", "/api/cli/sequences/{sequence}/leads/rows", "GET", "sequences leads rows", "oxygen_sequence_leads_rows", "sequences", "Read the complete enrolled-lead grid projection, including sequence status, step, action and failure columns.", { limit: int({ minimum: 1, maximum: 2000 }), cursor: text(), offset: int({ minimum: 0 }), f: text(), s: text() }),
46
+ operation("unibox.metadata", "/api/cli/unibox/metadata", "GET", "unibox metadata", "oxygen_unibox_metadata", "inbox", "Read the complete authenticated inbox account, filter and tag metadata."),
47
+ operation("workflows.observed_output", "/api/cli/workflows/{workflow}/observed-output", "GET", "workflows observed-output", "oxygen_workflow_observed_output", "workflows", "Inspect observed output paths and bounded samples from an exact run or recent matching workflow revision.", { run_id: text(), source_hash: text() }),
48
+ operation("workflows.run.events", "/api/cli/workflows/{workflow}/runs/{run}/events", "GET", "workflows runs events", "oxygen_workflow_run_events", "workflows", "Read the exact Workflow run's bounded event history and whether it is still active.", { limit: int({ minimum: 1, maximum: 1000 }) }),
49
+ operation("workflows.run.step_statuses", "/api/cli/workflows/{workflow}/runs/{run}/step-statuses", "GET", "workflows runs step-statuses", "oxygen_workflow_run_step_statuses", "workflows", "Read the current Workflow run status and every step status for immediate execution readback."),
50
+ operation("workflows.tool_output_paths", "/api/cli/workflows/tool-output-paths", "GET", "workflows tool-output-paths", "oxygen_workflow_tool_output_paths", "workflows", "Discover bounded workspace-observed tool output paths and run/revision provenance, without exposing provider values.", { tool_id: strings() }, ["tool_id"], { repeatedQueryFields: ["tool_id"] }),
51
+ operation("tables.resource.edit", table, "PATCH", "tables edit", "oxygen_table_resource_edit", "tables", "Rename a Table or move it to a workspace project; returns the saved Table.", { name: text(), project_id: text() }),
52
+ operation("tables.resource.archive", table, "DELETE", "tables archive", "oxygen_tables_archive", "tables", "Archive a Table indefinitely without dropping its physical data or scheduling a purge.", {}, [], nativeCli),
53
+ operation("tables.resource.duplicate", `${table}/duplicate`, "POST", "tables duplicate", "oxygen_tables_duplicate", "tables", "Duplicate a Table with its columns and optionally its row values.", { name: text(), include_rows: bool() }, [], nativeCli),
54
+ operation("tables.resource.activity", `${table}/activity`, "GET", "tables changes", "oxygen_table_resource_activity", "tables", "Read a Table's bounded activity history.", { limit: int({ minimum: 1 }) }),
55
+ operation("tables.resource.column_stats", `${table}/column-stats`, "GET", "tables column-stats", "oxygen_table_column_stats", "tables", "Read per-column completion and provider statistics; large Tables use labeled samples."),
56
+ operation("tables.resource.fill_progress", `${table}/fill-progress`, "GET", "tables fill-progress", "oxygen_table_fill_progress", "tables", "Read hosted Table fill progress and any actionable ingestion failure."),
57
+ operation("tables.resource.kind_estimates", "/api/cli/tables/column-kind-estimates", "GET", "columns kind-estimates", "oxygen_column_kind_estimates", "tables", "Read managed credit estimates for available Table column kinds."),
58
+ operation("tables.resource.columns.create", `${table}/columns`, "POST", "columns create", "oxygen_table_column_create", "tables", "Create a Table column at an optional zero-based position.", { column: obj({ label: text(), key: text(), kind: text(), data_type: dataType(), semantic_type: text(), definition: json }, { required: ["label"] }), position: int({ minimum: 0 }) }, ["column"]),
59
+ operation("tables.resource.columns.edit", column, "PATCH", "columns edit", "oxygen_table_column_edit", "tables", "Save a column's label, type, order or definition. Returns downstream reference warnings rather than silently discarding them.", { label: text(), semantic_type: nullable(text()), definition: json, definition_unset: strings(), data_type: dataType(), position: int({ minimum: 0 }) }),
60
+ operation("tables.resource.columns.archive", `${column}/archive`, "POST", "columns archive", "oxygen_columns_archive", "tables", "Archive a Table column indefinitely while preserving its stored values.", {}, [], nativeCli),
61
+ operation("tables.resource.columns.duplicate", `${column}/duplicate`, "POST", "columns duplicate", "oxygen_table_column_duplicate", "tables", "Duplicate a Table column and its definition with an optional new label.", { label: text() }),
62
+ operation("tables.resource.columns.materialize", `${column}/materialize`, "POST", "columns materialize", "oxygen_columns_materialize", "tables", "Materialize JSON paths into existing or newly created companion columns.", { preset: enumOf(["work_email"]), mappings: arr(obj({ source_path: text(), target_column: text(), label: text(), data_type: dataType(), semantic_type: text() }, { required: ["source_path", "target_column"] })), create_if_missing: bool(), overwrite_existing: bool() }, [], nativeCli),
63
+ operation("tables.resource.columns.retype", `${column}/retype`, "POST", "columns retype", "oxygen_columns_retype", "tables", "Preview or apply a manual column's stored-value type conversion.", { data_type: dataType(), dry_run: bool() }, ["data_type"], nativeCli),
64
+ operation("tables.resource.columns.run", `${column}/run`, "POST", "columns execute", "oxygen_table_column_execute", "runs", "Run one column over an exact row or bounded selection. Paid execution requires approval and a max_credits ceiling; uncertain prior effects need explicit resolution approval.", { row_id: text(), force: bool(), limit: int({ minimum: 1 }), full_table_mode: enumOf(["missing", "force"]), max_credits: num({ minimum: 0 }), max_concurrency: int({ minimum: 1 }), connection_id: text(), approved: bool(), approved_effect_unknown: bool() }, [], { spends: true }),
65
+ operation("tables.resource.columns.web_search_mode", `${table}/columns/{column_ref}/web-search-mode`, "POST", "columns web-search-mode", "oxygen_table_column_web_search_mode", "tables", "Convert a web-search column between agent, AI or keyword modes. Conversion requires confirm=true; mode plus column seeds an AI or keyword replacement.", { to: enumOf(["agent", "ai", "keyword"]), confirm: bool(), mode: enumOf(["ai", "keyword"]), column: json, prompt: text() }),
66
+ operation("tables.resource.action_columns", `${table}/action-columns`, "POST", "columns provider-action", "oxygen_table_provider_action_column", "tables", "Preview or create a provider action column; creating it does not execute provider actions.", { provider: text(), label: text(), key: text(), operations: strings(), dry_run: bool() }, ["provider"]),
67
+ operation("tables.resource.rows.page", `${table}/rows`, "GET", "tables rows page", "oxygen_table_rows_page", "tables", "Read a Table row page with the same filter, sort, counts and CRM relationship projection as the grid.", { limit: int({ minimum: 1 }), cursor: text(), offset: int({ minimum: 0 }), f: text("Serialized filter tree."), s: text("Serialized sort state."), q: text(), count: enumOf(["exact", "estimate", "registry", "skip"]), crm_object: text() }),
68
+ operation("tables.resource.rows.insert", `${table}/rows`, "POST", "tables rows insert", "oxygen_table_rows_insert", "tables", "Insert Table row values and return the committed rows plus any existing automatic run or dedupe grant.", { rows: objects(), values: json }),
69
+ operation("tables.resource.rows.get", `${table}/rows/{row}`, "GET", "tables rows inspect", "oxygen_table_row_inspect", "tables", "Read one Table row; hydrate_relations=1 includes CRM relationship display values. The optional field list narrows cell hydration to exact column keys.", { hydrate_relations: enumOf(["1"]), field: strings() }, [], { repeatedQueryFields: ["field"] }),
70
+ operation("tables.resource.rows.edit", `${table}/rows/{row}`, "PATCH", "tables rows edit", "oxygen_table_row_edit", "tables", "Patch one row's values and return the committed row.", { values: json }, ["values"]),
71
+ operation("tables.resource.rows.delete", `${table}/rows/{row}`, "DELETE", "tables rows remove", "oxygen_table_row_remove", "tables", "Remove one row from a workspace Table."),
72
+ operation("tables.resource.rows.bulk_delete", `${table}/rows/delete`, "POST", "tables rows remove-selection", "oxygen_table_rows_remove_selection", "tables", "Remove exact row ids or the same all-matching filtered selection used by the browser.", { row_ids: strings(), selection }),
73
+ operation("tables.resource.rows.ids", `${table}/rows/ids`, "GET", "tables rows range-ids", "oxygen_table_rows_range_ids", "tables", "Resolve a bounded row range into exact ids, preserving the current filter, search, sort and CRM scope.", { offset: int({ minimum: 0 }), limit: int({ minimum: 1 }), filter: text(), search: text(), sort: text(), crm_object: text() }, ["offset", "limit"]),
74
+ operation("tables.resource.relationships.get", `${table}/records/{row}/relationships`, "GET", "tables rows relationships", "oxygen_table_row_relationships", "crm", "Read a CRM-backed Table row's relationship links and available targets."),
75
+ operation("tables.resource.relationships.set", `${table}/records/{row}/relationships`, "POST", "tables rows relationship-set", "oxygen_table_row_relationship_set", "crm", "Set one CRM-backed Table row's relationship and return the saved links.", { relationship: text(), target_row_id: text() }, ["relationship", "target_row_id"]),
76
+ operation("tables.resource.views.list", `${table}/views`, "GET", "tables views list", "oxygen_tables_views_list", "tables", "List saved Table views and their complete display configuration.", {}, [], nativeCli),
77
+ operation("tables.resource.views.create", `${table}/views`, "POST", "tables views create", "oxygen_tables_views_create", "tables", "Create a saved Table view; group_by configures the same kanban grouping as the browser.", viewFields, ["name"], nativeCli),
78
+ operation("tables.resource.views.edit", `${table}/views/{view}`, "PATCH", "tables views update", "oxygen_tables_views_update", "tables", "Edit a saved view's name, type, configuration, grouping, order or default status.", viewFields, [], nativeCli),
79
+ operation("tables.resource.views.delete", `${table}/views/{view}`, "DELETE", "tables views delete", "oxygen_tables_views_delete", "tables", "Delete one saved Table view.", {}, [], nativeCli),
80
+ operation("tables.resource.runs.list", `${table}/runs`, "GET", "tables runs history", "oxygen_table_runs_history", "runs", "Read this Table's durable run history and progress.", { status: enumOf(["all", "active", "queued", "running", "paused", "completed", "completed_with_errors", "failed", "canceling", "canceled"]), limit: int({ minimum: 1 }) }),
81
+ operation("tables.resource.runs.start", `${table}/runs`, "POST", "tables runs start", "oxygen_table_resource_run_start", "runs", "Start selected Table actions using the browser's exact row-selection contract. Paid runs require an approved max_credits ceiling.", { actions, selection, max_credits: num({ minimum: 0 }), max_concurrency: int({ minimum: 1 }), force: bool(), approved: bool() }, ["actions"], { spends: true }),
82
+ operation("tables.resource.runs.estimate", `${table}/runs/estimate`, "POST", "tables runs preview", "oxygen_table_resource_run_preview", "runs", "Preview selected Table action costs and provider availability, including draft column definitions; starts no run.", { actions, selection }, ["actions"], { mutates: false }),
83
+ operation("tables.resource.runs.cancel", `${run}/cancel`, "POST", "tables runs stop", "oxygen_table_resource_run_stop", "runs", "Cancel one durable Table run; never repeats external provider effects."),
84
+ operation("tables.resource.runs.pause", `${run}/pause`, "POST", "tables runs pause", "oxygen_table_resource_run_pause", "runs", "Pause one durable Table run without discarding its approved scope."),
85
+ operation("tables.resource.runs.resume", `${run}/resume`, "POST", "tables runs resume", "oxygen_table_resource_run_resume", "runs", "Resume a paused Table run within its existing approved scope and credit ceiling."),
86
+ operation("tables.resource.runs.retry", `${run}/retry-failed`, "POST", "tables runs retry", "oxygen_table_resource_run_retry", "runs", "Retry failed cells within a Table run's approved scope. Uncertain external effects require approved_effect_unknown=true.", { approved_effect_unknown: bool() }, [], { spends: true }),
87
+ operation("tables.resource.runs.provider_summary", `${run}/provider-summary`, "GET", "tables runs provider-summary", "oxygen_table_resource_run_provider_summary", "runs", "Read a Table run's provider outcomes, fulfilled rows, costs and timing."),
88
+ operation("tables.resource.ingestions.cancel", `${table}/ingestions/{ingestion_run}/cancel`, "POST", "tables ingestion stop", "oxygen_table_resource_ingestion_stop", "runs", "Cancel this Table's active hosted ingestion run."),
89
+ operation("tables.resource.ingestions.retry", `${table}/ingestions/{ingestion_run}/retry-failed`, "POST", "tables ingestion retry", "oxygen_table_resource_ingestion_retry", "runs", "Retry failed items in this Table's hosted ingestion run."),
90
+ operation("tables.resource.import", "/api/cli/tables/import", "POST", "tables upload", "oxygen_table_resource_upload", "tables", "Preview or import small uploaded CSV, JSON or XLSX bytes through the exact browser import contract. Preview writes no rows.", upload, ["content_base64", "file_name"], { multipart }),
91
+ operation("billing.activate", "/api/cli/billing/activate", "POST", "billing activate", "oxygen_billing_activate", "billing", "Activate a trial subscription now and charge its saved payment method; administrator approval is required. An optional tier reprices the active trial in the same call.", { approved: bool(), tier: text() }, ["approved"], { spends: true }),
92
+ operation("billing.checkout", "/api/cli/billing/checkout", "POST", "billing checkout", "oxygen_billing_checkout", "billing", "Open the same hosted plan checkout as the browser; the returned URL carries the payment approval step.", { plan: text(), interval: enumOf(["month", "year"]), currency: text(), trial: bool(), return_to: text(), source: text(), blocked: text() }, ["plan"]),
93
+ operation("billing.portal", "/api/cli/billing/portal", "POST", "billing portal", "oxygen_billing_portal", "billing", "Create the workspace's authenticated Stripe billing-portal URL; requires workspace administrator authorization.", { returnUrl: text() }),
94
+ operation("billing.ownership.get", "/api/cli/billing/ownership", "GET", "billing ownership get", "oxygen_billing_ownership_get", "billing", "Read the active workspace's shared billing owner, candidate owners and credit cap."),
95
+ operation("billing.ownership.set", "/api/cli/billing/ownership", "POST", "billing ownership set", "oxygen_billing_ownership_set", "billing", "Set the billing owner and optional monthly credit cap using the browser's administrator and workspace eligibility checks.", { billing_owner_organization_id: text(), monthly_credit_cap: nullable(num({ minimum: 0 })) }, ["billing_owner_organization_id"]),
96
+ operation("billing.ownership.clear", "/api/cli/billing/ownership", "DELETE", "billing ownership clear", "oxygen_billing_ownership_clear", "billing", "Restore this workspace's own billing ownership after the same administrator checks as the browser."),
97
+ operation("integrations.default", "/api/cli/integrations/default", "POST", "integrations default", "oxygen_integration_default_set", "integrations", "Set the default active integration connection within this workspace.", { integration_id: text(), connection_id: text() }, ["integration_id", "connection_id"]),
98
+ operation("integrations.cloudflare", "/api/cli/integrations/cloudflare/connect", "POST", "integrations cloudflare-connect", "oxygen_cloudflare_connect", "integrations", "Verify a Cloudflare token, choose an account when required, and save the encrypted connection. account_id is optional for initial account discovery; no credential is returned.", { api_key: text(), account_id: text() }, ["api_key"]),
99
+ operation("integrations.github.start", "/api/cli/integrations/github/start", "POST", "integrations github-start", "oxygen_github_connection_start", "integrations", "Create the authenticated GitHub authorization URL for this workspace.", { return_to: text() }),
100
+ operation("integrations.hubspot.start", "/api/cli/integrations/hubspot/start", "POST", "integrations hubspot-start", "oxygen_hubspot_connection_start", "integrations", "Create the authenticated HubSpot authorization URL for this workspace.", { return_to: text() }),
101
+ operation("integrations.connection.test", "/api/cli/integrations/{integration}/test", "POST", "integrations connection-test", "oxygen_integration_connection_test", "integrations", "Test one workspace integration connection and save its health without changing its default or returning credentials.", { connection_id: text() }),
102
+ operation("workflows.resource.status", "/api/cli/workflows/{workflow}/status", "POST", "workflows status-set", "oxygen_workflow_resource_status_set", "workflows", "Enable or disable a Workflow and record an optional reason. Enabling paid recurring execution requires standing approval and max_credits; CRM-sync workflows use their paired lifecycle instead.", { status: enumOf(["active", "disabled"]), approved: bool(), max_credits: num({ minimum: 0 }), reason: str({ maxLength: 500 }) }, ["status"], { spends: true }),
103
+ operation("notetaker.desktop_upload", "/api/cli/notetaker/desktop-upload", "POST", "notetaker desktop-upload", "oxygen_notetaker_desktop_upload_prepare", "crm", "Prepare an approved desktop meeting recording and its scoped upload URL. Upload and completion remain separate steps.", { approved: bool(), title: text(), max_minutes: int({ minimum: 1 }), crm_links: crmLinks }, ["approved"]),
104
+ operation("notetaker.calendar.connect", "/api/cli/notetaker/calendar", "POST", "notetaker calendar connect", "oxygen_notetaker_calendar_connect", "crm", "Create the OAuth authorization URL for a Google Calendar or Outlook notetaker connection.", { integration_id: enumOf(["google_calendar", "outlook"]) }, ["integration_id"], { defaults: { action: "connect" } }),
105
+ operation("notetaker.calendar.sync", "/api/cli/notetaker/calendar", "POST", "notetaker calendar sync", "oxygen_notetaker_calendar_sync", "crm", "Synchronize upcoming Google Calendar or Outlook meeting events into this workspace's notetaker calendar.", { integration_id: enumOf(["google_calendar", "outlook"]), connection_id: text(), days_ahead: int({ minimum: 1 }) }, ["integration_id"], { defaults: { action: "sync" } }),
106
+ operation("notetaker.calendar.schedule", "/api/cli/notetaker/calendar", "POST", "notetaker calendar schedule", "oxygen_notetaker_calendar_schedule", "crm", "Schedule recording for an existing calendar event after approval; an external CLI or MCP caller must supply an explicit max_credits ceiling.", { event_id: text(), approved: bool(), max_credits: num({ minimum: 0 }) }, ["event_id", "approved"], { defaults: { action: "schedule" }, spends: true }),
107
+ operation("notetaker.note.edit", "/api/cli/notetaker/sessions/{session}/note", "PATCH", "notetaker notes edit", "oxygen_notetaker_note_edit", "crm", "Save the markdown note for one meeting session and return the committed note.", { markdown: text() }, ["markdown"]),
108
+ operation("orgs.create", "/api/cli/orgs/create", "POST", "orgs create", "oxygen_workspace_create", "workspace", "Create a workspace for the authenticated person's account and begin native company setup. Workspace API keys cannot create personal workspaces.", { name: text(), company_domain: text("Company website or domain, or its LinkedIn company page URL when it has no website."), linkedin_profile_url: text("Creator's LinkedIn profile URL, separate from the company's page.") }, ["name", "company_domain"]),
109
+ operation("onboarding.complete", "/api/cli/onboarding/complete", "POST", "onboarding complete", "oxygen_onboarding_complete", "workspace", "Complete the active workspace's onboarding step and record its discovery-source choices. The other choice requires source_other.", { sources: arr(enumOf(["linkedin_content", "linkedin_dm", "linkedin_ads", "youtube", "google", "email", "word_of_mouth", "referral", "event", "other"])), source_other: text(), redirect_url: text() }, ["sources"]),
110
+ ];
@@ -0,0 +1,4 @@
1
+ import type { ObjectProperty } from "./schema.js";
2
+ /** Custom CRM column writes accept both canonical snake_case and legacy aliases. */
3
+ export declare const CRM_CUSTOM_COLUMN_SCHEMA: ObjectProperty;
4
+ export declare const CRM_CUSTOM_IDENTITY_SCHEMA: ObjectProperty;
@@ -0,0 +1,39 @@
1
+ /** Custom CRM column writes accept both canonical snake_case and legacy aliases. */
2
+ export const CRM_CUSTOM_COLUMN_SCHEMA = {
3
+ type: "object",
4
+ properties: {
5
+ key: { type: "string" }, label: { type: "string" },
6
+ data_type: { type: "string", enum: ["text", "numeric", "boolean", "jsonb", "timestamptz"] },
7
+ dataType: { type: "string", enum: ["text", "numeric", "boolean", "jsonb", "timestamptz"] },
8
+ semantic_type: { type: "string" }, semanticType: { type: "string" },
9
+ kind: { type: "string" }, attribute_kind: { type: "string" }, attributeKind: { type: "string" },
10
+ option_model_type: { type: "string" }, optionModelType: { type: "string" },
11
+ is_required: { type: "boolean" }, isRequired: { type: "boolean" },
12
+ is_unique: { type: "boolean" }, isUnique: { type: "boolean" },
13
+ is_multivalue: { type: "boolean" }, isMultivalue: { type: "boolean" },
14
+ is_record_label: { type: "boolean" }, isRecordLabel: { type: "boolean" },
15
+ is_record_image: { type: "boolean" }, isRecordImage: { type: "boolean" },
16
+ kanban_candidate: { type: "boolean" }, kanbanCandidate: { type: "boolean" },
17
+ display_order: { type: "number" }, displayOrder: { type: "number" },
18
+ options: { type: "array", items: { type: "object", additionalProperties: true } },
19
+ definition: { type: "object", additionalProperties: true },
20
+ attribute_metadata: { type: "object", additionalProperties: true },
21
+ attributeMetadata: { type: "object", additionalProperties: true },
22
+ },
23
+ required: ["key", "label"],
24
+ additionalProperties: false,
25
+ };
26
+ export const CRM_CUSTOM_IDENTITY_SCHEMA = {
27
+ type: "object",
28
+ properties: {
29
+ column_key: { type: "string" }, columnKey: { type: "string" },
30
+ normalization: { type: "string", enum: ["email_v1", "domain_v1", "linkedin_url_v1", "exact_text_v1", "uuid_v1"] },
31
+ slug: { type: "string" }, display_name: { type: "string" }, displayName: { type: "string" },
32
+ identity_type: { type: "string" }, identityType: { type: "string" },
33
+ is_primary: { type: "boolean" }, isPrimary: { type: "boolean" },
34
+ is_required_for_assert: { type: "boolean" }, isRequiredForAssert: { type: "boolean" },
35
+ priority: { type: "number" },
36
+ },
37
+ required: ["normalization"],
38
+ additionalProperties: false,
39
+ };
@@ -0,0 +1,21 @@
1
+ /** Read selectors only; action=backfill is a separate governed operation. */
2
+ export declare const CRM_ENRICHMENT_READ_CONTRACT: import("./schema.js").ContractObject<{
3
+ readonly object: import("./schema.js").StringProperty;
4
+ readonly backfill: import("./schema.js").BooleanProperty;
5
+ readonly projection: {
6
+ type: "string";
7
+ enum: readonly ("full" | "summary" | "value")[];
8
+ description?: string;
9
+ };
10
+ readonly path: import("./schema.js").StringProperty;
11
+ readonly cursor: import("./schema.js").StringProperty;
12
+ readonly max_chars: {
13
+ type: "integer";
14
+ description?: string;
15
+ minimum?: number;
16
+ maximum?: number;
17
+ "x-clamp"?: true;
18
+ };
19
+ }, never>;
20
+ /** Preserve the shipped HTTP literals, including whitespace/case and 0. */
21
+ export declare function includeCrmEnrichmentBackfillCounts(value: string | null): boolean;
@@ -0,0 +1,15 @@
1
+ import { bool, enumOf, int, obj, str } from "./schema.js";
2
+ /** Read selectors only; action=backfill is a separate governed operation. */
3
+ export const CRM_ENRICHMENT_READ_CONTRACT = obj({
4
+ object: str({ minLength: 1, maxLength: 120, description: "CRM object slug (companies, people, deals)." }),
5
+ backfill: bool({ description: "list without object: include stored historical backlog counts (default true). false skips the scans; object reads ignore it. Never runs enrichment." }),
6
+ projection: enumOf(["summary", "full", "value"], { description: "summary: standing configuration without long arrays. full: legacy data. value: lossless JSON of path, continued with next_cursor. Omit for legacy output." }),
7
+ path: str({ maxLength: 1024, description: "value: JSON Pointer into the selected native result; empty selects everything. Examples /objects/0/auto_run or /presets." }),
8
+ cursor: str({ minLength: 1, maxLength: 2000, description: "value: previous next_cursor with unchanged object, backfill, path and projection. Refuses changed source." }),
9
+ max_chars: int({ minimum: 1000, maximum: 40000, description: "Opt-in complete receipt window. MCP caps external at 10000 and built-in at 4000. Use value to recover oversized fields." }),
10
+ });
11
+ /** Preserve the shipped HTTP literals, including whitespace/case and 0. */
12
+ export function includeCrmEnrichmentBackfillCounts(value) {
13
+ const normalized = value?.trim().toLowerCase();
14
+ return normalized !== "0" && normalized !== "false";
15
+ }
@@ -0,0 +1,2 @@
1
+ import type { OperationSpec } from "./operations.js";
2
+ export declare const CUSTOMER_OPERATIONS: readonly OperationSpec[];
@@ -0,0 +1,131 @@
1
+ /** Customer operations missing from one execution surface, with one field contract. */
2
+ import { arr, bool, enumOf, int, nullable, num, obj, str } from "./schema.js";
3
+ import { DATABASE_ATTACH_CONTRACT, DATABASE_MIGRATE_CONTRACT, DATABASE_QUERY_CONTRACT } from "./database.js";
4
+ const text = (description) => str(description === undefined ? {} : { description });
5
+ const list = () => arr(str());
6
+ const json = { type: "object", additionalProperties: true };
7
+ const rows = () => arr(json);
8
+ function operation(id, route, method, cli, mcp, pack, summary, fields = {}, required = [], options = {}) {
9
+ const { annotations, ...behavior } = options;
10
+ const parameters = [...route.matchAll(/\{(\w+)\}/g)].map((match) => match[1]);
11
+ return {
12
+ id, route, method, parameters,
13
+ contract: obj(fields, { required }),
14
+ cli: { path: cli.split(" "), summary },
15
+ mcp: { name: mcp, title: `Oxygen: ${cli}`, description: summary, pack, ...(annotations ? { annotations } : {}) },
16
+ mutates: method !== "GET", spends: false, ...behavior,
17
+ };
18
+ }
19
+ const mcpOnly = { generated: { cli: false, mcp: true } };
20
+ const cliOnly = { generated: { cli: true, mcp: false } };
21
+ export const CUSTOMER_OPERATIONS = [
22
+ operation("activation.state", "/api/cli/activation/state", "GET", "activation state", "oxygen_activation_state", "workspace", "Read this workspace's onboarding activation state and next available actions.", {}, [], mcpOnly),
23
+ operation("api_keys.list", "/api/cli/api-keys", "GET", "api-keys list", "oxygen_api_keys_list", "workspace", "List workspace API keys without revealing their tokens.", {}, [], mcpOnly),
24
+ operation("api_keys.create", "/api/cli/api-keys", "POST", "api-keys create", "oxygen_api_keys_create", "workspace", "Create a workspace API key; requires the caller's personal user session. The token is returned once.", { name: text(), expires_at: text("ISO timestamp; omit for no expiry.") }, [], mcpOnly),
25
+ operation("api_keys.revoke", "/api/cli/api-keys/{key_id}", "DELETE", "api-keys revoke", "oxygen_api_keys_revoke", "workspace", "Revoke a workspace API key; requires the caller's personal user session.", {}, [], mcpOnly),
26
+ operation("comments.edit", "/api/cli/collab/comments/{comment_id}", "PATCH", "comments edit", "oxygen_comments_edit", "collaboration", "Edit your own internal workspace comment; records the edit time.", { body: text() }, ["body"], mcpOnly),
27
+ operation("comments.delete", "/api/cli/collab/comments/{comment_id}", "DELETE", "comments delete", "oxygen_comments_delete", "collaboration", "Retract your own internal workspace comment while retaining its audit trail.", {}, [], mcpOnly),
28
+ operation("approvals.cancel", "/api/cli/collab/requests/{request_id}/cancel", "POST", "approvals cancel", "oxygen_approvals_cancel", "collaboration", "Withdraw an approval request you created; grants no approval.", { note: text() }, [], mcpOnly),
29
+ operation("members.client_role", "/api/cli/collab/members/role", "POST", "approvals client grant", "oxygen_members_client_role_set", "collaboration", "Set or clear a member's restricted client role; workspace admin only.", { email: text(), role: nullable(enumOf(["client"])) }, ["email", "role"], mcpOnly),
30
+ operation("members.workspace_role", "/api/cli/collab/members/workspace-role", "POST", "orgs member-role", "oxygen_members_workspace_role_set", "collaboration", "Change a member's workspace role; admin only and the last administrator is protected.", { email: text(), role: enumOf(["admin", "member"]) }, ["email", "role"], mcpOnly),
31
+ operation("copilot.sessions.list", "/api/cli/copilot/sessions", "GET", "copilot list", "oxygen_copilot_sessions_list", "agents", "List attended Copilot sessions in this workspace.", { status: text(), limit: int({ minimum: 1 }) }, [], mcpOnly),
32
+ operation("copilot.sessions.update", "/api/cli/copilot/sessions/{session_id}", "PATCH", "copilot policy set", "oxygen_copilot_session_update", "agents", "Set an attended Copilot session's approval and screen-follow policies. Paid auto-approval is explicit standing authorization.", { auto_approve_policy: enumOf(["off", "paid"]), surface_policy: enumOf(["off", "follow"]) }, [], { spends: true }),
33
+ operation("copilot.attachments.list", "/api/cli/copilot/sessions/{session_id}/attachments", "GET", "copilot attachments list", "oxygen_copilot_attachments_list", "agents", "List sent and unsent attachments in one Copilot session.", {}, []),
34
+ operation("copilot.attachments.prepare", "/api/cli/copilot/sessions/{session_id}/attachments", "POST", "copilot attachments prepare", "oxygen_copilot_attachment_prepare", "agents", "Reserve a Copilot attachment and return its scoped upload URL; complete only after uploading the exact bytes.", { file_name: text(), content_type: text(), content_length: int({ minimum: 1 }) }, ["file_name", "content_length"]),
35
+ operation("copilot.attachments.complete", "/api/cli/copilot/sessions/{session_id}/attachments/{attachment_id}/complete", "POST", "copilot attachments complete", "oxygen_copilot_attachment_complete", "agents", "Verify and describe an uploaded Copilot attachment in its session.", {}, []),
36
+ operation("copilot.attachments.delete", "/api/cli/copilot/sessions/{session_id}/attachments/{attachment_id}", "DELETE", "copilot attachments delete", "oxygen_copilot_attachment_delete", "agents", "Detach an unsent Copilot attachment; attachments already quoted by a turn cannot be removed."),
37
+ operation("copilot.feedback", "/api/cli/copilot/sessions/{session_id}/turns/{turn_id}/feedback", "POST", "copilot feedback", "oxygen_copilot_feedback", "agents", "Rate one attended Copilot turn and optionally explain the rating.", { rating: enumOf(["up", "down"]), comment: text() }, ["rating"]),
38
+ operation("copilot.interrupt", "/api/cli/copilot/sessions/{session_id}/turns/{turn_id}/interrupt", "POST", "copilot interrupt", "oxygen_copilot_interrupt", "agents", "Request cancellation of one Copilot turn. Read the session again to confirm it stopped.", {}, [], mcpOnly),
39
+ operation("context.canonical.get", "/api/cli/context/canonical/get", "POST", "context canonical get", "oxygen_context_canonical_get", "knowledge", "Read the pinned active context asset for a type; present=false means none is set.", { type: text() }, ["type"], { mutates: false }),
40
+ operation("crm.notes.update", "/api/cli/crm/notes/{note_id}", "PATCH", "crm notes update", "oxygen_crm_note_update", "crm", "Edit a CRM note's body, title or pinned state; an empty title clears it.", { body: text(), title: nullable(text()), pinned: bool() }, [], mcpOnly),
41
+ operation("crm.notes.delete", "/api/cli/crm/notes/{note_id}", "DELETE", "crm notes delete", "oxygen_crm_note_delete", "crm", "Delete one CRM note in this workspace.", {}, [], mcpOnly),
42
+ operation("crm.files.attach_inline", "/api/cli/crm/objects/{object}/records/{row_id}/files", "POST", "crm files attach-inline", "oxygen_crm_file_attach", "crm", "Attach base64 file bytes to one CRM record. For larger files use prepare-upload and complete-upload.", { name: text(), content_base64: text(), label: text() }, ["name", "content_base64"]),
43
+ operation("crm.files.prepare", "/api/cli/crm/objects/{object}/records/{row_id}/files/upload-url", "POST", "crm files prepare-upload", "oxygen_crm_file_prepare_upload", "crm", "Reserve a CRM file upload by exact name, length and SHA-256; returns a scoped upload URL.", { name: text(), byte_length: int({ minimum: 0 }), sha256: text() }, ["name", "byte_length", "sha256"]),
44
+ operation("crm.files.complete", "/api/cli/crm/objects/{object}/records/{row_id}/files/{file_id}/uploaded", "POST", "crm files complete-upload", "oxygen_crm_file_complete_upload", "crm", "Verify a reserved CRM upload and attach it to its record; mismatched bytes remain pending.", { label: text() }),
45
+ operation("crm.files.delete", "/api/cli/crm/files/{attachment_id}", "DELETE", "crm files detach", "oxygen_crm_file_delete", "crm", "Detach a CRM attachment by attachment_id, preserving the stored file.", {}, [], mcpOnly),
46
+ operation("crm.photos", "/api/cli/crm/photos", "POST", "crm photos", "oxygen_crm_photos_backfill", "crm", "Preview or fill people photos from data this workspace already holds. Free; mode defaults to dry_run.", { mode: enumOf(["dry_run", "live"]), limit: int({ minimum: 1, maximum: 10000 }) }, [], mcpOnly),
47
+ operation("dashboards.tabs.create", "/api/cli/dashboards/{dashboard_id}/tabs", "POST", "dashboards tabs add", "oxygen_dashboard_tab_create", "signals", "Create a named tab in a workspace dashboard.", { title: text() }, ["title"], mcpOnly),
48
+ operation("dashboards.tabs.delete", "/api/cli/dashboards/{dashboard_id}/tabs", "DELETE", "dashboards tabs remove", "oxygen_dashboard_tab_delete", "signals", "Remove one dashboard tab by its id.", { tab: text() }, ["tab"], { ...mcpOnly, queryFields: ["tab"] }),
49
+ operation("integrations.custom.connect", "/api/cli/custom-integrations/connect", "POST", "custom-integrations connect", "oxygen_custom_integration_connect", "integrations", "Store an encrypted API key for an applied custom HTTP integration; the secret is never returned.", { slug: text(), api_key: text() }, ["slug", "api_key"], mcpOnly),
50
+ operation("integrations.connections", "/api/cli/integrations/connections", "GET", "integrations connections", "oxygen_integrations_connections", "integrations", "List this workspace's stored integration connections and health without credentials.", { integration_id: text() }),
51
+ operation("db.attach", "/api/cli/db/attach", "POST", "db attach", "oxygen_database_attach", "workspace", "Attach an owned PostgreSQL database and initialize this workspace's schema/roles immediately. Changes the workspace database; no preview or confirmation field. Native workspace write access applies. Use Supabase import tools for external Tables. 0 credits.", {}, [], { ...mcpOnly, contract: DATABASE_ATTACH_CONTRACT, annotations: { destructiveHint: true, openWorldHint: true } }),
52
+ operation("db.migrate", "/api/cli/db/migrate", "POST", "db migrate", "oxygen_database_migrate", "workspace", "Apply pending workspace database migrations immediately; optionally repair runtime/read passwords. Read oxygen_workspace_database_status first. No preview or confirmation field; native workspace write access applies. Fleet repair remains separate. 0 credits.", {}, [], { ...mcpOnly, contract: DATABASE_MIGRATE_CONTRACT, annotations: { destructiveHint: true } }),
53
+ operation("db.query", "/api/cli/db/query", "POST", "db query", "oxygen_database_query", "workspace", "Read this workspace's database with one bounded SQL statement. Complete rows, default 20, maximum 1000; writes refused. On truncation, narrow SELECT/WHERE or use your own stable ORDER BY/keyset predicate; no server cursor. 0 credits.", {}, [], { contract: DATABASE_QUERY_CONTRACT, generated: { cli: false, mcp: false }, mutates: false }),
54
+ operation("db.cost_policy.reconcile", "/api/cli/db/reconcile-cost-policy", "POST", "db reconcile-cost-policy", "oxygen_database_cost_policy_reconcile", "workspace", "Retired Neon compatibility action: reports applied=false with no operations; does not suspend PostgreSQL. Native workspace write access applies; no preview or confirmation field. 0 credits.", { suspend_idle: bool() }, [], mcpOnly),
55
+ operation("directory.apply", "/api/cli/directory/apply", "POST", "directory apply", "oxygen_directory_apply", "crm", "Apply for an agency-directory listing for this workspace; reuses an existing open application.", { message: text(), website: text() }, [], mcpOnly),
56
+ operation("directory.logo", "/api/cli/directory/listing/logo", "POST", "directory logo upload", "oxygen_directory_logo_upload", "crm", "Upload a public agency-directory logo and read back the updated listing; PNG, JPEG, WebP or SVG, at most 2 MB.", { content_base64: text(), file_name: text(), content_type: enumOf(["image/png", "image/jpeg", "image/webp", "image/svg+xml"]) }, ["content_base64", "file_name", "content_type"], { multipart: { contentField: "content_base64", nameField: "file_name", typeField: "content_type", fileField: "file" } }),
57
+ operation("domains.delete", "/api/cli/domains/delete", "POST", "domains delete", "oxygen_domains_delete", "deliverability", "Preview domain removal and renewal cancellation; execute only with the exact plan hash and confirmation.", { domains: list(), approved: bool(), plan_hash: text(), confirmation: text() }, ["domains"], mcpOnly),
58
+ operation("knowledge.pages.archive", "/api/cli/knowledge/pages/archive", "POST", "knowledge page archive", "oxygen_knowledge_page_archive", "knowledge", "Archive a knowledge page while preserving its history.", { slug: text(), id: text(), expected_revision: int({ minimum: 1 }) }, [], mcpOnly),
59
+ operation("knowledge.sync.manifest", "/api/cli/knowledge/sync/manifest", "POST", "knowledge sync-manifest", "oxygen_knowledge_sync_manifest", "knowledge", "Read the wiki revision and content-hash inventory without downloading instruction bodies.", {}, [], { mutates: false }),
60
+ operation("knowledge.sync.delta", "/api/cli/knowledge/sync/delta", "POST", "knowledge sync", "oxygen_knowledge_sync_delta", "knowledge", "Read one resumable batch of wiki pages and tombstones, bounded by the server's byte budget.", { cursor: text(), limit: int({ minimum: 1, maximum: 200 }) }, [], { ...mcpOnly, mutates: false }),
61
+ operation("limits.provider.get", "/api/cli/limits/provider", "GET", "limits provider list", "oxygen_provider_limits_list", "billing", "Read effective workspace provider rate limits.", { provider: text() }, [], mcpOnly),
62
+ operation("limits.provider.set", "/api/cli/limits/provider", "POST", "limits provider set", "oxygen_provider_limits_set", "billing", "Set a workspace provider rate limit; admin or owner only.", { provider: text(), operation: text(), limit: int({ minimum: 1 }), window_seconds: int({ minimum: 1 }) }, ["provider", "limit", "window_seconds"], mcpOnly),
63
+ operation("limits.provider.delete", "/api/cli/limits/provider", "DELETE", "limits provider delete", "oxygen_provider_limits_delete", "billing", "Clear a workspace provider rate-limit override; admin or owner only.", { provider: text(), operation: text() }, ["provider"], { ...mcpOnly, queryFields: ["provider", "operation"] }),
64
+ operation("mailboxes.emailguard.auto_enroll.get", "/api/cli/mailboxes/emailguard/auto-enroll", "GET", "mailboxes emailguard auto-enroll", "oxygen_mailboxes_emailguard_auto_enroll_get", "deliverability", "Read the workspace's automatic monitoring enrollment grant and last sweep.", {}, [], mcpOnly),
65
+ operation("mailboxes.emailguard.auto_enroll.set", "/api/cli/mailboxes/emailguard/auto-enroll", "POST", "mailboxes emailguard auto-enroll", "oxygen_mailboxes_emailguard_auto_enroll_set", "deliverability", "Preview, arm or disarm automatic mailbox monitoring. Arming recurring spend requires approved=true and max_credits_per_cycle.", { enabled: bool(), approved: bool(), max_credits_per_cycle: num({ minimum: 0 }), run: bool(), dry_run: bool() }, [], { ...mcpOnly, spends: true }),
66
+ operation("mailboxes.warmup_profiles.get", "/api/cli/mailboxes/warmup-profile", "GET", "mailboxes warmup profile", "oxygen_mailboxes_warmup_profiles", "deliverability", "Read available OXYGEN Warm-up profiles and current bindings.", {}, [], mcpOnly),
67
+ operation("mailboxes.warmup_profiles.set", "/api/cli/mailboxes/warmup-profile", "POST", "mailboxes warmup profile", "oxygen_mailboxes_warmup_profile_set", "deliverability", "Preview or apply an OXYGEN Warm-up profile to exact mailboxes or domains; approved=true applies the reviewed scope.", { profile: { oneOf: [text(), json, { type: "null" }] }, budget: nullable(json), domains: list(), mailboxes: list(), all: bool(), approved: bool() }, [], mcpOnly),
68
+ operation("managed_inboxes.avatars.prepare", "/api/cli/managed-inboxes/avatars", "POST", "managed-inboxes avatars prepare", "oxygen_managed_inbox_avatar_prepare", "deliverability", "Prepare a scoped mailbox profile-picture upload; confirm the stored bytes before using its URL.", { content_type: enumOf(["image/png", "image/jpeg", "image/webp"]), content_length: int({ minimum: 1 }) }, ["content_length"]),
69
+ operation("managed_inboxes.avatars.confirm", "/api/cli/managed-inboxes/avatars", "PATCH", "managed-inboxes avatars confirm", "oxygen_managed_inbox_avatar_confirm", "deliverability", "Verify and normalize a mailbox profile picture owned by this workspace.", { storage_key: text() }, ["storage_key"]),
70
+ operation("managed_inboxes.domains.search", "/api/cli/managed-inboxes/domains/search", "POST", "managed-inboxes search-domains", "oxygen_managed_inbox_domains_search", "deliverability", "Search available domains for managed mailboxes; buys nothing.", { keyword: text(), tlds: list(), limit: int({ minimum: 1 }) }, ["keyword"], { mutates: false }),
71
+ operation("publishing.drafts.update", "/api/cli/publishing/drafts/{draft_id}", "PATCH", "publishing drafts edit", "oxygen_publishing_draft_update", "publishing", "Edit a queued publishing draft's text without publishing it.", { text: text(), variant_index: int({ minimum: 0 }) }, ["text"], mcpOnly),
72
+ operation("publishing.mentions.search", "/api/cli/publishing/mentions/search", "POST", "publishing mentions search", "oxygen_publishing_mentions_search", "publishing", "Find LinkedIn company or person identities for a mention; free, subject to the connected account's read quota.", { query: text(), kind: enumOf(["company", "person"]), connection_id: text() }, ["query"], { mutates: false }),
73
+ operation("settings.beta.get", "/api/cli/settings/beta-features", "GET", "settings beta-features get", "oxygen_settings_beta_features_get", "workspace", "Read this workspace's beta-navigation opt-in."),
74
+ operation("settings.beta.set", "/api/cli/settings/beta-features", "POST", "settings beta-features set", "oxygen_settings_beta_features_set", "workspace", "Enable or disable beta navigation for this workspace; admin or owner only.", { enabled: bool() }, ["enabled"]),
75
+ operation("spintax.generate", "/api/cli/spintax", "POST", "spintax generate", "oxygen_spintax_generate", "sequences", "Generate spintax for outreach text. Costs exactly 3 credits; failed generation refunds the reservation.", { text: str({ minLength: 1, maxLength: 4000 }), section: str({ maxLength: 80 }) }, ["text"], { spends: true }),
76
+ operation("table_action_items.resolve", "/api/cli/table-action-items/{item_id}/resolve", "POST", "table-actions resolve", "oxygen_table_action_item_resolve", "tables", "Record a verified uncertain Table action outcome; never repeats the external action.", { outcome: enumOf(["applied", "not_applied"]), note: text(), value: { oneOf: [text(), num(), bool(), json, rows(), { type: "null" }] } }, ["outcome"]),
77
+ operation("table_ingestions.append", "/api/cli/table-ingestion-runs/{ingestion_run_id}/items", "POST", "table-ingestions append", "oxygen_table_ingestions_append_items", "runs", "Append bounded ingestion items to an existing run; preserves the run's approved scope.", { items: rows(), max_attempts: int({ minimum: 1 }) }, ["items"], cliOnly),
78
+ operation("tables.favorites.get", "/api/cli/tables/favorite", "GET", "tables favorites", "oxygen_tables_favorites", "tables", "Read this member's table and folder favorites in the active workspace. A per-member preference; free (0 credits)."),
79
+ operation("tables.favorite.set", "/api/cli/tables/favorite", "POST", "tables favorite", "oxygen_tables_favorite", "tables", "Star or unstar exactly one table or folder in the active workspace. Free (0 credits); reads back the saved per-member preference.", { table: text("Table id or slug. Pass exactly one of table or folder."), folder: text("Folder id or slug. Pass exactly one of table or folder."), favorite: bool({ description: "true stars; false unstars. Free (0 credits); changes only this member's preference." }) }, ["favorite"]),
80
+ operation("tables.import.csv", "/api/cli/tables/import-csv", "POST", "tables import-csv", "oxygen_tables_import_csv", "tables", "Import inline CSV into a hosted Table through its durable ingestion run. Use staged upload for large files.", { csv_text: text(), csv_base64: text(), table: text(), table_name: text(), project: text(), filename: text(), upsert_key: text(), batch_size: int({ minimum: 1, maximum: 5000 }) }, [], cliOnly),
81
+ operation("tables.import.prepare", "/api/cli/tables/import-url", "POST", "tables import-upload", "oxygen_tables_import_upload", "tables", "Prepare a scoped storage upload for a hosted Table import; uploads and imports are separate, inspectable steps.", { file_name: text(), content_type: text(), byte_length: int({ minimum: 1 }), trace_id: text(), format: enumOf(["csv", "json", "xlsx"]) }, ["file_name", "byte_length"]),
82
+ operation("tables.import.preview", "/api/cli/tables/import-preview", "POST", "tables import-preview", "oxygen_tables_import_preview", "tables", "Preview a staged file's headers, rows and existing-table mapping; writes no imported rows.", { storage_key: text(), file_name: text(), format: enumOf(["csv", "json", "xlsx"]), byte_length: int({ minimum: 1 }), table: text(), upsert_key: text() }, ["storage_key", "byte_length"], { mutates: false }),
83
+ operation("tables.import.staged", "/api/cli/tables/import-staged", "POST", "tables import-staged", "oxygen_tables_import_staged", "tables", "Start a durable hosted import from this workspace's staged file; choose an existing table or create_name.", { table: text(), create_name: text(), project: text(), storage_key: text(), storage_bucket: text(), storage_provider: enumOf(["s3"]), file_name: text(), format: enumOf(["csv", "json", "xlsx"]), sha256: text(), upsert_key: text(), source_key_map: json, byte_length: int({ minimum: 1 }), row_count: int({ minimum: 1 }), max_concurrency: int({ minimum: 1, maximum: 1000 }), source: enumOf(["cli", "mcp", "web"]), batch_size: int({ minimum: 1, maximum: 5000 }) }, ["storage_key", "byte_length"]),
84
+ operation("tables.import.file", "/api/cli/tables/import-file", "POST", "tables import-file", "oxygen_tables_import_file", "tables", "Import small base64 file bytes through the shared multipart import API; use staged upload for large files.", { content_base64: text(), file_name: text(), content_type: text(), table: text(), table_name: text(), project: text(), format: enumOf(["csv", "json", "xlsx"]), sheet: text(), upsert_key: text(), source_key_map: json, source_hash: text(), batch_size: int({ minimum: 1, maximum: 5000 }), max_concurrency: int({ minimum: 1 }) }, ["content_base64", "file_name"], { multipart: { contentField: "content_base64", nameField: "file_name", typeField: "content_type", fileField: "file" } }),
85
+ operation("tables.link.suggest", "/api/cli/tables/link/suggest", "POST", "tables link-suggest", "oxygen_tables_link_suggest", "tables", "Preview which columns can link two Tables and how many rows match; creates no relation.", { table: text(), to: text(), on: text(), scan_limit: int({ minimum: 1 }) }, ["table", "to"], { mutates: false }),
86
+ operation("tables.recover_pending", "/api/cli/tables/recover-pending", "POST", "tables recover-pending", "oxygen_tables_recover_pending", "tables", "Recover bounded pending cells in one Table through the platform's native recovery path.", { table: text(), max_items: int({ minimum: 1 }) }, ["table"], mcpOnly),
87
+ operation("tables.webhooks.create", "/api/cli/tables/webhooks", "POST", "tables webhook create", "oxygen_table_webhook_create", "tables", "Create an inbound Table webhook or platform LinkedIn feed. Optional auto_run is standing permission with explicit or plan-default delivery ceilings.", { table: text(), name: text(), mode: enumOf(["insert", "upsert"]), upsert_key: text(), items_path: text(), event_id_path: text(), event_type_path: text(), occurred_at_path: text(), auth_mode: enumOf(["none", "secret"]), feed_kind: enumOf(["generic", "unipile"]), unipile_account_id: text(), event_types: list(), mapping: text(), auto_run: json }, ["table"], mcpOnly),
88
+ operation("tables.webhooks.update", "/api/cli/tables/webhooks", "PATCH", "tables webhook update", "oxygen_table_webhook_update", "tables", "Edit a Table webhook's name, state or standing auto_run; auth_mode=secret rotates its secret and returns it once.", { endpoint_id: text(), name: text(), status: enumOf(["active", "disabled"]), auth_mode: enumOf(["none", "secret"]), auto_run: json, clear_auto_run: bool() }, ["endpoint_id"], mcpOnly),
89
+ operation("whatsapp.draft", "/api/cli/whatsapp/draft", "POST", "whatsapp draft", "oxygen_whatsapp_draft", "inbox", "Generate a WhatsApp reply draft in the workspace voice, or approve a reviewed draft for an existing chat. Draft generation uses managed AI credits.", { conversation_id: text(), goal: text(), row_id: text(), object: text(), approved: bool(), draft_id: text(), text: text() }, ["conversation_id"], { ...cliOnly, spends: true }),
90
+ operation("whatsapp.drafts.list", "/api/cli/whatsapp/drafts", "GET", "whatsapp drafts list", "oxygen_whatsapp_drafts_list", "inbox", "Read the WhatsApp draft review queue.", { status: text(), limit: int({ minimum: 1 }) }, [], cliOnly),
91
+ operation("whatsapp.drafts.update", "/api/cli/whatsapp/drafts/update", "POST", "whatsapp drafts update", "oxygen_whatsapp_draft_update", "inbox", "Edit an open WhatsApp draft before approving or sending it.", { draft_id: text(), text: text() }, ["draft_id", "text"], cliOnly),
92
+ operation("whatsapp.drafts.reject", "/api/cli/whatsapp/drafts/reject", "POST", "whatsapp drafts reject", "oxygen_whatsapp_draft_reject", "inbox", "Reject an open WhatsApp draft without sending it.", { draft_id: text(), reason: text() }, ["draft_id"], cliOnly),
93
+ operation("worker.repair", "/api/cli/worker/repair", "POST", "worker repair", "oxygen_workspace_queue_repair", "workspace", "Repair this workspace's stalled execution queues through the native bounded recovery path.", {}, [], mcpOnly),
94
+ operation("db.status", "/api/cli/db/status", "GET", "db status", "oxygen_workspace_database_status", "workspace", "Read this workspace's database status and latest completed provisioning/migration receipt; not a list of current pending migrations. Historical Neon fields are lineage. Hosting is plan-included; 0 credits.", {}, [], mcpOnly),
95
+ operation("db.provision", "/api/cli/db/provision", "POST", "db provision", "oxygen_workspace_database_provision", "workspace", "Provision or retry this workspace database immediately; already-ready workspaces retain it. No preview or confirmation field; native workspace write access applies. May outlast the connector deadline: read oxygen_workspace_database_status before retrying. Hosting is plan-included; 0 credits.", {}, [], mcpOnly),
96
+ operation("sessions.list", "/api/cli/sessions", "GET", "sessions list", "oxygen_sessions_list", "workspace", "List your personal CLI sessions across workspaces; OAuth or personal user session required, organization API keys are refused."),
97
+ operation("sessions.revoke", "/api/cli/sessions/{session_id}", "DELETE", "sessions revoke", "oxygen_sessions_revoke", "workspace", "Revoke one of your personal CLI sessions; OAuth or personal user session required, organization API keys are refused."),
98
+ operation("billing.allowance", "/api/cli/billing/allowance", "GET", "billing allowance", "oxygen_billing_allowance", "billing", "Read this workspace's current recurring credit allowance and period.", {}, [], mcpOnly),
99
+ operation("billing.invoices", "/api/cli/billing/invoices", "GET", "billing invoices", "oxygen_billing_invoices", "billing", "Read this workspace's billing invoices and their hosted payment links.", {}, [], mcpOnly),
100
+ operation("billing.seats.set", "/api/cli/billing/seats", "POST", "billing seats set", "oxygen_billing_seats_set", "billing", "Set purchased legacy sending-seat quantity. Retired seats cannot increase; reducing existing seats lowers the next renewal charge.", { seat_key: enumOf(["linkedin", "whatsapp", "phone_number", "email_sender"]), quantity: int({ minimum: 0 }) }, ["seat_key", "quantity"], mcpOnly),
101
+ operation("collaboration.gates.list", "/api/cli/collab/gates", "GET", "approvals gate list", "oxygen_approval_gates_list", "collaboration", "Read this workspace's approval gates, optionally for one exact subject.", { subject: text() }, [], mcpOnly),
102
+ operation("collaboration.members.list", "/api/cli/collab/members", "GET", "approvals client list", "oxygen_workspace_members_list", "collaboration", "Read workspace members and their collaboration roles.", {}, [], mcpOnly),
103
+ operation("crm.connections", "/api/cli/crm/objects/{object}/records/{row_id}/connections", "GET", "crm connections", "oxygen_crm_connections", "crm", "Read a CRM record's connected conversations, sequence enrollments and related activity.", { conversation_limit: int({ minimum: 1 }), sequence_limit: int({ minimum: 1 }) }, [], mcpOnly),
104
+ operation("crm.tasks.list", "/api/cli/crm/tasks", "GET", "crm tasks list", "oxygen_crm_tasks_queue", "crm", "Read the workspace task queue by status, assignee and cursor.", { status: text(), assignee: text(), cursor: text(), limit: int({ minimum: 1 }) }, [], mcpOnly),
105
+ operation("crm.files.download", "/api/cli/crm/files/{attachment_id}/download", "GET", "crm files download", "oxygen_crm_file_download", "crm", "Download a tenant-owned record attachment by attachment_id. The native authenticated response contains its exact bytes.", {}, [], { binary: { maxBytes: 50 * 1024 * 1024 } }),
106
+ operation("db.cost_policy.get", "/api/cli/db/cost-policy", "GET", "db cost-policy", "oxygen_database_cost_policy", "workspace", "Read this workspace's retired Neon cost-policy status. PostgreSQL has no per-workspace sleep control; stored Neon policy is historical. 0 credits.", {}, [], mcpOnly),
107
+ operation("egress.dedicated.get", "/api/cli/egress/dedicated", "GET", "egress dedicated status", "oxygen_dedicated_egress_status", "deliverability", "Read dedicated outbound egress status and its current monthly commitment without activating it.", {}, [], mcpOnly),
108
+ operation("knowledge.files.list", "/api/cli/knowledge/files/list", "GET", "knowledge files list", "oxygen_knowledge_files_list", "knowledge", "List files in one entitled Knowledge repository mount; returns metadata and a bounded cursor.", { mountId: text(), cursor: text(), limit: int({ minimum: 1 }) }, ["mountId"], mcpOnly),
109
+ operation("knowledge.files.get", "/api/cli/knowledge/files/get", "GET", "knowledge files get", "oxygen_knowledge_file_get", "knowledge", "Read one entitled Knowledge file's metadata and bounded text when supported; revision pins historical content.", { id: text(), revision: int({ minimum: 1 }) }, ["id"], mcpOnly),
110
+ operation("knowledge.files.download", "/api/cli/knowledge/files/download", "GET", "knowledge files download", "oxygen_knowledge_file_download", "knowledge", "Download exact bytes for one entitled Knowledge file or pinned revision.", { id: text(), revision: int({ minimum: 1 }) }, ["id"], { ...mcpOnly, binary: { maxBytes: 50 * 1024 * 1024 } }),
111
+ operation("mailboxes.sequences", "/api/cli/mailboxes/{mailbox_id}/sequences", "GET", "mailboxes sequences", "oxygen_mailbox_sequences", "deliverability", "Read the sequences using one sending mailbox by id or email address."),
112
+ operation("managed_inboxes.registrant", "/api/cli/managed-inboxes/registrant", "GET", "managed-inboxes registrant", "oxygen_managed_inbox_registrant", "deliverability", "Read this workspace's domain-registration contact.", {}, [], mcpOnly),
113
+ operation("orgs.billing_owners", "/api/cli/orgs/billing-owners", "GET", "orgs billing-owners", "oxygen_workspace_billing_owners", "workspace", "Read eligible billing-owner workspaces from the caller's memberships.", {}, [], mcpOnly),
114
+ operation("search.runs.items", "/api/cli/search/runs/{search_run_id}/items", "GET", "search runs items", "oxygen_search_run_items", "enrichment", "Read durable search ingestion items by run id and optional status.", { status: text(), limit: int({ minimum: 1 }) }, [], mcpOnly),
115
+ operation("senders.checkpoints", "/api/cli/senders/checkpoints", "GET", "senders checkpoints", "oxygen_sender_checkpoints", "sequences", "Read connected sender authentication checkpoints that need human completion.", {}, [], mcpOnly),
116
+ operation("suppressions.domains.list", "/api/cli/suppressions/domains", "GET", "suppressions domains", "oxygen_suppressed_domains_list", "sequences", "Read domain-wide email suppressions with reason, search and pagination.", { reason: text(), search: text(), limit: int({ minimum: 1 }), offset: int({ minimum: 0 }) }, [], mcpOnly),
117
+ operation("suppressions.domains.remove", "/api/cli/suppressions/domains", "DELETE", "suppressions remove-domain", "oxygen_suppressed_domains_remove", "sequences", "Remove one domain-wide email suppression, or exact domains in bulk. This does not resume enrollments automatically.", { domain: text(), domains: list() }, [], mcpOnly),
118
+ operation("suppressions.addresses.remove", "/api/cli/suppressions/addresses", "DELETE", "suppressions remove-address", "oxygen_suppressed_addresses_remove", "sequences", "Remove one email-address suppression, or exact emails in bulk. This does not resume enrollments automatically.", { email: text(), emails: list() }, [], mcpOnly),
119
+ operation("table_action_runs.list", "/api/cli/table-action-runs", "GET", "table-runs list", "oxygen_table_action_runs_list", "runs", "Read durable Table action runs, optionally filtered by status or trace.", { table: text(), status: text(), trace_id: text(), limit: int({ minimum: 1 }) }, ["table"], mcpOnly),
120
+ operation("tables.columns.fields", "/api/cli/tables/columns/fields", "GET", "columns fields", "oxygen_table_column_fields", "tables", "Read the output fields available from one hosted Table column.", { table: text(), column: text() }, ["table", "column"], mcpOnly),
121
+ operation("tools.favorites", "/api/cli/tools/favorites", "GET", "tools favorites", "oxygen_tool_favorites", "enrichment", "Read this member's favorite tool catalog entries in the active workspace.", {}, [], mcpOnly),
122
+ operation("tools.check_access", "/api/cli/tools/check-access", "POST", "tools check-access", "oxygen_tools_check_access", "enrichment", "Read tool access for this workspace; verify_credential explicitly opts into a provider credential probe.", { tool_id: text(), connection_id: text(), credential_mode: enumOf(["managed", "user_api_key"]), input: json, verify_credential: bool() }, ["tool_id"], { ...cliOnly, mutates: false }),
123
+ operation("linkedin.connections.import_status", "/api/cli/linkedin/connections/import", "GET", "linkedin connections import-status", "oxygen_linkedin_connections_import_status", "sequences", "Read the durable LinkedIn connections import status and target Table.", { account: text() }),
124
+ operation("linkedin.followers.import_status", "/api/cli/linkedin/followers/import", "GET", "linkedin followers import-status", "oxygen_linkedin_followers_import_status", "sequences", "Read the durable LinkedIn followers import status and target Table.", { account: text() }),
125
+ operation("linkedin.inbox.backfill_status", "/api/cli/linkedin/inbox/backfill", "GET", "linkedin inbox backfill-status", "oxygen_linkedin_inbox_backfill_status", "inbox", "Read the durable LinkedIn inbox backfill status.", { account: text() }),
126
+ operation("linkedin.viewers.list", "/api/cli/linkedin/viewers", "GET", "linkedin viewers list", "oxygen_profile_viewers", "publishing", "Read named LinkedIn profile viewers, respecting the connected account's privacy ceiling.", { account: text(), start: int({ minimum: 0 }), include_raw: bool() }, [], cliOnly),
127
+ operation("signals.search.filters", "/api/cli/signals/search/preview", "GET", "signals search filters", "oxygen_signals_search_preview", "signals", "Read the LinkedIn post-search filter catalog and credential status; runs no search.", { source: enumOf(["linkedin_posts"]) }, [], cliOnly),
128
+ operation("visuals.download", "/api/cli/visuals/files/{file_id}/download", "GET", "visuals download", "oxygen_visual_file_download", "publishing", "Download a tenant-owned visual source or rendered file as exact bytes.", {}, [], { ...mcpOnly, binary: { maxBytes: 50 * 1024 * 1024 } }),
129
+ operation("workflows.mcp_callable", "/api/cli/workflows/mcp-callable", "GET", "workflows mcp list", "oxygen_workflows_mcp_callable", "workflows", "List this workspace's published MCP-callable Workflow definitions and interfaces.", {}, [], mcpOnly),
130
+ operation("skills.get", "/api/cli/skills/get", "GET", "skills get", "oxygen_skills_get", "workspace", "Read one authenticated, entitled product-skill entrypoint or manifest-listed supporting file.", { name: text(), file: text("Relative supporting-file path from the manifest; omit for SKILL.md.") }, ["name"], mcpOnly),
131
+ ];
@@ -0,0 +1,30 @@
1
+ import { type ContractObject } from "./schema.js";
2
+ export declare const DATABASE_DEFAULT_MAX_ROWS = 100;
3
+ export declare const DATABASE_HARD_MAX_ROWS = 1000;
4
+ export declare const DATABASE_HARD_MAX_CHARS = 48000;
5
+ export declare function normalizeDatabaseMaxRows(value?: number): number;
6
+ export declare const DATABASE_ATTACH_CONTRACT: ContractObject<{
7
+ readonly database_url: import("./schema.js").StringProperty;
8
+ }, "database_url">;
9
+ export declare const DATABASE_MIGRATE_CONTRACT: ContractObject<{
10
+ readonly rotate_credentials: import("./schema.js").BooleanProperty;
11
+ }, never>;
12
+ export declare const DATABASE_QUERY_CONTRACT: ContractObject<{
13
+ readonly sql: import("./schema.js").StringProperty;
14
+ readonly max_rows: {
15
+ type: "integer";
16
+ description?: string;
17
+ minimum?: number;
18
+ maximum?: number;
19
+ "x-clamp"?: true;
20
+ };
21
+ readonly max_chars: {
22
+ type: "integer";
23
+ description?: string;
24
+ minimum?: number;
25
+ maximum?: number;
26
+ "x-clamp"?: true;
27
+ };
28
+ }, "sql">;
29
+ /** Normalize existing route aliases/coercions first; ignored fields stay compatible. */
30
+ export declare function validateDatabaseInput(contract: ContractObject, input: Record<string, unknown>): Record<string, unknown>;
@@ -0,0 +1,29 @@
1
+ /** Customer database fields; fleet repair stays on its existing staff routes. */
2
+ import { OxygenError } from "../cli-result.js";
3
+ import { bool, int, obj, str, validateContract } from "./schema.js";
4
+ export const DATABASE_DEFAULT_MAX_ROWS = 100;
5
+ export const DATABASE_HARD_MAX_ROWS = 1_000;
6
+ export const DATABASE_HARD_MAX_CHARS = 48_000;
7
+ export function normalizeDatabaseMaxRows(value) {
8
+ return value === undefined || !Number.isFinite(value)
9
+ ? DATABASE_DEFAULT_MAX_ROWS
10
+ : Math.max(1, Math.min(Math.trunc(value), DATABASE_HARD_MAX_ROWS));
11
+ }
12
+ export const DATABASE_ATTACH_CONTRACT = obj({
13
+ database_url: str({ minLength: 1, description: "Owner PostgreSQL URL for a database you own; attaches and initializes it immediately." }),
14
+ }, { required: ["database_url"] });
15
+ export const DATABASE_MIGRATE_CONTRACT = obj({
16
+ rotate_credentials: bool({ description: "Repair runtime/read passwords and stored credentials. Omitted/false keeps them; routine migrations do not need rotation." }),
17
+ });
18
+ export const DATABASE_QUERY_CONTRACT = obj({
19
+ sql: str({ minLength: 1, description: "One SELECT, WITH or EXPLAIN statement; read-only transaction, dedicated customer login, default 5-second timeout." }),
20
+ max_rows: int({ minimum: 1, maximum: DATABASE_HARD_MAX_ROWS, clamp: true, description: "CLI/API default 100; MCP default 20; hard cap 1000." }),
21
+ max_chars: int({ minimum: 1_000, maximum: DATABASE_HARD_MAX_CHARS, description: "Optional complete-row response window including metadata. MCP caps at 10000 chars, built-in agents 4000. Narrow SELECT if one row cannot fit." }),
22
+ }, { required: ["sql"] });
23
+ /** Normalize existing route aliases/coercions first; ignored fields stay compatible. */
24
+ export function validateDatabaseInput(contract, input) {
25
+ const result = validateContract(contract, input, { unknown: "warn", overMax: "reject" });
26
+ if (!result.ok)
27
+ throw new OxygenError("invalid_request", "Invalid workspace database request.", { details: { issues: result.issues }, exitCode: 1 });
28
+ return result.value;
29
+ }
@@ -0,0 +1,43 @@
1
+ export declare const knowledgeIndexReadContract: import("./schema.js").ContractObject<{
2
+ readonly cursor: import("./schema.js").StringProperty;
3
+ readonly max_chars: {
4
+ type: "integer";
5
+ description?: string;
6
+ minimum?: number;
7
+ maximum?: number;
8
+ "x-clamp"?: true;
9
+ };
10
+ readonly limit: {
11
+ type: "integer";
12
+ description?: string;
13
+ minimum?: number;
14
+ maximum?: number;
15
+ "x-clamp"?: true;
16
+ };
17
+ }, never>;
18
+ export declare const knowledgePageReadContract: import("./schema.js").ContractObject<{
19
+ readonly cursor: import("./schema.js").StringProperty;
20
+ readonly max_chars: {
21
+ type: "integer";
22
+ description?: string;
23
+ minimum?: number;
24
+ maximum?: number;
25
+ "x-clamp"?: true;
26
+ };
27
+ readonly slug: import("./schema.js").StringProperty;
28
+ readonly id: import("./schema.js").StringProperty;
29
+ readonly view: {
30
+ type: "string";
31
+ enum: readonly ("body" | "data" | "data_keys" | "full" | "links" | "metadata" | "sources")[];
32
+ description?: string;
33
+ };
34
+ readonly revision: {
35
+ type: "integer";
36
+ description?: string;
37
+ minimum?: number;
38
+ maximum?: number;
39
+ "x-clamp"?: true;
40
+ };
41
+ readonly include_links: import("./schema.js").BooleanProperty;
42
+ readonly path: import("./schema.js").StringProperty;
43
+ }, never>;
@@ -0,0 +1,20 @@
1
+ import { KNOWLEDGE_SLUG_MAX_LENGTH } from "../knowledge-constants.js";
2
+ import { bool, enumOf, int, obj, str } from "./schema.js";
3
+ // Optional native windows: unqualified HTTP/CLI reads retain their legacy shape.
4
+ const window = {
5
+ cursor: str({ minLength: 1, maxLength: 4096 }),
6
+ max_chars: int({ minimum: 2000, maximum: 48000, description: "Complete receipt window; surface cap applies." }),
7
+ };
8
+ export const knowledgeIndexReadContract = obj({
9
+ limit: int({ minimum: 1, maximum: 1000 }),
10
+ ...window,
11
+ });
12
+ export const knowledgePageReadContract = obj({
13
+ slug: str({ minLength: 1, maxLength: KNOWLEDGE_SLUG_MAX_LENGTH }),
14
+ id: str({ minLength: 1 }),
15
+ view: enumOf(["metadata", "body", "data", "data_keys", "links", "sources", "full"]),
16
+ revision: int({ minimum: 1 }),
17
+ include_links: bool({ description: "Full only; default true." }),
18
+ path: str({ maxLength: 1024, description: "JSON pointer; data/data_keys/sources." }),
19
+ ...window,
20
+ });
@@ -0,0 +1,12 @@
1
+ export declare const LINKEDIN_SENT_INVITES_MAX_LIMIT = 200;
2
+ export declare const LINKEDIN_SENT_INVITES_QUERY_CONTRACT: import("./schema.js").ContractObject<{
3
+ readonly account: import("./schema.js").StringProperty;
4
+ readonly limit: {
5
+ type: "integer";
6
+ description?: string;
7
+ minimum?: number;
8
+ maximum?: number;
9
+ "x-clamp"?: true;
10
+ };
11
+ readonly cursor: import("./schema.js").StringProperty;
12
+ }, never>;