@oxygen-agent/cli 1.1011.1091 → 1.1012.2
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.d.ts +3 -1
- package/dist/command-manifest.js +11 -0
- package/dist/customer-operation-commands.js +1 -1
- package/dist/index.js +57 -31
- package/dist/transcript.js +1 -1
- package/node_modules/@oxygen/formula/dist/expression.js +16 -0
- package/node_modules/@oxygen/shared/dist/attempt-provenance.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/attempt-provenance.js +15 -0
- package/node_modules/@oxygen/shared/dist/byok-connect.js +2 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +2 -1
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +28 -4
- package/node_modules/@oxygen/shared/dist/contracts/browser-operations.js +2 -2
- package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +3 -3
- package/node_modules/@oxygen/shared/dist/contracts/geo.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/contracts/geo.js +138 -0
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +4 -3
- package/node_modules/@oxygen/shared/dist/contracts/table-row-removal.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/contracts/table-row-removal.js +10 -4
- package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate.js +23 -8
- package/node_modules/@oxygen/shared/dist/enrichment-intents.js +2 -1
- package/node_modules/@oxygen/shared/dist/external-effect-arming.d.ts +54 -0
- package/node_modules/@oxygen/shared/dist/external-effect-arming.js +90 -0
- package/node_modules/@oxygen/shared/dist/geo.d.ts +247 -0
- package/node_modules/@oxygen/shared/dist/geo.js +314 -0
- package/node_modules/@oxygen/shared/dist/linkedin-countries.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/linkedin-countries.js +1 -1
- package/node_modules/@oxygen/shared/dist/managed-email-branding.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/managed-email-branding.js +81 -0
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +20 -24
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +15 -16
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +3 -3
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +3 -3
- package/node_modules/@oxygen/shared/dist/product-doctrine.js +6 -5
- package/node_modules/@oxygen/shared/dist/recipes.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/recipes.js +1 -0
- package/node_modules/@oxygen/shared/dist/repricing.d.ts +4 -14
- package/node_modules/@oxygen/shared/dist/repricing.js +25 -64
- package/node_modules/@oxygen/shared/dist/research-depth.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/research-depth.js +7 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/dist/whatsapp-sender-limits.d.ts +46 -0
- package/node_modules/@oxygen/shared/dist/whatsapp-sender-limits.js +51 -1
- package/node_modules/@oxygen/shared/package.json +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ export type CommandManifestEntry = {
|
|
|
17
17
|
flags: CommandManifestFlag[];
|
|
18
18
|
spends_credits: boolean;
|
|
19
19
|
mutates: boolean;
|
|
20
|
+
destructive: boolean;
|
|
20
21
|
preview_by_default: boolean;
|
|
21
22
|
json_supported: boolean;
|
|
22
23
|
hidden: boolean;
|
|
@@ -33,10 +34,11 @@ export type CommandSearchResult = {
|
|
|
33
34
|
total_matches: number;
|
|
34
35
|
returned: number;
|
|
35
36
|
route: Record<string, unknown> | null;
|
|
36
|
-
commands: Array<Pick<CommandManifestEntry, "name" | "group" | "description" | "spends_credits" | "mutates" | "json_supported" | "hidden"> & {
|
|
37
|
+
commands: Array<Pick<CommandManifestEntry, "name" | "group" | "description" | "spends_credits" | "mutates" | "destructive" | "json_supported" | "hidden"> & {
|
|
37
38
|
score: number;
|
|
38
39
|
}>;
|
|
39
40
|
hydrate: string;
|
|
41
|
+
next_step?: string;
|
|
40
42
|
};
|
|
41
43
|
export declare function buildCommandManifest(program: Command, binaryName: string): CommandManifest;
|
|
42
44
|
/** Bounded natural-language projection over the complete local manifest. */
|
package/dist/command-manifest.js
CHANGED
|
@@ -108,6 +108,13 @@ const MUTATING_COMMANDS = new Set([
|
|
|
108
108
|
// credits. The exact exceptions disclose writes without inventing spend.
|
|
109
109
|
"billing balance",
|
|
110
110
|
"billing seats",
|
|
111
|
+
// The ledger audit reads that same balance first, with the same conditional writes.
|
|
112
|
+
"billing audit",
|
|
113
|
+
// Creates an approval or Checkout link; a first plan's Checkout can start the
|
|
114
|
+
// first-month offer window. The plan changes only after an admin confirms.
|
|
115
|
+
"billing change",
|
|
116
|
+
// --approved records a domain's renewal exit, or with --revoke resumes renewals.
|
|
117
|
+
"managed-inboxes exit",
|
|
111
118
|
// A default preview is free; approval restores provider seats and billing.
|
|
112
119
|
"managed-inboxes recover",
|
|
113
120
|
"mailboxes warmup status",
|
|
@@ -215,6 +222,7 @@ const READ_ONLY_COMMANDS = new Set([
|
|
|
215
222
|
// invocation still previews, so machine-readable discovery must not imply that
|
|
216
223
|
// calling the command without its confirmation flag writes anything.
|
|
217
224
|
const PREVIEW_BY_DEFAULT_COMMANDS = new Set([
|
|
225
|
+
"mailboxes warmup enable",
|
|
218
226
|
"mailboxes warmup google",
|
|
219
227
|
"deliverability placement-test run",
|
|
220
228
|
"deliverability placement-test send",
|
|
@@ -300,11 +308,13 @@ export function searchCommandManifest(manifest, query, requestedLimit = DEFAULT_
|
|
|
300
308
|
description: command.description,
|
|
301
309
|
spends_credits: command.spends_credits,
|
|
302
310
|
mutates: command.mutates,
|
|
311
|
+
destructive: command.destructive,
|
|
303
312
|
json_supported: command.json_supported,
|
|
304
313
|
hidden: command.hidden,
|
|
305
314
|
score,
|
|
306
315
|
})),
|
|
307
316
|
hydrate: `${manifest.binary} commands get <exact-command> --json`,
|
|
317
|
+
...(matches.length ? {} : { next_step: `No command matched. Describe the outcome with ${manifest.binary} capabilities search, or inspect ${manifest.binary} commands --json.` }),
|
|
308
318
|
};
|
|
309
319
|
}
|
|
310
320
|
/** Exact second hop after search; avoids serializing the full command grammar. */
|
|
@@ -426,6 +436,7 @@ function toManifestEntry(command, path) {
|
|
|
426
436
|
&& (MUTATING_COMMANDS.has(commandName)
|
|
427
437
|
|| MUTATING_VERBS.has(leafVerb)
|
|
428
438
|
|| MUTATING_VERBS.has(verbPrefix))),
|
|
439
|
+
destructive: operations.some((spec) => spec.mcp.annotations?.destructiveHint === true),
|
|
429
440
|
preview_by_default: flagStrings.some((flags) => flags.includes("--live")) ||
|
|
430
441
|
PREVIEW_BY_DEFAULT_COMMANDS.has(commandName),
|
|
431
442
|
json_supported: flagStrings.some((flags) => flags.includes("--json")),
|
|
@@ -34,7 +34,7 @@ const GROUP_PURPOSES = {
|
|
|
34
34
|
const OPERATION_FLAGS = {
|
|
35
35
|
"tables rows remove-selection": {
|
|
36
36
|
table: { help: "Table id or slug." },
|
|
37
|
-
row_ids: { help: "Comma-separated exact row IDs. Choose this or --selection-json." },
|
|
37
|
+
row_ids: { help: "Comma-separated exact row IDs. Choose this, --filter-tree-json or --selection-json." },
|
|
38
38
|
},
|
|
39
39
|
"tables favorite": { favorite: { negateHelp: "Unstar the selected Table or folder (favorite: false)." } },
|
|
40
40
|
"tables favorites": { details: { negateHelp: "Return stored star keys without names or links (details: false)." } },
|
package/dist/index.js
CHANGED
|
@@ -7242,7 +7242,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7242
7242
|
.option("--filter-json <json>", "Legacy filter object or array, e.g. '{\"column\":\"mobile_phone_e164\",\"op\":\"is_null\"}'. Mutually exclusive with --filter-tree-json/--sort-json.")
|
|
7243
7243
|
.option("--filter-tree-json <json>", `Airtable-style filter group, e.g. '{\"type\":\"group\",\"conjunction\":\"and\",\"children\":[{\"type\":\"leaf\",\"columnKey\":\"stage\",\"operator\":\"is\",\"value\":\"won\"}]}'. Add \"path\":\"a.b\" to a leaf to match a key inside a JSON cell, e.g. the rows one Function run touched: {\"columnKey\":\"<function_column>\",\"path\":\"action_run_id\",\"operator\":\"is\",\"value\":\"<run_id>\"}. Leaf operators: ${FILTER_TREE_OPERATORS.join(", ")} (is_true/is_false need a checkbox column; --filter-json's is_null/is_not_null are is_empty/is_not_empty here). Mutually exclusive with --filter-json.`)
|
|
7244
7244
|
.option("--search <text>", "Find rows by one piece of text, matched against every text, number, date and checkbox column at once — the grid's magnifier. Combines with --filter-tree-json (both must hold). Use --filter-tree-json when you know which column to match.")
|
|
7245
|
-
.option("--formula-values <mode>", "Formula filters are refused by default because displayed formulas are evaluated live. Refresh the formula's stored cells with `columns run <table> <column> --force` (0 credits), then pass 'materialized'; filtering uses that stored snapshot and returns a freshness note.")
|
|
7245
|
+
.option("--formula-values <mode>", "Formula filters are refused by default because displayed formulas are evaluated live. Refresh the formula's stored cells with `columns run <table> <column> --force` (0 credits), then pass 'materialized'; filtering uses that stored snapshot and returns a freshness note. A fill-empty field a bundle adds (definition fill: \"empty\", such as Headcount or Job title) is stored, not live: its cell is the value, so 'materialized' filters it exactly.")
|
|
7246
7246
|
.option("--sort-json <json>", "Ordered sort rules, e.g. '[{\"columnKey\":\"_created_at\",\"direction\":\"desc\"}]'. Earlier rules dominate. Mutually exclusive with --filter-json.")
|
|
7247
7247
|
.option("--no-system-fields", "Omit _row_id, _created_at, and _updated_at from returned rows (included by default).")
|
|
7248
7248
|
.option("--json", "Print a JSON envelope.")
|
|
@@ -7448,7 +7448,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7448
7448
|
}));
|
|
7449
7449
|
}))
|
|
7450
7450
|
.addCommand(new Command("recover-pending")
|
|
7451
|
-
.description("
|
|
7451
|
+
.description("Recover existing BetterContact email or phone requests and inspect terminal verification blockers; no new orders.")
|
|
7452
7452
|
.argument("<table>", "Table id or slug.")
|
|
7453
7453
|
.option("--max-items <n>", "Max pending cache rows to poll in this run. Defaults to 1000.")
|
|
7454
7454
|
.option("--json", "Print a JSON envelope.")
|
|
@@ -9393,13 +9393,14 @@ Examples:
|
|
|
9393
9393
|
}));
|
|
9394
9394
|
}))
|
|
9395
9395
|
.addCommand(new Command("describe")
|
|
9396
|
-
.description("Describe one blueprint (seed or saved) by slug.")
|
|
9396
|
+
.description("Describe one blueprint (seed or saved) by slug. The workflow's compiled bundle is left out (its size and sha256 are listed) unless --include-bundle is passed.")
|
|
9397
9397
|
.argument("<slug>", "Blueprint slug.")
|
|
9398
|
+
.option("--include-bundle", "Include each workflow's compiled bundle (often 300k+ characters).")
|
|
9398
9399
|
.option("--json", "Print a JSON envelope.")
|
|
9399
9400
|
.action(async (slug, options) => {
|
|
9400
9401
|
await handleAsyncAction("blueprints describe", options, () => requestOxygen("/api/cli/blueprints/get", {
|
|
9401
9402
|
method: "POST",
|
|
9402
|
-
body: { slug },
|
|
9403
|
+
body: { slug, ...(options.includeBundle ? { include_bundle: true } : {}) },
|
|
9403
9404
|
}));
|
|
9404
9405
|
}))
|
|
9405
9406
|
.addCommand(new Command("export")
|
|
@@ -9800,9 +9801,9 @@ Examples:
|
|
|
9800
9801
|
.command("columns")
|
|
9801
9802
|
.description("Workspace table column commands. Price first: `tools search --for-table <table>` lists every enrichment this table's columns support with its credit cost per row (0 credits). Ready-made templates (pricing pages, job posts, event pages, 10-Ks, website facts, market questions, text extraction, plus free 0-credit cleanups — company names, job titles, email type, text to list; person questions over an enriched LinkedIn profile and person appearances; company research questions — founders, parent company, funding, cloud provider, offers demos, industry, NAICS, HQ and footprint, LinkedIn/Crunchbase/careers page lookups): `columns catalog`, then `columns add <table> --prompt-key <key> --input <name>=<column>`. Column choice guide: the oxygen-tables skill (`oxygen skills get oxygen-tables --json`); enrichment presets and waterfalls: the oxygen-enrichment skill. With --json parse stdout only: the envelope is stdout, and the spend and cost notes (`estimated N credits for a live run`) are stderr, so merging the streams breaks the JSON.")
|
|
9802
9803
|
.addCommand(new Command("add")
|
|
9803
|
-
.description("Add a nullable column to a workspace table. Writes the definition only — this never runs the column and never spends credits; use `columns run` for that, with --dry-run first to see the cost. For a page, posting or filing the row already links to, or a company fact Oxygen already knows how to research (founders, parent company, funding, cloud provider, offers demos, industry, NAICS, HQ, a LinkedIn or Crunchbase page), pick a template from `columns catalog` (--prompt-key) instead of writing a prompt; or read a URL column with --kind research --research-url. The guide is the oxygen-tables skill (`oxygen skills get oxygen-tables --json`). For ICP fit, use Classify Score: `columns add <table> --label 'ICP fit' --score 'low,medium,high' --prompt 'Assess {{company_name}}: {{description}}, {{employees}} employees' --grounding workspace` (0.075 credits/row). A formula column needs no run at all: it evaluates on read from its current inputs, so `tables query` shows its values immediately.")
|
|
9804
|
+
.description("Add a nullable column, a catalog template (--prompt-key) or a multi-column enrichment bundle (--preset) to a workspace table. Writes the definition only — this never runs the column and never spends credits; use `columns run` for that, with --dry-run first to see the cost. For a page, posting or filing the row already links to, or a company fact Oxygen already knows how to research (founders, parent company, funding, cloud provider, offers demos, industry, NAICS, HQ, a LinkedIn or Crunchbase page), pick a template from `columns catalog` (--prompt-key) instead of writing a prompt; or read a URL column with --kind research --research-url. The guide is the oxygen-tables skill (`oxygen skills get oxygen-tables --json`). For ICP fit, use Classify Score: `columns add <table> --label 'ICP fit' --score 'low,medium,high' --prompt 'Assess {{company_name}}: {{description}}, {{employees}} employees' --grounding workspace` (0.075 credits/row). A formula column needs no run at all: it evaluates on read from its current inputs, so `tables query` shows its values immediately.")
|
|
9804
9805
|
.argument("<table>", "Table id or slug.")
|
|
9805
|
-
.option("--preset <preset>", "Add a pre-built column bundle instead of one column, which is what --prompt-key adds: a preset writes SEVERAL columns at once and needs no --input on a table whose identity column it can find, while --prompt-key adds one template column and binds each input you name. Presets: `person_enrich` (one LinkedIn profile lookup — Blitz first, the LinkedIn Scraper only when Blitz has no match — then first/last/full name, current company and its LinkedIn URL, job title, start date, education, school, headline, bio, location and followers as free stored fields that fill when the lookup runs; the person templates in `columns catalog --category people` all read its payload column), `company_enrich` (one company lookup from a domain or LinkedIn URL — Blitz first, the LinkedIn Scraper only when Blitz has no match, about 1 credit a company — keeping the whole profile in its JSON column plus name, domain, LinkedIn URL, headcount, industry, description, founded year and HQ country as free stored fields; no field choices; binds to the person preset's current_company_linkedin_url automatically), `company_tech_stack` (the technologies a company runs, from its website and hiring), `company_funding` (total raised, latest round, number of rounds and investors), `company_revenue` (an annual revenue estimate), `company_hiring` (the roles a company is hiring for, with a count) — each of these four is one paid lookup column that fills its own columns, priced per row in `tools search --for-table` — or `domain_check` (1 credit/row for a live/redirect/parked/not_found/dead verdict on every domain, cached 30 days, plus a free label column to filter on). For a single cleaned or classified column, use a template from `columns catalog` instead — that is one column, not a bundle. Name the input column with --input: `url` for person_enrich, `domain` and/or `linkedin_url` for the company presets. Any column works whatever it is called; without --input Oxygen guesses from column names. Creating the columns is free; run them afterwards, --dry-run first.")
|
|
9806
|
+
.option("--preset <preset>", "Add a pre-built column bundle instead of one column, which is what --prompt-key adds: a preset writes SEVERAL columns at once and needs no --input on a table whose identity column it can find, while --prompt-key adds one template column and binds each input you name. Presets: `person_enrich` (one LinkedIn profile lookup — Blitz first, the LinkedIn Scraper only when Blitz has no match — then first/last/full name, current company and its LinkedIn URL, job title, start date, education, school, headline, bio, location and followers as free stored fields that fill when the lookup runs, followers only on rows the LinkedIn Scraper answers because Blitz publishes none; the person templates in `columns catalog --category people` all read its payload column), `company_enrich` (one company lookup from a domain or LinkedIn URL — Blitz first, the LinkedIn Scraper only when Blitz has no match, about 1 credit a company — keeping the whole profile in its JSON column plus name, domain, LinkedIn URL, headcount, industry, description, founded year and HQ country as free stored fields; no field choices; binds to the person preset's current_company_linkedin_url automatically), `company_tech_stack` (the technologies a company runs, from its website and hiring), `company_funding` (total raised, latest round, number of rounds and investors), `company_revenue` (an annual revenue estimate), `company_hiring` (the roles a company is hiring for, with a count) — each of these four is one paid lookup column that fills its own columns, priced per row in `tools search --for-table` — or `domain_check` (1 credit/row for a live/redirect/parked/not_found/dead verdict on every domain, cached 30 days, plus a free label column to filter on). For a single cleaned or classified column, use a template from `columns catalog` instead — that is one column, not a bundle. Name the input column with --input: `url` for person_enrich, `domain` and/or `linkedin_url` for the company presets. Any column works whatever it is called; without --input Oxygen guesses from column names. Creating the columns is free; run them afterwards, --dry-run first.")
|
|
9806
9807
|
.option("--input <slot=column...>", "Bind a preset or template input to an exact column, e.g. --input url=pricing_url with --prompt-key, or --input domain=website --input linkedin_url=company_li with --preset. Repeatable. For a preset it is only needed when the automatic match is wrong or missing; for a template it names the column each declared input reads.", collectRepeatable, [])
|
|
9807
9808
|
// No preset takes a field list any more (founder, 2026-09-24). Hidden,
|
|
9808
9809
|
// not removed: an old script passing them gets the server's refusal,
|
|
@@ -10495,10 +10496,10 @@ Examples:
|
|
|
10495
10496
|
});
|
|
10496
10497
|
}))
|
|
10497
10498
|
.addCommand(new Command("catalog")
|
|
10498
|
-
.description("List column templates and native Classify
|
|
10499
|
+
.description("List column templates and native Classify; multi-column enrichment bundles (presets such as person_enrich and company_enrich) are listed by `oxygen enrichment catalog`. For ICP fit, Classify Score (0.075 credits/row) appears in `data.native`, including with `--family classification`; `data.entries` lists separate prompt templates. Templates can be added with `columns add <table> --prompt-key <key>`: the extract templates (pricing-page, job-posting, event, 10-K, website-fact, market and text extractors) and the company research questions (founders, parent company, subsidiaries, funding rounds, market cap, cloud provider, is SaaS, offers demos, industry, NAICS, HQ / states / cities, competitors, LinkedIn / Crunchbase / Glassdoor / careers page lookups) and the person templates (--category people: skill set, grad school, location, job fit and current company read from the person_enrich payload for 15 credits; events, keynotes, podcasts and GitHub profile searched by name), each with the input it reads (bind it with --input <name>=<column_key>), the providers behind it, the answer fields, and the credits per row. Read-only and free. Website contacts (emails and phones from each row's site) is here as `--kind tool`; other tool-backed columns that read a page or call a provider are found with `oxygen tools search <words>`. Provider waterfalls and enrichment bundles are a different list with their own per-row prices: `oxygen enrichment catalog`.")
|
|
10499
10500
|
.option("--category <category>", "Only templates in one picker section: extract (page and text extraction), normalize, organize, summarize (free cleanups and classifications), people (person questions and appearances) or company (company research questions); comma-separate to combine (extract,people). The outreach prompts sit under copy, scoring, qa, research.")
|
|
10500
10501
|
.option("--family <family>", "Only one family. Extract: pricing, job_posting, events, filings, site_facts, market, portfolio, text_extraction. Cleanups: company, person, email, lists, classification. Company: web_presence, corporate_structure, funding, footprint, business_model, tech, market. People: profile, appearances.")
|
|
10501
|
-
.option("--kind <kind>", "Only one column kind: research (reads the web or a page, answer with sources), search (a managed web search whose cell is a page URL or the result list, fixed price), ai (reads row text),
|
|
10502
|
+
.option("--kind <kind>", "Only one column kind: research (reads the web or a page, answer with sources), search (a managed web search whose cell is a page URL or the result list, fixed price), ai (reads row text), formula (free), or tool (a native fetch over a website, such as website contacts).")
|
|
10502
10503
|
.option("--search <term>", "Match a word against template keys, titles and descriptions, e.g. pricing or 10-K.")
|
|
10503
10504
|
.option("--json", "Print a JSON envelope.")
|
|
10504
10505
|
.action(async (options) => {
|
|
@@ -10771,16 +10772,16 @@ Examples:
|
|
|
10771
10772
|
.command("table-ingestions")
|
|
10772
10773
|
.description("Durable background table ingestion commands. To find a table's ingestion runs: `runs list --table <table>`.")
|
|
10773
10774
|
.addCommand(new Command("create-tool-page")
|
|
10774
|
-
.description("Create a paged tool-source ingestion
|
|
10775
|
+
.description("Create a bounded paged tool-source ingestion. LinkedIn ads: --tool adyntel.linkedin_ads --rows-path response.ads --cursor-path response.continuation_token --cursor-request-key continuation_token --upsert-key ad_id. Completed means the approved bound finished; use table-ingestions items to check whether more pages remain.")
|
|
10775
10776
|
.argument("<table>", "Table id or slug.")
|
|
10776
10777
|
.option("--tool <tool_id>", "Tool id such as blitzapi.people_search or hubspot.contacts_list.")
|
|
10777
10778
|
.option("--tool-id <tool_id>", "Alias for --tool.")
|
|
10778
10779
|
.requiredOption("--request-json <json>", "Tool request JSON object for the first page.")
|
|
10779
|
-
.option("--rows-path <path>", "
|
|
10780
|
+
.option("--rows-path <path>", "Path from the whole tool result, including response. Defaults to response.results; LinkedIn/Google ads use response.ads.")
|
|
10780
10781
|
.option("--row-mapping-json <json>", "Object mapping target column keys to dotted source-row paths.")
|
|
10781
|
-
.option("--cursor-path <path>", "
|
|
10782
|
+
.option("--cursor-path <path>", "Path from the whole tool result; Adyntel ads use response.continuation_token.")
|
|
10782
10783
|
.option("--cursor-request-key <path>", "Dotted request key to receive the next cursor.")
|
|
10783
|
-
.option("--max-pages <n>", "Maximum pages
|
|
10784
|
+
.option("--max-pages <n>", "Maximum pages in this item's cursor chain. Defaults to 1; a page limit is not supplier exhaustion.")
|
|
10784
10785
|
.option("--upsert-key <key>", "Column key used to upsert instead of inserting.")
|
|
10785
10786
|
.option("--connection-id <connection_id>", "Optional provider integration connection id.")
|
|
10786
10787
|
.option("--max-concurrency <n>", "Maximum concurrent ingestion items for this run. Defaults to 5.")
|
|
@@ -10833,14 +10834,14 @@ Examples:
|
|
|
10833
10834
|
});
|
|
10834
10835
|
}))
|
|
10835
10836
|
.addCommand(new Command("get")
|
|
10836
|
-
.description("Get one durable table ingestion run.")
|
|
10837
|
+
.description("Get one durable table ingestion run. For provider pages, completed means its bounded work finished; inspect table-ingestions items for the source cursor and exhaustion.")
|
|
10837
10838
|
.argument("<run_id>", "Table ingestion run UUID.")
|
|
10838
10839
|
.option("--json", "Print a JSON envelope.")
|
|
10839
10840
|
.action(async (runId, options) => {
|
|
10840
10841
|
await handleAsyncAction("table-ingestions get", options, () => requestOxygen(`/api/cli/table-ingestion-runs/${encodeURIComponent(runId)}`));
|
|
10841
10842
|
}))
|
|
10842
10843
|
.addCommand(new Command("items")
|
|
10843
|
-
.description("List items
|
|
10844
|
+
.description("List ingestion items, including completed pages. For tool_page, item.output.has_next_cursor=true means more pages remain; item.output.stopped_by=provider_exhausted records cursor exhaustion. item.nextCursor is the saved continuation: start a new approved create-tool-page run in the same Table with that request cursor, the same filters/mapping/upsert key and a new credit cap. Inspect failed or unknown outcomes before retrying.")
|
|
10844
10845
|
.argument("<run_id>", "Table ingestion run UUID.")
|
|
10845
10846
|
.option("--status <status>", "Filter by pending, leased, completed, failed, skipped, or canceled.")
|
|
10846
10847
|
.option("--limit <n>", "Maximum items to return. Defaults to 100.")
|
|
@@ -10858,7 +10859,7 @@ Examples:
|
|
|
10858
10859
|
});
|
|
10859
10860
|
}))
|
|
10860
10861
|
.addCommand(new Command("wait")
|
|
10861
|
-
.description("Wait for a table ingestion run (read-only, no credits). Retries transient network failures, including deploy disconnects,
|
|
10862
|
+
.description("Wait for a table ingestion run (read-only, no credits). A completed bounded run may have more provider pages; inspect table-ingestions items for has_next_cursor, stopped_by and nextCursor. Retries transient network failures, including deploy disconnects, within the same timeout.")
|
|
10862
10863
|
.argument("<run_id>", "Table ingestion run UUID.")
|
|
10863
10864
|
.option("--timeout-seconds <n>", "Maximum time to wait. Defaults to 600.")
|
|
10864
10865
|
.option("--interval-seconds <n>", "Polling interval. Defaults to 5.")
|
|
@@ -11525,7 +11526,7 @@ Examples:
|
|
|
11525
11526
|
}))
|
|
11526
11527
|
.addCommand(new Command("usage")
|
|
11527
11528
|
.description("Show credit ledger events (each tagged with its FIXED/FLEXIBLE spend_class), workflow step usage (automation_actions: credits per step, credits billed; writing rows is free), calls with your own API keys (byok_calls: the platform fee per call and the fee credits this month; your provider bills the call itself), or connected LinkedIn and WhatsApp accounts (provider_seats: the rolling-30d peak and its implied connectivity cost; what each account is charged is its credit reservation in `billing commitments`). On a plan that pays for several workspaces (an agency covering client workspaces), `--group-by workspace` totals the credits each workspace spent (`by_workspace`) and `--workspace <id|slug>` narrows the ledger to one of them; a linked client workspace sees only its own spend. managed_credits_used counts captured credits; for where credits went by category, provider or tool, with shares, run `billing audit`.")
|
|
11528
|
-
.option("--meter <meter>", "credits, automation_actions, byok_calls, provider_seats (rolling-30d peak connected accounts and the flat per-account charge; each account's reservation is listed by `billing commitments`), or holds (each held reservation behind reserved_credits in this workspace: credits, run, why it is held (an unmarked LinkedIn lookup hold cannot say whether its result was unknown or delivered), and automatic_return:
|
|
11529
|
+
.option("--meter <meter>", "credits, automation_actions, byok_calls, provider_seats (rolling-30d peak connected accounts and the flat per-account charge; each account's reservation is listed by `billing commitments`), or holds (each held reservation behind reserved_credits in this workspace: credits, run, why it is held (an unmarked LinkedIn lookup hold cannot say whether its result was unknown or delivered), and automatic_return: LinkedIn lookup holds from 24 hours to 30 days old and explicitly unknown LeadMagic email-validation holds older than 24 hours are eligible while automatic return is switched on, and an hourly release returns it on a later pass with no fixed deadline; read-only, newest first, --days up to 365 and --limit apply). Defaults to credits.")
|
|
11529
11530
|
.option("--days <n>", "Lookback window in days. Defaults to 30.")
|
|
11530
11531
|
.option("--from <iso>", "Only include ledger events at or after this ISO timestamp.")
|
|
11531
11532
|
.option("--to <iso>", "Only include ledger events at or before this ISO timestamp.")
|
|
@@ -15125,10 +15126,12 @@ Examples:
|
|
|
15125
15126
|
});
|
|
15126
15127
|
}))
|
|
15127
15128
|
.addCommand(new Command("get")
|
|
15128
|
-
.description("Get one conversation with its full message thread. <conversation> accepts a conversation id, Unipile chat id, or (email) Zapbox thread id, and is resolved on any channel by default (email first, then LinkedIn/WhatsApp). Opening a LinkedIn or WhatsApp thread may fetch provider history and update the local mirror. WhatsApp also merges provider-confirmed phone/contact identities. This uses no Oxygen credits and sends no message; old conversation references remain usable.
|
|
15129
|
+
.description("Get one conversation with its full message thread. <conversation> accepts a conversation id, Unipile chat id, or (email) Zapbox thread id, and is resolved on any channel by default (email first, then LinkedIn/WhatsApp). Opening a LinkedIn or WhatsApp thread may fetch provider history and update the local mirror. WhatsApp also merges provider-confirmed phone/contact identities. This uses no Oxygen credits and sends no message; old conversation references remain usable. Use --stored-only to read the mirror without provider refresh. --sender-account-id retains that participant’s DM privacy window.")
|
|
15129
15130
|
.argument("<conversation>", "Conversation id, Unipile chat id, or Zapbox thread id.")
|
|
15130
15131
|
.option("--channel <channel>", "Inbox channel: all (default, resolves the id on any channel), email, linkedin, or whatsapp.")
|
|
15131
15132
|
.option("--message-limit <n>", "Maximum messages to return (1-500). Defaults to 100.")
|
|
15133
|
+
.option("--sender-account-id <ids>", "Scope DM history to participant UUIDs, comma-separated.")
|
|
15134
|
+
.option("--stored-only", "Read mirrored messages without provider refresh.")
|
|
15132
15135
|
.option("--json", "Print a JSON envelope.")
|
|
15133
15136
|
.action(async (conversation, options) => {
|
|
15134
15137
|
await handleAsyncAction("inbox get", options, () => {
|
|
@@ -15139,15 +15142,21 @@ Examples:
|
|
|
15139
15142
|
const messageLimit = readOption(options.messageLimit);
|
|
15140
15143
|
if (messageLimit)
|
|
15141
15144
|
params.set("message_limit", messageLimit);
|
|
15145
|
+
const senderAccountId = readOption(options.senderAccountId);
|
|
15146
|
+
if (senderAccountId)
|
|
15147
|
+
params.set("sender_account_id", senderAccountId);
|
|
15148
|
+
if (options.storedOnly)
|
|
15149
|
+
params.set("sync", "0");
|
|
15142
15150
|
const suffix = params.toString();
|
|
15143
15151
|
return requestOxygen(`/api/cli/inbox/${encodeURIComponent(conversation)}${suffix ? `?${suffix}` : ""}`);
|
|
15144
15152
|
});
|
|
15145
15153
|
}))
|
|
15146
15154
|
.addCommand(new Command("send")
|
|
15147
|
-
.description("Reply into a conversation on its own channel, resolved from the conversation when --channel is omitted: an email thread replies from its own mailbox (Zapbox/native Gmail/Graph, threaded); a WhatsApp thread gets a warm reply; a LinkedIn thread replies via Unipile. Sends a real message — requires --approved. Without it, returns a preview.")
|
|
15155
|
+
.description("Reply into a conversation on its own channel, resolved from the conversation when --channel is omitted: an email thread replies from its own mailbox (Zapbox/native Gmail/Graph, threaded); a WhatsApp thread gets a warm reply; a LinkedIn thread replies via Unipile. Shared LinkedIn threads require --sender-account-id. Sends a real message — requires --approved. Without it, returns a preview.")
|
|
15148
15156
|
.argument("<conversation>", "Conversation id, Unipile chat id, or (email) Zapbox thread id.")
|
|
15149
15157
|
.requiredOption("--text <message>", "Reply text to send.")
|
|
15150
15158
|
.option("--channel <channel>", "Optional: email, linkedin, or whatsapp. Resolved from the conversation when omitted.")
|
|
15159
|
+
.option("--sender-account-id <id>", "LinkedIn participant UUID from the thread’s senderAccountIds. Required for a shared thread.")
|
|
15151
15160
|
.option("--approved", "Approve and send the message. Without this flag, returns a preview only.")
|
|
15152
15161
|
.option("--draft-id <id>", "When approving an AI reply-agent draft (email or LinkedIn), its id (marks it sent on success). Use the matching --channel.")
|
|
15153
15162
|
.option("--attach <path-or-url>", "Email: local PDF, DOCX, XLSX, PPTX, TXT, or CSV path; preview uploads it for 24 hours without sending. LinkedIn: public URL. Repeatable, up to 5 files and 10 MiB total for email.", collectRepeatable, [])
|
|
@@ -15209,6 +15218,7 @@ Examples:
|
|
|
15209
15218
|
...(channel ? { channel } : {}),
|
|
15210
15219
|
...(options.approved ? { approved: true } : {}),
|
|
15211
15220
|
...(draftId ? { draft_id: draftId } : {}),
|
|
15221
|
+
...(readOption(options.senderAccountId) ? { sender_account_id: readOption(options.senderAccountId) } : {}),
|
|
15212
15222
|
...(attachments.length > 0 ? { attachments } : {}),
|
|
15213
15223
|
...(attachmentIds.length > 0 ? { attachment_ids: attachmentIds } : {}),
|
|
15214
15224
|
},
|
|
@@ -15259,14 +15269,23 @@ Examples:
|
|
|
15259
15269
|
}));
|
|
15260
15270
|
}))
|
|
15261
15271
|
.addCommand(new Command("mark-read")
|
|
15262
|
-
.description("Mark
|
|
15272
|
+
.description("Mark visible messages in a conversation as read (LinkedIn by default; use --channel email or whatsapp for those channels). Sender filters retain the selected DM history window.")
|
|
15263
15273
|
.argument("<conversation>", "Conversation id, Unipile chat id, or (email) Zapbox thread id.")
|
|
15264
15274
|
.option("--channel <channel>", "Inbox channel: linkedin (default), whatsapp, or email.")
|
|
15275
|
+
.option("--sender-account-id <ids>", "Scope DM read state to participant UUIDs, comma-separated.")
|
|
15276
|
+
.option("--sender-profile-id <ids>", "Scope DM read state to sender profile UUIDs, comma-separated.")
|
|
15265
15277
|
.option("--json", "Print a JSON envelope.")
|
|
15266
15278
|
.action(async (conversation, options) => {
|
|
15267
15279
|
await handleAsyncAction("inbox mark-read", options, () => {
|
|
15268
15280
|
const channel = readOption(options.channel);
|
|
15269
|
-
|
|
15281
|
+
const params = new URLSearchParams();
|
|
15282
|
+
const senderAccountId = readOption(options.senderAccountId), senderProfileId = readOption(options.senderProfileId);
|
|
15283
|
+
if (senderAccountId)
|
|
15284
|
+
params.set("sender_account_id", senderAccountId);
|
|
15285
|
+
if (senderProfileId)
|
|
15286
|
+
params.set("sender_profile_id", senderProfileId);
|
|
15287
|
+
const suffix = params.toString();
|
|
15288
|
+
return requestOxygen(`/api/cli/inbox/${encodeURIComponent(conversation)}/read${suffix ? `?${suffix}` : ""}`, {
|
|
15270
15289
|
method: "POST",
|
|
15271
15290
|
body: channel ? { channel } : {},
|
|
15272
15291
|
});
|
|
@@ -17830,7 +17849,7 @@ Examples:
|
|
|
17830
17849
|
await handleAsyncAction("managed-inboxes registrant", options, () => requestOxygen("/api/cli/managed-inboxes/registrant"));
|
|
17831
17850
|
}))
|
|
17832
17851
|
.addCommand(new Command("subscribe")
|
|
17833
|
-
.description("Buy managed monthly inboxes on a new domain or your existing Cloudflare domain. Use --existing-domain for Cloudflare: the read-only preview shows ownership, occupancy, DNS scope and blockers, with zero registration charge. --max-per-domain saves a stricter limit with the approved order, including existing addresses; later additions honor it. Preview does not save the limit or change workspace data. 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. The preview shows the price in force: from the 2026-09 repricing date a Google
|
|
17852
|
+
.description("Buy managed monthly inboxes on a new domain or your existing Cloudflare domain. Use --existing-domain for Cloudflare: the read-only preview shows ownership, occupancy, DNS scope and blockers, with zero registration charge. --max-per-domain saves a stricter limit with the approved order, including existing addresses; later additions honor it. Preview does not save the limit or change workspace data. 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. The preview shows the price in force: from the 2026-09 repricing date a Google Workspace or Microsoft 365 inbox is one flat mailbox line plus the 100-credit mailbox connection, warm-up included, which `connection_monthly_credits` shows and your balance reserves once the inbox connects (400 credits a month all-in at the committed price), and an Azure tenant is one flat charge for its domain (4,000 a month) plus each inbox's connection; `managed-inboxes list --json` reports the prices in force under `prices`. Domains are priced from the vendor's LIVE domain price, and any price below vendor cost is refused. A domain you bought on its own through Oxygen (`domains buy`) takes its first inboxes here too: the preview says `domain_source: owned`, nothing is registered again and `one_off_credits` is 0 (Google or Microsoft; omit --years). To add mailboxes to a domain that already has inboxes use `managed-inboxes add-inboxes`.")
|
|
17834
17853
|
.argument("[domain]", "Sending domain to register + host the mailboxes (e.g. send.acme.com), or one you already bought through Oxygen. May also be passed as --domain.")
|
|
17835
17854
|
.option("--domain <domain>", "Sending domain (alternative to the positional argument).")
|
|
17836
17855
|
.requiredOption("--provider <provider>", "Mailbox PLATFORM: google, microsoft, or azure. (google/microsoft cap at 5 mailboxes per domain; azure allows 100.)")
|
|
@@ -18492,7 +18511,7 @@ Examples:
|
|
|
18492
18511
|
.addCommand(new Command("emailguard")
|
|
18493
18512
|
.description("Assess every mailbox origin for EmailGuard and connect credential-capable Google Workspace mailboxes from managed, Zapmail, or compatible encrypted external imports. Manual/native Google rows report credential_required; Microsoft 365/Azure reports vendor_blocked. Preview never materializes a password; approved execution fetches it just in time and never prints it.")
|
|
18494
18513
|
.addCommand(new Command("connect")
|
|
18495
|
-
.description("Preview or connect selected mailboxes to EmailGuard.
|
|
18514
|
+
.description("Preview or connect selected mailboxes to EmailGuard. Managed mode is 100 credits/inbox-month, with two placement tests per inbox per cycle included; an extra managed test costs 66 credits. The preview shows the price in force; BYOK is 0 Oxygen credits. Google Workspace requires a real app password, fetched only after approval. Microsoft 365/Azure is explicitly vendor-blocked because EmailGuard exposes neither Microsoft OAuth nor tenant consent; Oxygen never downgrades it to password auth. Starts with a 0-credit, no-provider-write preview. Account connection sends no placement probe.")
|
|
18496
18515
|
.option("--mailboxes <list>", "Comma-separated mailbox ids or addresses. Omit for the whole pool.")
|
|
18497
18516
|
.option("--approved", "Perform the external EmailGuard account write.")
|
|
18498
18517
|
.option("--plan <hash>", "Exact hash from the fresh preview (required with --approved).")
|
|
@@ -18612,7 +18631,7 @@ Examples:
|
|
|
18612
18631
|
.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 costs 100 credits per warming inbox per month ($1), or nothing once it is included in your own mailbox's 100-credit monthly connection (2026-09 repricing); the preview shows which applies. 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. On existing active paid seats, Google credential repair uses its own enabled-by-default switch. When the separate worker recovery mode is enforce (the unset default), Microsoft OAuth or tenant-consent seats get a credential-free vendor reactivation, and stalled dispatch or DNS readiness is rechecked. Shadow reports candidates in those enabled lanes without their vendor writes; off skips those lanes. Google credential repair can still run in shadow/off. Correct bad DNS records yourself; an active unpaused paid seat in enforce mode needs no manual resume after that correction. OXYGEN never invents consent, resumes a human or safety pause, starts a new paid enrollment, or blindly repeats an ambiguous provider write. Check `warmup status` and `mailboxes list` for the exact next action.")
|
|
18613
18632
|
.addHelpText("after", "\nDocs: https://oxygen-agent.com/docs/providers/mailboxes\n")
|
|
18614
18633
|
.addCommand(new Command("enable")
|
|
18615
|
-
.description("Optional provider warm-up defaults to 1/day, +1/day, max 10/day (dedicated-tenant profiles stay stricter); see `mailboxes warmup config` for the effective plan and unsupported OXYGEN Warm-up reply-rate control. This is separate from the 5/10/20 campaign ramp. 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 price is 100 credits per warming mailbox-month, or 0 once warm-up is included in the mailbox's 100-credit monthly connection (2026-09 repricing; the preview's included_in_connection says which, and its max_credits is then 0); 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.")
|
|
18634
|
+
.description("Optional provider warm-up defaults to 1/day, +1/day, max 10/day (dedicated-tenant profiles stay stricter); see `mailboxes warmup config` for the effective plan and unsupported OXYGEN Warm-up reply-rate control. This is separate from the 5/10/20 campaign ramp. 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 price is 100 credits per warming mailbox-month, or 0 once warm-up is included in the mailbox's 100-credit monthly connection (2026-09 repricing; the preview's included_in_connection says which, and its max_credits is then 0); 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. Gmail connected through Composio uses that connection after approval of standard mailbox access (send, read and organize), without a second Google consent. The preview names this access and disabling warm-up revokes it. Existing vendor-consent seats are preserved. Eligible InboxKit-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.")
|
|
18616
18635
|
.addHelpText("after", "\nDocs: https://oxygen-agent.com/docs/providers/mailboxes\n")
|
|
18617
18636
|
.option("--mailboxes <list>", "Comma-separated mailbox ids or addresses to warm. Omit to warm the whole pool.")
|
|
18618
18637
|
.option("--approved", "Execute the PRICED enrollment. Without it the response is a priced preview and nothing is enrolled or billed.")
|
|
@@ -18657,7 +18676,7 @@ Examples:
|
|
|
18657
18676
|
});
|
|
18658
18677
|
}))
|
|
18659
18678
|
.addCommand(new Command("google")
|
|
18660
|
-
.description("
|
|
18679
|
+
.description("Legacy Google owner consent for one to ten connected inboxes. Gmail connected through Composio uses warmup enable to review standard mailbox access without a second Google consent. This legacy flow is separate from native sending: no passwords or OXYGEN tokens transfer. Preview costs 0 credits and creates no link. Approve its hash with --max-credits 0, open each consent_url as that exact inbox, then poll --status. Google shows the warm-up provider's consent application. Warmup stays off until a separately approved warmup enable; its fresh preview shows the price, including 0 extra credits when included in the mailbox connection.")
|
|
18661
18680
|
.requiredOption("--mailboxes <list>", "Exact Google inbox ids or addresses, comma-separated; maximum ten.")
|
|
18662
18681
|
.option("--approved", "Create the previewed authorization links.")
|
|
18663
18682
|
.option("--plan <hash>", "Fresh preview hash; required with approval.")
|
|
@@ -18935,15 +18954,22 @@ Examples:
|
|
|
18935
18954
|
}));
|
|
18936
18955
|
})));
|
|
18937
18956
|
program.addCommand(new Command("deliverability")
|
|
18938
|
-
.description("External email deliverability: fleet reputation health and DIRECTIONAL inbox-placement (spam) tests via EmailGuard, Zapmail, or an explicitly configured
|
|
18957
|
+
.description("External email deliverability: fleet reputation health and DIRECTIONAL inbox-placement (spam) tests via EmailGuard, Zapmail, or an explicitly configured OXYGEN placement evaluation pilot. Start with `placement-test list` and `placement-test get` to inspect existing results for 0 credits without creating or sending a test. For continuous EmailGuard account monitoring use `oxygen mailboxes emailguard connect`. Placement tests are approval-gated paid runs (managed bills Oxygen credits; BYOK = 0 Oxygen credits — Zapmail BYOK bills your Zapmail wallet ~$2/test). OXYGEN placement evaluation is never auto-selected or generally available: it is a limited pilot with one 0-credit test for its configured eligible connected sender.")
|
|
18939
18958
|
.addCommand(new Command("placement-test")
|
|
18940
18959
|
.description("Inspect saved directional inbox-placement results with list/get (0 credits; no test email is sent). New tests use preview and approval through run/send.")
|
|
18941
18960
|
.addCommand(new Command("run")
|
|
18942
|
-
.description("Create a placement test for one sending mailbox. Without --approved this returns a cost PREVIEW. EmailGuard creation returns exact seeds + phrase but sends nothing; next run `placement-test send <id>` to preview and approve that external email. Zapmail owns its probe delivery and completes async (2-24h).
|
|
18961
|
+
.description("Create a placement test for one sending mailbox. Without --approved this returns a cost PREVIEW. EmailGuard creation returns exact seeds + phrase but sends nothing; next run `placement-test send <id>` to preview and approve that external email. Zapmail owns its probe delivery and completes async (2-24h). OXYGEN placement evaluation requires explicit --provider oxygen_placement plus --subject and --body, and is available only for the eligible connected sender enabled for the limited pilot. Its approval creates AND sends one probe with no separate send step: preview first, then re-run the same mailbox, provider, subject, and body with --approved --max-credits 0 --plan <plan_hash>.")
|
|
18943
18962
|
.argument("<mailbox>", "Sending mailbox address to test (e.g. ada@send.acme.com).")
|
|
18944
|
-
.addOption(new Option("--provider <provider>", "Health provider: emailguard, zapmail, or
|
|
18945
|
-
.
|
|
18946
|
-
|
|
18963
|
+
.addOption(new Option("--provider <provider>", "Health provider: emailguard, zapmail, or oxygen_placement. Omit to auto-resolve EmailGuard/Zapmail. OXYGEN placement evaluation is never auto-selected and requires the sender enabled for the limited pilot.").choices(["emailguard", "zapmail", "oxygen_placement"])
|
|
18964
|
+
.argParser((value) => {
|
|
18965
|
+
const provider = value === "sendkit" ? "oxygen_placement" : value;
|
|
18966
|
+
if (!["emailguard", "zapmail", "oxygen_placement"].includes(provider)) {
|
|
18967
|
+
throw new CommanderError(2, "commander.invalidArgument", "Choose emailguard, zapmail, or oxygen_placement.");
|
|
18968
|
+
}
|
|
18969
|
+
return provider;
|
|
18970
|
+
}))
|
|
18971
|
+
.option("--subject <subject>", "Seed-message subject. Required with --provider oxygen_placement; Zapmail ignores it beyond labeling the test.")
|
|
18972
|
+
.option("--body <text>", "Plain-text seed-message body. Required with --provider oxygen_placement; ignored by Zapmail.")
|
|
18947
18973
|
.option("--allow-paused-probe", "Opt in to one diagnostic probe while preserving campaign pause; repeat at both create and send approvals. Never permits disabled senders.")
|
|
18948
18974
|
.option("--approved", "Actually run the test (otherwise a preview is returned).")
|
|
18949
18975
|
.option("--max-credits <n>", "Credit cap the caller accepts (must be >= credits_required for managed runs).")
|
|
@@ -23229,7 +23255,7 @@ function readPeopleSearchRunBody(options) {
|
|
|
23229
23255
|
if (!options.sourceFiltersJson) {
|
|
23230
23256
|
throw new OxygenError("invalid_request", "--source people_search needs --source-filters-json (the same filters as people search preview).", { exitCode: 1 });
|
|
23231
23257
|
}
|
|
23232
|
-
const maxCreditsNative =
|
|
23258
|
+
const maxCreditsNative = readCreditCeilingOrZero(options.maxCredits);
|
|
23233
23259
|
const nativeTarget = readPositiveInt(options.targetCount);
|
|
23234
23260
|
// Forwarded so the server refuses them by name; the native lane reads only
|
|
23235
23261
|
// --source-filters-json (OXP-1.39.2 S6).
|
|
@@ -23256,7 +23282,7 @@ function readPeopleSearchRunBody(options) {
|
|
|
23256
23282
|
throw new OxygenError("invalid_request", "Pass --prompt, --plan-json, --from-table with --linkedin-url-column, or --source people_search with --source-filters-json.", { exitCode: 1 });
|
|
23257
23283
|
}
|
|
23258
23284
|
const maxPages = readPeopleSearchMaxPages(options);
|
|
23259
|
-
const maxCredits =
|
|
23285
|
+
const maxCredits = readCreditCeilingOrZero(options.maxCredits);
|
|
23260
23286
|
const targetCount = readPositiveInt(options.targetCount);
|
|
23261
23287
|
const filters = prompt ? readPeopleSearchFilters(options) : null;
|
|
23262
23288
|
const sourceOverrides = source ? {} : readPeopleSearchCompanyWindow(options);
|
package/dist/transcript.js
CHANGED
|
@@ -129,7 +129,7 @@ const SECRET_PATTERNS = [
|
|
|
129
129
|
[/\bgh[pousr]_[A-Za-z0-9]{20,}/g, "ghx_[REDACTED]"],
|
|
130
130
|
[/\bxox[baprs]-[A-Za-z0-9-]{10,}/g, "xox-[REDACTED]"],
|
|
131
131
|
[/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{6,}/g, "[REDACTED_JWT]"],
|
|
132
|
-
[/\/skills-access\/
|
|
132
|
+
[/\/skills-access\/v[12]\.[A-Za-z0-9_-]+\.\d+\.(?:[A-Za-z0-9_-]+\.)?[A-Za-z0-9_-]+/g, "/skills-access/[REDACTED_GRANT]"],
|
|
133
133
|
[/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]{16,}/gi, "$1 [REDACTED]"],
|
|
134
134
|
// "secret-ish key": "value" / secret_ish_key=value (JSON + env shapes)
|
|
135
135
|
[/("?(?:api[_-]?key|apikey|secret|password|passwd|token|access[_-]?token|refresh[_-]?token|client[_-]?secret|authorization|auth[_-]?token|private[_-]?key)"?\s*[:=]\s*"?)([^"\s,}]{6,})/gi, "$1[REDACTED]"],
|
|
@@ -403,6 +403,22 @@ function tokenize(expression, options = {}) {
|
|
|
403
403
|
index += 1;
|
|
404
404
|
continue;
|
|
405
405
|
}
|
|
406
|
+
// A `{{column}}` template token is the most common paste from another tool
|
|
407
|
+
// (and from an AI or tool column's prompt here). It stays the same typed
|
|
408
|
+
// syntax error at the same position; the hint names the bare-identifier form
|
|
409
|
+
// and the expression with every token unwrapped. Nothing is accepted:
|
|
410
|
+
// `{{` is still not formula syntax (OXP-1.27.6 AC4).
|
|
411
|
+
if (char === "{" && expression[index + 1] === "{") {
|
|
412
|
+
const suggested = expression.replace(/\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g, "$1");
|
|
413
|
+
const example = suggested !== expression ? suggested : "upper(company_name)";
|
|
414
|
+
throw formulaExpressionError(`Unexpected character in formula expression. Formulas name columns as bare identifiers, not {{templates}}: write ${example}.`, {
|
|
415
|
+
position: index,
|
|
416
|
+
character: char,
|
|
417
|
+
expression,
|
|
418
|
+
hint: "Formulas read columns by bare key (company_name), not {{company_name}}; {{ }} is template syntax for AI and tool column prompts.",
|
|
419
|
+
...(suggested !== expression ? { suggested_expression: suggested } : {}),
|
|
420
|
+
});
|
|
421
|
+
}
|
|
406
422
|
throw formulaExpressionError("Unexpected character in formula expression.", {
|
|
407
423
|
position: index,
|
|
408
424
|
character: char,
|
|
@@ -20,3 +20,11 @@ export declare function describeRejectedAnswer(reason: string | null | undefined
|
|
|
20
20
|
* show and an empty tooltip is worse than none.
|
|
21
21
|
*/
|
|
22
22
|
export declare function describeIdentityMatch(identityMatch: AttemptIdentityMatch | null | undefined): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* One provider attempt with its lane named the same way for every waterfall.
|
|
25
|
+
* Person waterfalls name a lane by `operation`; the company waterfall names it
|
|
26
|
+
* by `toolId`. Readers that key on `operation` dropped every company attempt,
|
|
27
|
+
* and with it the identity evidence the attempt carried (OXP-7.3.40), so each
|
|
28
|
+
* cell renderer normalizes through here before validating an attempt.
|
|
29
|
+
*/
|
|
30
|
+
export declare function normalizeAttemptLane(value: unknown): unknown;
|
|
@@ -81,3 +81,18 @@ export function describeIdentityMatch(identityMatch) {
|
|
|
81
81
|
const got = returned ? `provider returned ${returned}` : "provider returned no identity";
|
|
82
82
|
return `${asked}; ${got}`;
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* One provider attempt with its lane named the same way for every waterfall.
|
|
86
|
+
* Person waterfalls name a lane by `operation`; the company waterfall names it
|
|
87
|
+
* by `toolId`. Readers that key on `operation` dropped every company attempt,
|
|
88
|
+
* and with it the identity evidence the attempt carried (OXP-7.3.40), so each
|
|
89
|
+
* cell renderer normalizes through here before validating an attempt.
|
|
90
|
+
*/
|
|
91
|
+
export function normalizeAttemptLane(value) {
|
|
92
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
93
|
+
return value;
|
|
94
|
+
const record = value;
|
|
95
|
+
if (typeof record.operation === "string" || typeof record.toolId !== "string")
|
|
96
|
+
return value;
|
|
97
|
+
return { ...record, operation: record.toolId };
|
|
98
|
+
}
|
|
@@ -90,10 +90,11 @@ export declare function inferPrimitiveRoute(query: string): PrimitiveRouteMatch
|
|
|
90
90
|
*/
|
|
91
91
|
export declare const RETIRED_PRODUCT_SKILLS: readonly string[];
|
|
92
92
|
/** Current hosted instruction audiences, shared by discovery and Agent snapshots. */
|
|
93
|
-
export declare const OXYGEN_PRODUCT_SKILL_AUDIENCES: Readonly<Record<string, "workspace" | "agency_partner">>;
|
|
93
|
+
export declare const OXYGEN_PRODUCT_SKILL_AUDIENCES: Readonly<Record<string, "workspace" | "agency_partner" | "internal_dev">>;
|
|
94
94
|
export declare function canReadProductSkillInstructions(name: string, entitlements: {
|
|
95
95
|
authenticated?: boolean;
|
|
96
96
|
agencyPartner: boolean;
|
|
97
|
+
internalDev?: boolean;
|
|
97
98
|
}): boolean;
|
|
98
99
|
/** Identify hosted instruction bodies by native tool lineage, never by user prose. */
|
|
99
100
|
export declare function readProductSkillInstructionResult(capabilityName: string, result: unknown): {
|