@oxygen-agent/cli 1.1014.985 → 1.1014.1087
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-families/program.d.ts +2 -0
- package/dist/command-families/program.js +241 -0
- package/dist/command-families/tables/automation.d.ts +22 -0
- package/dist/command-families/tables/automation.js +250 -0
- package/dist/command-families/tables/bundle-export.d.ts +7 -0
- package/dist/command-families/tables/bundle-export.js +198 -0
- package/dist/command-families/tables/bundle-format.d.ts +66 -0
- package/dist/command-families/tables/bundle-format.js +274 -0
- package/dist/command-families/tables/bundle-import.d.ts +20 -0
- package/dist/command-families/tables/bundle-import.js +260 -0
- package/dist/command-families/tables/display.d.ts +107 -0
- package/dist/command-families/tables/display.js +229 -0
- package/dist/command-families/tables/import-batches.d.ts +26 -0
- package/dist/command-families/tables/import-batches.js +94 -0
- package/dist/command-families/tables/import-columns.d.ts +24 -0
- package/dist/command-families/tables/import-columns.js +131 -0
- package/dist/command-families/tables/import-upload.d.ts +30 -0
- package/dist/command-families/tables/import-upload.js +203 -0
- package/dist/command-families/tables/import-values.d.ts +10 -0
- package/dist/command-families/tables/import-values.js +60 -0
- package/dist/command-families/tables/imports.d.ts +19 -0
- package/dist/command-families/tables/imports.js +200 -0
- package/dist/command-families/tables/options.d.ts +314 -0
- package/dist/command-families/tables/options.js +1 -0
- package/dist/command-families/tables/register-automation.d.ts +2 -0
- package/dist/command-families/tables/register-automation.js +127 -0
- package/dist/command-families/tables/register-external-imports.d.ts +2 -0
- package/dist/command-families/tables/register-external-imports.js +293 -0
- package/dist/command-families/tables/register-files.d.ts +2 -0
- package/dist/command-families/tables/register-files.js +72 -0
- package/dist/command-families/tables/register-followers.d.ts +2 -0
- package/dist/command-families/tables/register-followers.js +73 -0
- package/dist/command-families/tables/register-lifecycle.d.ts +2 -0
- package/dist/command-families/tables/register-lifecycle.js +114 -0
- package/dist/command-families/tables/register-reads.d.ts +2 -0
- package/dist/command-families/tables/register-reads.js +187 -0
- package/dist/command-families/tables/register-relations.d.ts +2 -0
- package/dist/command-families/tables/register-relations.js +244 -0
- package/dist/command-families/tables/register-row-actions.d.ts +2 -0
- package/dist/command-families/tables/register-row-actions.js +53 -0
- package/dist/command-families/tables/register-row-writes.d.ts +2 -0
- package/dist/command-families/tables/register-row-writes.js +140 -0
- package/dist/command-families/tables/register-views.d.ts +2 -0
- package/dist/command-families/tables/register-views.js +83 -0
- package/dist/command-families/tables/register-watchers.d.ts +2 -0
- package/dist/command-families/tables/register-watchers.js +65 -0
- package/dist/command-families/tables/register-webhooks.d.ts +2 -0
- package/dist/command-families/tables/register-webhooks.js +126 -0
- package/dist/command-families/tables/registration-context.d.ts +8 -0
- package/dist/command-families/tables/registration-context.js +1 -0
- package/dist/command-families/tables/registration.d.ts +3 -0
- package/dist/command-families/tables/registration.js +30 -0
- package/dist/command-families/tables/row-selection.d.ts +5 -0
- package/dist/command-families/tables/row-selection.js +82 -0
- package/dist/command-families/tables/send.d.ts +6 -0
- package/dist/command-families/tables/send.js +111 -0
- package/dist/command-manifest.js +1 -0
- package/dist/commands/copilot.js +8 -0
- package/dist/commands/deliverability.js +4 -2
- package/dist/commands/sequences.js +30 -2
- package/dist/commands/tables.d.ts +1 -3
- package/dist/commands/tables.js +1 -3505
- package/dist/credentials.js +1 -1
- package/dist/customer-operation-commands.js +62 -3
- package/dist/help.js +1 -1
- package/dist/index.d.ts +1 -99
- package/dist/index.js +5 -337
- package/dist/knowledge-mirror.js +1 -1
- package/dist/skills.js +4 -2
- package/dist/update.js +2 -1
- package/dist/workspace-placement-commands.d.ts +3 -0
- package/dist/workspace-placement-commands.js +32 -0
- package/node_modules/@oxygen/shared/dist/agent-sessions.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/agent-sessions.js +11 -3
- package/node_modules/@oxygen/shared/dist/agents-enabled.d.ts +29 -0
- package/node_modules/@oxygen/shared/dist/agents-enabled.js +51 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +39 -2
- package/node_modules/@oxygen/shared/dist/contracts/agent-sessions.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/customer-operations.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/guided-activation.js +5 -5
- package/node_modules/@oxygen/shared/dist/contracts/guided-company-files.d.ts +30 -0
- package/node_modules/@oxygen/shared/dist/contracts/guided-company-files.js +53 -0
- package/node_modules/@oxygen/shared/dist/contracts/guided-first-sourcing.d.ts +120 -0
- package/node_modules/@oxygen/shared/dist/contracts/guided-first-sourcing.js +48 -0
- package/node_modules/@oxygen/shared/dist/contracts/guided-onboarding-journey.d.ts +267 -0
- package/node_modules/@oxygen/shared/dist/contracts/guided-onboarding-journey.js +63 -0
- package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +9 -4
- package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.js +3 -0
- package/node_modules/@oxygen/shared/dist/contracts/teammates.js +17 -15
- package/node_modules/@oxygen/shared/dist/email-tracking-token.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/email-tracking-token.js +1 -0
- package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/feature-gates.js +5 -0
- package/node_modules/@oxygen/shared/dist/file-import.d.ts +46 -0
- package/node_modules/@oxygen/shared/dist/file-import.js +138 -0
- package/node_modules/@oxygen/shared/dist/guided-activation.d.ts +8 -0
- package/node_modules/@oxygen/shared/dist/guided-company-files.d.ts +13 -0
- package/node_modules/@oxygen/shared/dist/guided-company-files.js +14 -0
- package/node_modules/@oxygen/shared/dist/guided-first-sourcing.d.ts +116 -0
- package/node_modules/@oxygen/shared/dist/guided-first-sourcing.js +11 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-answers.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-answers.js +42 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-company-context.d.ts +22 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-company-context.js +38 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.d.ts +147 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.fixture.d.ts +12 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.fixture.js +83 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-journey.js +44 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-prospects.d.ts +7 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-prospects.js +13 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-signals.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/guided-onboarding-signals.js +34 -0
- package/node_modules/@oxygen/shared/dist/guided-trial-billing.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/guided-trial-billing.js +19 -0
- package/node_modules/@oxygen/shared/dist/index.d.ts +0 -1
- package/node_modules/@oxygen/shared/dist/index.js +0 -6
- package/node_modules/@oxygen/shared/dist/table-limits.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/table-limits.js +6 -0
- package/node_modules/@oxygen/shared/dist/teammates.d.ts +13 -8
- package/node_modules/@oxygen/shared/dist/teammates.js +20 -11
- package/node_modules/@oxygen/shared/dist/user-capability-routing.js +78 -0
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/workspace-placement-view.d.ts +149 -0
- package/node_modules/@oxygen/shared/dist/workspace-placement-view.js +101 -0
- package/node_modules/@oxygen/shared/dist/workspace-placement.d.ts +77 -0
- package/node_modules/@oxygen/shared/dist/workspace-placement.js +94 -0
- package/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { readPositiveInt } from "../../cli-values.js";
|
|
2
|
+
import { readCsvOption } from "../../command-support.js";
|
|
3
|
+
import { readOption, isRecord } from "../../util.js";
|
|
4
|
+
import { OxygenError } from "@oxygen/shared/cli-result";
|
|
5
|
+
// Assemble the `tables send` request: --map pairs (headline sugar) become
|
|
6
|
+
// column-copy mapping entries; --mapping JSON is the literal/path escape hatch;
|
|
7
|
+
// --automap asks the server to resolve the mapping (echoed back). Exactly one
|
|
8
|
+
// mapping source must be chosen, locally, so the error names CLI flags instead
|
|
9
|
+
// of API fields.
|
|
10
|
+
export function buildTablesSendRequest(source, target, options) {
|
|
11
|
+
const mapping = parseTablesSendMapping(options);
|
|
12
|
+
if (mapping && options.automap) {
|
|
13
|
+
throw new OxygenError("invalid_send", "Pass either a mapping (--map/--mapping) or --automap, not both.", { exitCode: 1 });
|
|
14
|
+
}
|
|
15
|
+
if (!mapping && !options.automap) {
|
|
16
|
+
throw new OxygenError("invalid_send", "Pass --map target=source pairs, --mapping <json>, or --automap.", { exitCode: 1 });
|
|
17
|
+
}
|
|
18
|
+
const mode = normalizeTablesSendMode(options.mode);
|
|
19
|
+
const upsertKey = readOption(options.upsertKey);
|
|
20
|
+
if (mode === "upsert" && !upsertKey) {
|
|
21
|
+
throw new OxygenError("invalid_send", "--mode upsert requires --upsert-key <target column>.", { exitCode: 1 });
|
|
22
|
+
}
|
|
23
|
+
if (mode === "insert" && upsertKey) {
|
|
24
|
+
throw new OxygenError("invalid_send", "--upsert-key is only valid with --mode upsert.", { exitCode: 1 });
|
|
25
|
+
}
|
|
26
|
+
const flattenColumn = readOption(options.flatten);
|
|
27
|
+
const flattenPath = readOption(options.flattenPath);
|
|
28
|
+
if (flattenPath && !flattenColumn) {
|
|
29
|
+
throw new OxygenError("invalid_send", "--flatten-path requires --flatten <column>.", { exitCode: 1 });
|
|
30
|
+
}
|
|
31
|
+
const filters = parseTablesSendFilters(options.filter);
|
|
32
|
+
const pageSize = readPositiveInt(options.pageSize);
|
|
33
|
+
return {
|
|
34
|
+
source,
|
|
35
|
+
target,
|
|
36
|
+
...(mapping ? { mapping } : {}),
|
|
37
|
+
...(options.automap ? { automap: true } : {}),
|
|
38
|
+
...(flattenColumn ? { flatten: { column: flattenColumn, ...(flattenPath ? { path: flattenPath } : {}) } } : {}),
|
|
39
|
+
...(filters ? { filters } : {}),
|
|
40
|
+
mode,
|
|
41
|
+
...(upsertKey ? { upsert_key: upsertKey } : {}),
|
|
42
|
+
...(pageSize ? { page_size: pageSize } : {}),
|
|
43
|
+
...(options.dryRun ? { dry_run: true } : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function parseTablesSendMapping(options) {
|
|
47
|
+
const pairs = (options.map ?? []).flatMap((entry) => readCsvOption(entry));
|
|
48
|
+
const mappingJson = readOption(options.mapping);
|
|
49
|
+
if (pairs.length > 0 && mappingJson) {
|
|
50
|
+
throw new OxygenError("invalid_send", "Pass either --map pairs or --mapping <json>, not both.", { exitCode: 1 });
|
|
51
|
+
}
|
|
52
|
+
if (mappingJson) {
|
|
53
|
+
let parsed;
|
|
54
|
+
try {
|
|
55
|
+
parsed = JSON.parse(mappingJson);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new OxygenError("invalid_send", "--mapping is not valid JSON.", { details: { mapping: mappingJson }, exitCode: 1 });
|
|
59
|
+
}
|
|
60
|
+
if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every(isRecord)) {
|
|
61
|
+
throw new OxygenError("invalid_send", "--mapping must be a non-empty JSON array of { target, source, path? } entries.", { exitCode: 1 });
|
|
62
|
+
}
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
|
65
|
+
if (pairs.length === 0)
|
|
66
|
+
return null;
|
|
67
|
+
return pairs.map((entry) => {
|
|
68
|
+
const separator = entry.indexOf("=");
|
|
69
|
+
if (separator <= 0 || separator === entry.length - 1) {
|
|
70
|
+
throw new OxygenError("invalid_send", "--map entries must be target=source column pairs, e.g. company=company_name,website=domain.", { details: { entry }, exitCode: 1 });
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
target: entry.slice(0, separator).trim(),
|
|
74
|
+
source: { type: "column", key: entry.slice(separator + 1).trim() },
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export function parseTablesSendFilters(value) {
|
|
79
|
+
const raw = readOption(value);
|
|
80
|
+
if (!raw)
|
|
81
|
+
return null;
|
|
82
|
+
let parsed;
|
|
83
|
+
try {
|
|
84
|
+
parsed = JSON.parse(raw);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
throw new OxygenError("invalid_send", "--filter is not valid JSON.", { details: { filter: raw }, exitCode: 1 });
|
|
88
|
+
}
|
|
89
|
+
const entries = Array.isArray(parsed) ? parsed : [parsed];
|
|
90
|
+
if (entries.length === 0 || !entries.every(isRecord)) {
|
|
91
|
+
throw new OxygenError("invalid_send", "--filter must be a JSON array of { column, op, value? } objects.", { exitCode: 1 });
|
|
92
|
+
}
|
|
93
|
+
return entries;
|
|
94
|
+
}
|
|
95
|
+
export function normalizeTablesSendMode(value) {
|
|
96
|
+
const normalized = (readOption(value) ?? "insert").toLowerCase();
|
|
97
|
+
if (normalized === "insert" || normalized === "upsert")
|
|
98
|
+
return normalized;
|
|
99
|
+
throw new OxygenError("invalid_send", "--mode must be insert or upsert.", {
|
|
100
|
+
details: { mode: value },
|
|
101
|
+
exitCode: 1,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// After a live send starts, point the terminal at the durable run's inspection
|
|
105
|
+
// surfaces (stderr, so the machine-read stdout envelope stays clean).
|
|
106
|
+
export function writeTablesSendHint(data) {
|
|
107
|
+
if (!isRecord(data) || typeof data.ingestion_run_id !== "string")
|
|
108
|
+
return;
|
|
109
|
+
const runUrl = typeof data.run_web_url === "string" ? ` (or open ${data.run_web_url})` : "";
|
|
110
|
+
process.stderr.write(`hint: watch the copy with oxygen table-ingestions wait ${data.ingestion_run_id}${runUrl}\n`);
|
|
111
|
+
}
|
package/dist/command-manifest.js
CHANGED
|
@@ -262,6 +262,7 @@ const PREVIEW_BY_DEFAULT_COMMANDS = new Set([
|
|
|
262
262
|
"domains dns apply",
|
|
263
263
|
"mailboxes warmup enable",
|
|
264
264
|
"mailboxes warmup google",
|
|
265
|
+
"deliverability workspace-test run",
|
|
265
266
|
"deliverability placement-test run",
|
|
266
267
|
"deliverability placement-test send",
|
|
267
268
|
"managed-inboxes recover",
|
package/dist/commands/copilot.js
CHANGED
|
@@ -483,6 +483,7 @@ export function registerCopilotFamily(program, handleAsyncAction) {
|
|
|
483
483
|
.option("--no-wait", "Post the turn and return immediately without streaming the reply.")
|
|
484
484
|
.option("--context <json>", "Optional JSON object of surface context to attach to the turn.")
|
|
485
485
|
.option("--attach <path>", "Attach a file to this message (repeatable). Spreadsheets become tables the copilot can import and enrich; text documents ground the conversation. Uploaded straight to object storage, so large files are fine.", collectRepeatable, [])
|
|
486
|
+
.option("--interrupt", "In a Copilot chat, stop a still-working turn and send this message as the next turn instead of being refused. Not with --attach or in an Oxygent chat.")
|
|
486
487
|
.option("--timeout-seconds <n>", "Maximum time to wait for the turn to finish. Defaults to 300.")
|
|
487
488
|
.option("--interval-seconds <n>", "Polling interval while streaming. Defaults to 2.")
|
|
488
489
|
.option("--json", "Print a JSON envelope.")
|
|
@@ -501,11 +502,18 @@ export function registerCopilotFamily(program, handleAsyncAction) {
|
|
|
501
502
|
message,
|
|
502
503
|
...(context ? { context } : {}),
|
|
503
504
|
...(attachmentIds.length > 0 ? { attachment_ids: attachmentIds } : {}),
|
|
505
|
+
...(options.interrupt ? { interrupt: true } : {}),
|
|
504
506
|
},
|
|
505
507
|
});
|
|
506
508
|
// Commander maps --no-wait to `wait: false` (default true).
|
|
507
509
|
if (options.wait === false)
|
|
508
510
|
return posted;
|
|
511
|
+
// Held for the turn being stopped: the message becomes the next turn
|
|
512
|
+
// once that stop lands, so there is no turn of its own to wait on yet.
|
|
513
|
+
if (posted.queued_after_interrupt === true) {
|
|
514
|
+
process.stderr.write(`Stopping the current turn; your message runs next. Follow it with \`oxygen copilot get ${sessionId}\`.\n`);
|
|
515
|
+
return posted;
|
|
516
|
+
}
|
|
509
517
|
const turn = isRecord(posted.turn) ? posted.turn : {};
|
|
510
518
|
const turnId = typeof turn.id === "string" ? turn.id : "";
|
|
511
519
|
const latestSeq = posted.latest_seq;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerWorkspacePlacementCommands } from "../workspace-placement-commands.js";
|
|
1
2
|
import { requestOxygen } from "../http-client.js";
|
|
2
3
|
import { readOption } from "../util.js";
|
|
3
4
|
import { Command, CommanderError, Option } from "commander";
|
|
@@ -71,7 +72,7 @@ export function registerDeliverabilityFamily(program, handleAsyncAction) {
|
|
|
71
72
|
}));
|
|
72
73
|
}))
|
|
73
74
|
.addCommand(new Command("list")
|
|
74
|
-
.description("Inspect directional inbox-placement tests. Use --view summary for inventory, --status to filter, and pass next_cursor as --cursor until null. count is this page; total and status_counts cover all matches before the cursor. creditsUsed is
|
|
75
|
+
.description("Inspect directional inbox-placement tests. Use --view summary for inventory, --status to filter, and pass next_cursor as --cursor until null. count is this page; total and status_counts cover all matches before the cursor. creditsUsed is recorded original cost; results.refund (summary: refund) records credit or allowance restoration. Credit refunds follow the actual captured charge; test rows do not establish net wallet spend. Plus `pricing`: OXYGEN deliverability monitoring credits per inbox-month, two included tests per inbox per cycle, and the managed extra-test price in force. Covered tests cost no extra credits. The allowance resets each UTC calendar month; unused tests do not roll over. Own-key tests pay the quoted OXYGEN platform fee separately from provider charges. The returned web link opens Accounts; use get for an exact result link. Costs 0 credits and never creates or sends a test; active tests may refresh their observations.")
|
|
75
76
|
.option("--limit <n>", "Max rows (default 50, cap 200).")
|
|
76
77
|
.option("--status <status>", "Filter: pending_send, sending, awaiting_results, completed, incomplete, failed.")
|
|
77
78
|
.option("--cursor <cursor>", "Continue from next_cursor with the same status filter.")
|
|
@@ -89,10 +90,11 @@ export function registerDeliverabilityFamily(program, handleAsyncAction) {
|
|
|
89
90
|
});
|
|
90
91
|
}))
|
|
91
92
|
.addCommand(new Command("get")
|
|
92
|
-
.description("Inspect
|
|
93
|
+
.description("Inspect a saved directional placement report and refund receipt; recorded cost is not net wallet spend. creditsUsed retains recorded cost; results.refund records credit or allowance restoration. Credit refunds follow the actual captured charge. Includes status, seed counts, per-provider results and a web result link. Costs 0 credits and never creates or sends a test; active tests may refresh their observations. Zapmail results land async (2-24h); keep polling while status is awaiting_results.")
|
|
93
94
|
.argument("<id>", "Placement test id.")
|
|
94
95
|
.option("--json", "Print a JSON envelope.")
|
|
95
96
|
.action(async (id, options) => {
|
|
96
97
|
await handleAsyncAction("deliverability placement-test get", options, () => requestOxygen(`/api/cli/deliverability/placement-tests/${encodeURIComponent(id)}`));
|
|
97
98
|
}))));
|
|
99
|
+
registerWorkspacePlacementCommands(program.commands.find(c => c.name() === "deliverability"), handleAsyncAction);
|
|
98
100
|
}
|
|
@@ -1593,6 +1593,7 @@ export function registerSequencesFamily(program, handleAsyncAction) {
|
|
|
1593
1593
|
.option("--senders <ids>", "Comma-separated LinkedIn or WhatsApp sender account ids (or connection / Unipile ids; `senders list` / `whatsapp accounts` list them). Optional at create; enroll and start require at least one when the journey has LinkedIn or WhatsApp steps (attach later with `sequences update --senders`).")
|
|
1594
1594
|
.option("--table <id>", "Source table id or slug whose rows supply {{column}} template values.")
|
|
1595
1595
|
.option("--activation-id <id>", "Guided authored LinkedIn draft: bind the creator's activation and exact First leads Table. Free; retries reuse the native draft.")
|
|
1596
|
+
.option("--activation-revision <n>", "Reviewed current activation revision, a positive safe integer; requires --activation-id. Native first creation checks it before mutation.")
|
|
1596
1597
|
.option("--from-table <id>", "Alias of --table: the source table id or slug this sequence draws its leads and {{column}} values from.")
|
|
1597
1598
|
.option("--enroll", "After creating the sequence, immediately enroll every row of the bound table (same as running `sequences enroll --from-table` next). Requires --table/--from-table. Enrolling spends no credits and sends nothing.")
|
|
1598
1599
|
.option("--url-column <key>", "Column key holding each lead's LinkedIn URL/provider id.")
|
|
@@ -1649,6 +1650,11 @@ export function registerSequencesFamily(program, handleAsyncAction) {
|
|
|
1649
1650
|
// so it names the same thing here rather than being a second
|
|
1650
1651
|
// concept; `--table` stays for every existing script.
|
|
1651
1652
|
const sourceTable = readOption(options.fromTable) ?? readOption(options.table);
|
|
1653
|
+
const activationId = readOption(options.activationId);
|
|
1654
|
+
const activationRevision = readPositiveInt(options.activationRevision);
|
|
1655
|
+
if (options.activationRevision !== undefined && (!activationId || !Number.isSafeInteger(activationRevision))) {
|
|
1656
|
+
throw new OxygenError("invalid_input", "--activation-revision needs --activation-id and a positive safe integer from current onboarding.", { exitCode: 1 });
|
|
1657
|
+
}
|
|
1652
1658
|
if (options.enroll && !sourceTable) {
|
|
1653
1659
|
throw new Error("--enroll needs a source table: pass --from-table <table> (or --table <table>).");
|
|
1654
1660
|
}
|
|
@@ -1662,7 +1668,8 @@ export function registerSequencesFamily(program, handleAsyncAction) {
|
|
|
1662
1668
|
...(senders.length > 0 ? { senders } : {}),
|
|
1663
1669
|
...(tags.length > 0 ? { tags } : {}),
|
|
1664
1670
|
...(sourceTable ? { source_table_id: sourceTable } : {}),
|
|
1665
|
-
...(
|
|
1671
|
+
...(activationId ? { activation_id: activationId } : {}),
|
|
1672
|
+
...(activationRevision !== undefined ? { activation_revision: activationRevision } : {}),
|
|
1666
1673
|
...(options.enroll ? { enroll_from_table: true } : {}),
|
|
1667
1674
|
...(readOption(options.urlColumn) ? { linkedin_url_column_key: readOption(options.urlColumn) } : {}),
|
|
1668
1675
|
...(email ? { email } : {}),
|
|
@@ -1709,6 +1716,9 @@ export function registerSequencesFamily(program, handleAsyncAction) {
|
|
|
1709
1716
|
.description("Update a sequence. Journey structure/channels/email binding are draft-only so revised scope receives a fresh launch approval; a started sequence may only re-time waits it already has (clear a delay by zeroing it, never by deleting the step — enrollments track position by index); sender pools can change while draft or paused; launch caps change through `sequences start` after first start. Name, throttles, the sending schedule, tags, the unsubscribe option (off by default), and open/click tracking toggles remain editable as allowed by the server. Pass only the fields you want to change.")
|
|
1710
1717
|
.addHelpText("after", "\nSenders: email_send.sender_profile_id sends one email from any inbox of a selected person (a lead already assigned to that person keeps its own inbox), and email_send.sender_mailbox_id from one exact inbox of a selected sender; a LinkedIn step's sender_profile_id sends it from a selected person (invite, message, inmail and withdraw_invite share one or none). Omit both to use the lead's assigned person. An email whose From changes needs its own subject_template. `sequences start <sequence>` without --approved shows who sends each step; nothing changes.\n")
|
|
1711
1718
|
.argument("<sequence>", "Sequence id or slug.")
|
|
1719
|
+
.option("--expected-revision <revision>", "Exact sequence_revision from sequences get; stale edits refuse. Not launch approval.")
|
|
1720
|
+
.option("--activation-id <id>", "Guided activation UUID; requires --activation-revision and --expected-revision.")
|
|
1721
|
+
.option("--activation-revision <revision>", "Current guided activation revision; requires --activation-id and --expected-revision.")
|
|
1712
1722
|
.option("--name <name>", "New human-readable sequence name.")
|
|
1713
1723
|
.option("--steps-file <path>", "Path to a JSON file: { \"steps\": [...] } replacing the journey. Once started, only wait timing may change: same steps, same order (zero a delay, never delete its step). Copy supports {{column}} interpolation; every step also takes {{sender_first_name}} / {{sender_last_name}} / {{sender_full_name}} for the person who sends it, and email adds {{sender_email}} (a same-named row column wins). A `branch` can also route on the lead's data with a data leaf { has_column: \"email\" } (true when that row_values column is non-empty; present:false for \"missing\").")
|
|
1714
1724
|
.option("--channels <list>", "Draft only: comma-separated channels: linkedin,email,whatsapp,call,crm.")
|
|
@@ -1771,6 +1781,18 @@ export function registerSequencesFamily(program, handleAsyncAction) {
|
|
|
1771
1781
|
.action(async (sequence, options) => {
|
|
1772
1782
|
await handleAsyncAction("sequences update", options, () => {
|
|
1773
1783
|
const body = {};
|
|
1784
|
+
const expectedRevision = options.expectedRevision;
|
|
1785
|
+
const activationId = options.activationId;
|
|
1786
|
+
const activationRevision = readPositiveInt(options.activationRevision);
|
|
1787
|
+
if (options.activationRevision !== undefined && activationRevision === undefined) {
|
|
1788
|
+
throw new OxygenError("invalid_number", "Expected a positive activation revision.", { exitCode: 1 });
|
|
1789
|
+
}
|
|
1790
|
+
if (expectedRevision !== undefined)
|
|
1791
|
+
body.expected_revision = expectedRevision;
|
|
1792
|
+
if (activationId !== undefined)
|
|
1793
|
+
body.activation_id = activationId;
|
|
1794
|
+
if (activationRevision !== undefined)
|
|
1795
|
+
body.activation_revision = activationRevision;
|
|
1774
1796
|
const name = readOption(options.name);
|
|
1775
1797
|
if (name)
|
|
1776
1798
|
body.name = name;
|
|
@@ -1834,9 +1856,15 @@ export function registerSequencesFamily(program, handleAsyncAction) {
|
|
|
1834
1856
|
.description("Get a sequence's definition, senders, status, and credit usage. --readiness adds the launch check — whether it CAN go live, why not, and the mailbox pool it would send from — without touching it; `sequences preview <sequence>` (a read; 0 credits) shows who sends each step (sender_journey).")
|
|
1835
1857
|
.argument("<sequence>", "Sequence id or slug.")
|
|
1836
1858
|
.option("--readiness", "Add launch_readiness: blockers (e.g. a missing send cap), warnings, and the resolved sending mailboxes. Read-only — sends nothing, changes nothing, spends nothing. Skips the LinkedIn rendered-copy scan, which stays in `sequences start` without --approved.")
|
|
1859
|
+
.addOption(new Option("--include <fields>", "Add readiness or your retained guided_proposal, comma-separated. Reading a proposal does not apply it or start the campaign.")
|
|
1860
|
+
.choices(["readiness", "guided_proposal", "readiness,guided_proposal", "guided_proposal,readiness"]))
|
|
1837
1861
|
.option("--json", "Print a JSON envelope.")
|
|
1838
1862
|
.action(async (sequence, options) => {
|
|
1839
|
-
|
|
1863
|
+
const include = new Set(options.include?.split(",") ?? []);
|
|
1864
|
+
if (options.readiness)
|
|
1865
|
+
include.add("readiness");
|
|
1866
|
+
const query = include.size ? `?include=${[...include].join(",")}` : "";
|
|
1867
|
+
await handleAsyncAction("sequences get", options, () => requestOxygen(`/api/cli/sequences/${encodeURIComponent(sequence)}${query}`));
|
|
1840
1868
|
}))
|
|
1841
1869
|
.addCommand(new Command("enroll")
|
|
1842
1870
|
.description("Enroll leads into a sequence. Enrolling spends no credits. An active sequence contacts the new leads on its next dispatch tick; a draft or paused one holds them until `sequences start`. --from-table enrolls every not-yet-enrolled row of the sequence's bound source table (up to 500 per run; the response's from_table.has_more says whether to run again) — for a LinkedIn sequence the profile is read from the bound table's URL column, where a full /in/ URL and a bare public handle carrying a hyphen or digit (ada-lovelace) both work, so no pre-resolved provider ids are needed; each handle Oxygen canonicalized is echoed in linkedin_handles_canonicalized (value + url) to check before start, and every row that still cannot be resolved is named in unresolved_linkedin_lead_details (row, value, reason) rather than only counted in unresolved_linkedin_leads. In a LinkedIn + email sequence a row with no usable profile but a recipient email is still enrolled for the email steps (its LinkedIn steps are skipped as no_linkedin_profile, a connection wait passes at once and a connection branch takes its not-connected path) and is listed in linkedin_unavailable_lead_details instead; unresolved_linkedin_leads names only rows that were not enrolled. --leads-file enrolls an explicit JSON list { leads: [...] }: for an email/WhatsApp sequence a lead identified only by an email (row_values.email) or phone (row_values.phone) is auto-filed as a row in the sequence's leads table (auto-creating and binding one if there is none), deduped by email/phone, so no table has to exist first (the table is returned as leads_table with a web_url); a LinkedIn + email sequence auto-files an email-only lead the same way; a LinkedIn lead needs a lead_provider_id, a lead_profile_url (a /in/ URL or a bare public handle with a hyphen or digit), or a table_row_id whose row carries one of those. A CRM-only lead needs a stable table_row_id or lead_provider_id in addition to mapped row_values; a HubSpot contact id used in crm_task associations identifies the destination record, not the Oxygen enrollment. When the sequence is bound to a source table, a lead's table_row_id auto-snapshots that row's columns (incl. AI/tool outputs) into row_values for {{column}} copy — explicit row_values win. Idempotent per table row (and per email/phone for auto-filed leads). WhatsApp-only campaigns also enroll each normalized number once across all source rows and enrollment statuses; mixed campaigns keep other channels and skip duplicate WhatsApp actions. Duplicate protection defaults to this campaign; --whatsapp-duplicate-scope on create/update can additionally block identical copy across all campaigns. The org do-not-contact list is always enforced; --exclude-contacted and --suppress-list add further opt-in skips (reported under skipped_by_reason). Leads already owned by a sender account (from an earlier real send) are routed back to that same account; a lead owned by a sender NOT on this sequence is skipped (bound_to_other_sender) unless --ignore-sender-bindings. This enrolls into an OXYGEN Sequence; to put the same rows into a campaign you already run in Smartlead, Instantly, lemlist or HeyReach, use `sequences push-external`. esp.pool_scope identifies the active workspace inventory used for routing and strict_deferred_leads estimates; these estimates omit sequence sender restrictions, readiness and daily limits. A zero estimate does not guarantee readiness. Review sequences preview before launch.")
|
|
@@ -1,3 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Command } from "commander";
|
|
3
|
-
export declare function registerTablesFamily(program: Command, handleAsyncAction: ExtractedCommandActionHandler, binaryName: any): void;
|
|
1
|
+
export { registerTablesFamily } from "../command-families/tables/registration.js";
|