@erdoai/cli 0.126.0 → 0.128.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +44 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1257,10 +1257,22 @@ var ErdoClient = class {
|
|
|
1257
1257
|
}
|
|
1258
1258
|
// The captures the platform refused to resolve: two leads sharing a contact
|
|
1259
1259
|
// detail, or a submission whose email and phone point at different leads.
|
|
1260
|
-
listLeadMergeCandidates(datasetSlug, limit) {
|
|
1261
|
-
const
|
|
1260
|
+
listLeadMergeCandidates(datasetSlug, limit, traffic) {
|
|
1261
|
+
const query = new URLSearchParams();
|
|
1262
|
+
if (limit !== void 0) query.set("limit", String(limit));
|
|
1263
|
+
if (traffic !== void 0) query.set("traffic", traffic);
|
|
1264
|
+
const qs = query.size ? `?${query}` : "";
|
|
1262
1265
|
return this.request("GET", `/v1/datasets/${encodeURIComponent(datasetSlug)}/lead-candidates${qs}`);
|
|
1263
1266
|
}
|
|
1267
|
+
resolveLeadCapture(datasetSlug, captureID, input) {
|
|
1268
|
+
if (!["apply_to_lead", "dismiss"].includes(input.action) || typeof input.reason !== "string" || !input.reason.trim()) {
|
|
1269
|
+
throw new Error("A resolution action and reason are required.");
|
|
1270
|
+
}
|
|
1271
|
+
if (input.apply && (!input.operation_key || !input.expected_snapshot)) {
|
|
1272
|
+
throw new Error("Apply requires operation_key and expected_snapshot from the exact preview.");
|
|
1273
|
+
}
|
|
1274
|
+
return this.request("POST", `/v1/datasets/${encodeURIComponent(datasetSlug)}/lead-captures/${encodeURIComponent(captureID)}/resolve`, input);
|
|
1275
|
+
}
|
|
1264
1276
|
// Not reversible in practice: the absorbed lead's row values are combined into
|
|
1265
1277
|
// the survivor's and its row is gone. It needs no idempotency key — the pair it
|
|
1266
1278
|
// joins identifies it, so a retry answers "already_merged".
|
|
@@ -1784,6 +1796,7 @@ var ErdoClient = class {
|
|
|
1784
1796
|
if (opts.applicability) params.set("applicability", opts.applicability);
|
|
1785
1797
|
if (opts.outcome) params.set("outcome", opts.outcome);
|
|
1786
1798
|
if (opts.approval_id) params.set("approval_id", opts.approval_id);
|
|
1799
|
+
if (opts.since) params.set("since", opts.since);
|
|
1787
1800
|
if (opts.limit) params.set("limit", String(opts.limit));
|
|
1788
1801
|
if (opts.offset) params.set("offset", String(opts.offset));
|
|
1789
1802
|
const qs = params.toString();
|
|
@@ -4397,7 +4410,7 @@ decisionsCmd.command("list").description("List decisions, newest first").option(
|
|
|
4397
4410
|
).option("--applicability <kind>", "standing | one_shot").option("--outcome <outcome>", "only decisions with a settled effect that came back met | not_met | inconclusive").option(
|
|
4398
4411
|
"--approval <id>",
|
|
4399
4412
|
"only the decisions produced by answering this approval (approvals are referenced by id, not slug)"
|
|
4400
|
-
).option("-l, --limit <n>", "max rows (default 50, max 200)", (v) => parseInt(v, 10)).option("--offset <n>", "pagination offset", (v) => parseInt(v, 10)).option("--json", "output raw JSON").action(
|
|
4413
|
+
).option("--since <timestamp>", "inclusive RFC3339 lower bound on the latest known decision movement").option("-l, --limit <n>", "max rows (default 50, max 200)", (v) => parseInt(v, 10)).option("--offset <n>", "pagination offset", (v) => parseInt(v, 10)).option("--json", "output raw JSON").action(
|
|
4401
4414
|
async (opts) => {
|
|
4402
4415
|
try {
|
|
4403
4416
|
const res = await new ErdoClient().listDecisions({
|
|
@@ -4411,6 +4424,7 @@ decisionsCmd.command("list").description("List decisions, newest first").option(
|
|
|
4411
4424
|
applicability: opts.applicability,
|
|
4412
4425
|
outcome: opts.outcome,
|
|
4413
4426
|
approval_id: opts.approval,
|
|
4427
|
+
since: opts.since,
|
|
4414
4428
|
limit: opts.limit,
|
|
4415
4429
|
offset: opts.offset
|
|
4416
4430
|
});
|
|
@@ -6749,7 +6763,7 @@ datasetsCmd.command("create <name>").description(
|
|
|
6749
6763
|
});
|
|
6750
6764
|
datasetsCmd.command("ensure <purpose>").description(
|
|
6751
6765
|
"Create or reuse the one dataset for a purpose and verify its declared producer contract. Refuses legacy splits and incompatible populated datasets instead of choosing silently."
|
|
6752
|
-
).requiredOption("--schema <file>", "JSON file containing { columns: [...] } or a columns array").option("--name <name>", "dataset name when creation is required").option("--description <text>", "dataset description when creation is required").option("--purpose-description <text>", "what writes and reads this purpose").option("--preferred-dataset <slug>", "existing bound dataset slug or id to adopt").option("--email-column <name>", "for a leads purpose: which declared column holds the email (default email)").option("--phone-column <name>", "for a leads purpose: which declared column holds the phone (default phone)").action(async (purpose, opts) => {
|
|
6766
|
+
).requiredOption("--schema <file>", "JSON file containing { columns: [...] } or a columns array").option("--name <name>", "dataset name when creation is required").option("--description <text>", "dataset description when creation is required").option("--purpose-description <text>", "what writes and reads this purpose").option("--preferred-dataset <slug>", "existing bound dataset slug or id to adopt").option("--reviewed-optional-addition <name>", "producer-reviewed optional column to add atomically; repeat for multiple columns", (value, previous = []) => [...previous, value], []).option("--email-column <name>", "for a leads purpose: which declared column holds the email (default email)").option("--phone-column <name>", "for a leads purpose: which declared column holds the phone (default phone)").action(async (purpose, opts) => {
|
|
6753
6767
|
try {
|
|
6754
6768
|
const declaredSchema = readSchemaFile(opts.schema);
|
|
6755
6769
|
const result = await new ErdoClient().ensureDataset({
|
|
@@ -6758,6 +6772,7 @@ datasetsCmd.command("ensure <purpose>").description(
|
|
|
6758
6772
|
...opts.description ? { description: opts.description } : {},
|
|
6759
6773
|
...opts.purposeDescription ? { purpose_description: opts.purposeDescription } : {},
|
|
6760
6774
|
...opts.preferredDataset ? { preferred_dataset: opts.preferredDataset } : {},
|
|
6775
|
+
...opts.reviewedOptionalAddition.length ? { reviewed_optional_additions: opts.reviewedOptionalAddition } : {},
|
|
6761
6776
|
...opts.emailColumn ? { email_column: opts.emailColumn } : {},
|
|
6762
6777
|
...opts.phoneColumn ? { phone_column: opts.phoneColumn } : {},
|
|
6763
6778
|
declared_schema: declaredSchema
|
|
@@ -7392,10 +7407,10 @@ leadsCmd.command("get <dataset> <lead>").description(
|
|
|
7392
7407
|
}
|
|
7393
7408
|
});
|
|
7394
7409
|
leadsCmd.command("candidates <dataset>").description(
|
|
7395
|
-
"List
|
|
7396
|
-
).option("-l, --limit <n>", "maximum captures to return (default 50, maximum 200)", (v) => parseInt(v, 10)).option("--json", "print the raw JSON result instead of a table").action(async (dataset, opts) => {
|
|
7410
|
+
"List unresolved refused submissions and their candidate leads. Inspect the saved evidence before previewing an apply-to-lead or dismissal decision with leads resolve-capture."
|
|
7411
|
+
).option("-l, --limit <n>", "maximum captures to return (default 50, maximum 200)", (v) => parseInt(v, 10)).addOption(new Option2("--traffic <kind>", "include real submissions, independently trusted tests, or both").choices(["real", "test", "all"])).option("--json", "print the raw JSON result instead of a table").action(async (dataset, opts) => {
|
|
7397
7412
|
try {
|
|
7398
|
-
const res = await new ErdoClient().listLeadMergeCandidates(dataset, opts.limit);
|
|
7413
|
+
const res = await new ErdoClient().listLeadMergeCandidates(dataset, opts.limit, opts.traffic);
|
|
7399
7414
|
if (opts.json) {
|
|
7400
7415
|
print(res);
|
|
7401
7416
|
return;
|
|
@@ -7413,8 +7428,10 @@ leadsCmd.command("candidates <dataset>").description(
|
|
|
7413
7428
|
}
|
|
7414
7429
|
const rows = [];
|
|
7415
7430
|
for (const item of candidates) {
|
|
7431
|
+
if (!item.candidates?.length) rows.push([item.capture.id, item.capture.created_at, item.capture.resolution, "No candidate", "", "", ""]);
|
|
7416
7432
|
for (const candidate of item.candidates ?? []) {
|
|
7417
7433
|
rows.push([
|
|
7434
|
+
item.capture.id,
|
|
7418
7435
|
item.capture.created_at,
|
|
7419
7436
|
item.capture.resolution,
|
|
7420
7437
|
candidate.canonical_lead_id,
|
|
@@ -7424,15 +7441,29 @@ leadsCmd.command("candidates <dataset>").description(
|
|
|
7424
7441
|
]);
|
|
7425
7442
|
}
|
|
7426
7443
|
}
|
|
7427
|
-
printAlignedTable(["captured", "why", "lead", "emails", "phones", "settled"], rows);
|
|
7444
|
+
printAlignedTable(["receipt", "captured", "why", "lead", "emails", "phones", "settled"], rows);
|
|
7428
7445
|
console.log(
|
|
7429
7446
|
`
|
|
7430
|
-
|
|
7447
|
+
Review saved submission evidence with: erdo leads resolve-capture ${res.dataset_slug} <receipt> --input <decision.json>`
|
|
7431
7448
|
);
|
|
7432
7449
|
} catch (e) {
|
|
7433
7450
|
fail(e);
|
|
7434
7451
|
}
|
|
7435
7452
|
});
|
|
7453
|
+
leadsCmd.command("resolve-capture <dataset> <receipt>").description("Preview a refused submission against a candidate lead, or record an audited dismissal. Applying preserves the original receipt and does not merge identities, report a conversion, or replay arrival automations.").requiredOption("--input <file>", "JSON decision: action, target_lead_id for apply_to_lead, reason; applying also requires expected_snapshot and operation_key").option("--apply", "confirm the exact preview; without this flag the decision only previews").action(async (dataset, receipt, opts) => {
|
|
7454
|
+
try {
|
|
7455
|
+
const input = JSON.parse(readFileSync4(opts.input, "utf8"));
|
|
7456
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("--input must contain a JSON object");
|
|
7457
|
+
if (Object.hasOwn(input, "apply")) throw new Error("Use the explicit --apply flag; do not put apply in the decision file");
|
|
7458
|
+
for (const key of Object.keys(input)) {
|
|
7459
|
+
if (!["action", "target_lead_id", "reason", "expected_snapshot", "operation_key"].includes(key)) throw new Error(`Unknown decision field: ${key}`);
|
|
7460
|
+
}
|
|
7461
|
+
const result = await new ErdoClient().resolveLeadCapture(dataset, receipt, { ...input, apply: Boolean(opts.apply) });
|
|
7462
|
+
print(result);
|
|
7463
|
+
} catch (e) {
|
|
7464
|
+
fail(e);
|
|
7465
|
+
}
|
|
7466
|
+
});
|
|
7436
7467
|
leadsCmd.command("merge <dataset> <survivor>").description(
|
|
7437
7468
|
"Merge two leads into one. The absorbed lead stops being its own lead: its contact evidence moves onto the survivor, and the two dataset rows become one row \u2014 a value the absorbed row carries fills a blank in the survivor's row, and never replaces one the survivor already holds unless you name the column with --overwrite-column. This cannot be undone: the absorbed row's values are combined away and only the pointer survives, so an id already handed out keeps resolving. Read both leads with `leads get` before running it."
|
|
7438
7469
|
).requiredOption("--absorb <lead>", "the lead that stops being its own lead (UUID or lead reference)").option(
|
|
@@ -7907,6 +7938,10 @@ must not affect priority ${rb.priority.forbidden_factors.join(", ")}`);
|
|
|
7907
7938
|
}
|
|
7908
7939
|
}
|
|
7909
7940
|
if (rb.flow) printLeadPlaybookFlow(rb.flow);
|
|
7941
|
+
if (rb.call_plan?.length) {
|
|
7942
|
+
console.log("\ncalls to a lead who has not answered (enforced)");
|
|
7943
|
+
for (const line of rb.call_plan) console.log(` ${line}`);
|
|
7944
|
+
}
|
|
7910
7945
|
if (rb.limits?.length) {
|
|
7911
7946
|
console.log("\nlimits Erdo applied");
|
|
7912
7947
|
for (const l of rb.limits) console.log(` ${l.kind} \u2192 ${l.mode}: ${l.reason}`);
|