@erdoai/cli 0.129.0 → 0.130.1
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 +70 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1283,6 +1283,16 @@ var ErdoClient = class {
|
|
|
1283
1283
|
input
|
|
1284
1284
|
);
|
|
1285
1285
|
}
|
|
1286
|
+
// Cannot be undone. Removes the lead from every Erdo store so the same phone
|
|
1287
|
+
// and email can arrive as a new lead. Staff-only, for demos and tests. A
|
|
1288
|
+
// retry of a delete that stopped part-way resumes it, and a lead already
|
|
1289
|
+
// deleted answers "already_deleted".
|
|
1290
|
+
deleteLead(datasetSlug, lead) {
|
|
1291
|
+
return this.request(
|
|
1292
|
+
"DELETE",
|
|
1293
|
+
`/v1/datasets/${encodeURIComponent(datasetSlug)}/leads/${encodeURIComponent(lead)}`
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1286
1296
|
// The lead playbook: plain text that decides each lead's next action. An
|
|
1287
1297
|
// organization that never saved one gets the default template, exists=false.
|
|
1288
1298
|
getLeadPlaybook() {
|
|
@@ -2054,7 +2064,7 @@ async function browserLogin() {
|
|
|
2054
2064
|
// src/input.ts
|
|
2055
2065
|
import { readFileSync as readFileSync2 } from "fs";
|
|
2056
2066
|
function readMaybeFile(value) {
|
|
2057
|
-
if (
|
|
2067
|
+
if (value === void 0) return void 0;
|
|
2058
2068
|
if (value.startsWith("@@")) return value.slice(1);
|
|
2059
2069
|
return value.startsWith("@") ? readFileSync2(value.slice(1), "utf8") : value;
|
|
2060
2070
|
}
|
|
@@ -4828,7 +4838,7 @@ approvalsCmd.command("settings [mode]").description(
|
|
|
4828
4838
|
});
|
|
4829
4839
|
var attnCmd = program.command("attention").description("The attention feed \u2014 digests, choices, escalations awaiting a human");
|
|
4830
4840
|
function attentionItemLine(item) {
|
|
4831
|
-
return [item.slug, item.severity, item.kind, item.status, item.title].join(" ");
|
|
4841
|
+
return [item.slug, item.severity, item.kind, item.ask ?? "", item.status, item.title].join(" ");
|
|
4832
4842
|
}
|
|
4833
4843
|
function renderAttentionItemReport(item, report) {
|
|
4834
4844
|
console.log("");
|
|
@@ -5359,6 +5369,12 @@ pagesCmd.command("update <id>").description(
|
|
|
5359
5369
|
).option(
|
|
5360
5370
|
"--meta-image-asset <attachmentId>",
|
|
5361
5371
|
'new public share image: a Brand Style attachment id (og-image / hero photo) (omit to keep current; pass "" to clear)'
|
|
5372
|
+
).option(
|
|
5373
|
+
"--build-brief <textOr@file>",
|
|
5374
|
+
"new judge build brief (or @path to a UTF-8 file; omit to keep current, pass an empty string to clear)"
|
|
5375
|
+
).option(
|
|
5376
|
+
"--required-modules <csv>",
|
|
5377
|
+
'replacement runtime module list (comma-separated; "[]" clears)'
|
|
5362
5378
|
).option("--datasets <csv>", 'replacement read-dataset slugs ("[]" clears)').option("--writable-datasets <csv>", 'replacement writable-dataset slugs ("[]" clears)').option("--kv <csv>", 'replacement read KV-store slugs ("[]" clears)').option("--writable-kv <csv>", 'replacement writable KV-store slugs ("[]" clears)').option("--public", "make the page publicly viewable").option("--private", "make the page private").option(
|
|
5363
5379
|
"--request-review",
|
|
5364
5380
|
"ask the judge lenses to review the page after this update and report findings -- for material edits to a LIVE page (layout, forms/CTA, legal/compliance copy, restructured sections), not cosmetic tweaks. First builds and full rewrites are already reviewed automatically"
|
|
@@ -5374,6 +5390,8 @@ pagesCmd.command("update <id>").description(
|
|
|
5374
5390
|
meta_description: opts.metaDescription,
|
|
5375
5391
|
meta_icon_asset_id: opts.metaIconAsset,
|
|
5376
5392
|
meta_image_asset_id: opts.metaImageAsset,
|
|
5393
|
+
build_brief: readMaybeFile(opts.buildBrief),
|
|
5394
|
+
required_modules: grantList(opts.requiredModules),
|
|
5377
5395
|
dataset_slugs: grantList(opts.datasets),
|
|
5378
5396
|
writable_dataset_slugs: grantList(opts.writableDatasets),
|
|
5379
5397
|
kv_slugs: grantList(opts.kv),
|
|
@@ -6330,7 +6348,7 @@ contact: ${parts.join(" \xB7 ")}`);
|
|
|
6330
6348
|
}
|
|
6331
6349
|
}
|
|
6332
6350
|
);
|
|
6333
|
-
var voiceNumbersCmd = voiceCmd.command("numbers").description("The organization's phone numbers, their A2P SMS registration and their
|
|
6351
|
+
var voiceNumbersCmd = voiceCmd.command("numbers").description("The organization's phone numbers, their A2P SMS registration, their caller name and their call attestation");
|
|
6334
6352
|
function printRegistrationState(registration) {
|
|
6335
6353
|
if (registration === null || typeof registration !== "object") {
|
|
6336
6354
|
if (registration !== void 0) print(registration);
|
|
@@ -6378,7 +6396,7 @@ voiceNumbersCmd.command("list").description("List the organization's numbers wit
|
|
|
6378
6396
|
} else {
|
|
6379
6397
|
const callable = new Set(res.person_call_numbers ?? []);
|
|
6380
6398
|
printAlignedTable(
|
|
6381
|
-
["number", "agent", "kind", "calls", "status", "waiting on", "reason", "caller name", "shows"],
|
|
6399
|
+
["number", "agent", "kind", "calls", "status", "waiting on", "reason", "caller name", "shows", "attest."],
|
|
6382
6400
|
numbers.map((n) => [
|
|
6383
6401
|
n.address,
|
|
6384
6402
|
n.agent_ref ?? "",
|
|
@@ -6388,7 +6406,8 @@ voiceNumbersCmd.command("list").description("List the organization's numbers wit
|
|
|
6388
6406
|
n.next_step ?? "",
|
|
6389
6407
|
n.reason ?? "",
|
|
6390
6408
|
n.caller_name_attachment ?? "",
|
|
6391
|
-
describeObservedCallerName(n.caller_name_observed, n.observed_caller_name)
|
|
6409
|
+
describeObservedCallerName(n.caller_name_observed, n.observed_caller_name),
|
|
6410
|
+
n.call_attestation ?? ""
|
|
6392
6411
|
])
|
|
6393
6412
|
);
|
|
6394
6413
|
process.stderr.write(`showing ${numbers.length} number(s)
|
|
@@ -6403,6 +6422,7 @@ voiceNumbersCmd.command("list").description("List the organization's numbers wit
|
|
|
6403
6422
|
console.log("Complete it with: erdo org business-profile set --help, or in Settings \u2192 Business.");
|
|
6404
6423
|
}
|
|
6405
6424
|
printStaleCallerNameNote(numbers);
|
|
6425
|
+
printCallAttestationNote(res.call_attestation);
|
|
6406
6426
|
} catch (e) {
|
|
6407
6427
|
fail(e);
|
|
6408
6428
|
}
|
|
@@ -6452,6 +6472,16 @@ voiceNumbersCmd.command("provision-sms <number>").description("Ask for A2P SMS r
|
|
|
6452
6472
|
fail(e);
|
|
6453
6473
|
}
|
|
6454
6474
|
});
|
|
6475
|
+
function printCallAttestationNote(attestation) {
|
|
6476
|
+
if (!attestation || attestation.status === "approved") return;
|
|
6477
|
+
console.log("");
|
|
6478
|
+
const waiting = attestation.next_step ? ` (waiting on ${attestation.next_step})` : "";
|
|
6479
|
+
console.log(`Call attestation (SHAKEN/STIR): ${attestation.status}${waiting} \u2014 calls carry B until it is approved.`);
|
|
6480
|
+
if (attestation.reason) console.log(attestation.reason);
|
|
6481
|
+
if (attestation.missing_profile_fields && attestation.missing_profile_fields.length > 0) {
|
|
6482
|
+
console.log("Complete the business profile with: erdo org business-profile set --help, or in Settings \u2192 Business.");
|
|
6483
|
+
}
|
|
6484
|
+
}
|
|
6455
6485
|
function describeObservedCallerName(observed, name) {
|
|
6456
6486
|
switch (observed) {
|
|
6457
6487
|
case "ours":
|
|
@@ -7390,7 +7420,7 @@ filterCmd.command("list <slug>").description("List the default filters on a data
|
|
|
7390
7420
|
}
|
|
7391
7421
|
});
|
|
7392
7422
|
var leadsCmd = program.command("leads").description(
|
|
7393
|
-
"Read leads in a lead dataset, merge two that are the same person, and run the lead playbook that suggests each lead's next action"
|
|
7423
|
+
"Read leads in a lead dataset, merge two that are the same person, delete one, and run the lead playbook that suggests each lead's next action"
|
|
7394
7424
|
);
|
|
7395
7425
|
leadsCmd.command("get <dataset> <lead>").description(
|
|
7396
7426
|
"Read one lead: its permanent canonical_lead_id, what it told us (on the form and in every call, chat, text and email reply since), the email and phone evidence bound to it, its dataset row, the leads merged into it, and its capture history. <lead> is either the canonical_lead_id UUID or the 22-character lead reference, and an id that has since been merged away resolves to the lead it became \u2014 so an id read off an old capture response still works."
|
|
@@ -7504,6 +7534,40 @@ leadsCmd.command("merge <dataset> <survivor>").description(
|
|
|
7504
7534
|
}
|
|
7505
7535
|
}
|
|
7506
7536
|
);
|
|
7537
|
+
leadsCmd.command("delete <dataset> <lead>").description(
|
|
7538
|
+
"Delete a lead from every Erdo store: its row and identity, its text and WhatsApp conversations, its calls and website chats, its emails, and the follow-up engine's decisions about it. The same phone and email submitted again then make a new lead with nothing behind it. This cannot be undone. A staff tool for demos and tests: copies held by providers (message bodies, call audio, sent mail) and in logs stay, and so do opt-outs. A phone or email another lead in the organization also holds is not released until that lead is deleted too. Read the lead with `leads get` before running it."
|
|
7539
|
+
).option("-y, --yes", "skip the confirmation prompt").option("--json", "print the raw JSON result instead of a summary").action(async (dataset, lead, opts) => {
|
|
7540
|
+
try {
|
|
7541
|
+
if (!opts.yes) {
|
|
7542
|
+
const answer = await promptLine(
|
|
7543
|
+
`Delete lead ${lead} from ${dataset}? Its row, conversations, calls, emails and follow-up history are removed and this cannot be undone. Type "delete" to continue: `
|
|
7544
|
+
);
|
|
7545
|
+
if (answer.trim() !== "delete") {
|
|
7546
|
+
console.error("Not deleted.");
|
|
7547
|
+
process.exit(1);
|
|
7548
|
+
}
|
|
7549
|
+
}
|
|
7550
|
+
const res = await new ErdoClient().deleteLead(dataset, lead);
|
|
7551
|
+
if (opts.json) {
|
|
7552
|
+
print(res);
|
|
7553
|
+
return;
|
|
7554
|
+
}
|
|
7555
|
+
console.log(
|
|
7556
|
+
res.state === "already_deleted" ? `Lead ${res.reference} was already deleted. That delete removed:` : `Deleted lead ${res.reference}. Removed:`
|
|
7557
|
+
);
|
|
7558
|
+
const removed = Object.entries(res.removed ?? {}).filter(([, count]) => count > 0).sort(([a], [b]) => a.localeCompare(b));
|
|
7559
|
+
if (removed.length === 0) console.log(" nothing");
|
|
7560
|
+
for (const [kind, count] of removed) console.log(` ${kind.replace(/_/g, " ")}: ${count}`);
|
|
7561
|
+
const shared = (res.shared_phones ?? 0) + (res.shared_emails ?? 0);
|
|
7562
|
+
if (shared > 0) {
|
|
7563
|
+
console.log(
|
|
7564
|
+
`${shared} of this lead's contact details ${shared === 1 ? "is" : "are"} also held by another lead in the organization, so what is kept by number or address for ${shared === 1 ? "it" : "them"} was left in place. Delete that lead too to release ${shared === 1 ? "it" : "them"}.`
|
|
7565
|
+
);
|
|
7566
|
+
}
|
|
7567
|
+
} catch (e) {
|
|
7568
|
+
fail(e);
|
|
7569
|
+
}
|
|
7570
|
+
});
|
|
7507
7571
|
var leadPlaybookCmd = leadsCmd.command("playbook").description("Read, try and save the lead playbook: the plain-text rules for what Erdo does next with each lead");
|
|
7508
7572
|
leadPlaybookCmd.command("get").description(
|
|
7509
7573
|
"Show the saved playbook: whether the sweep is enabled, the daily contact cap, the read-back Erdo enforces (each action's mode and the working hours), and the text. An organization that never saved one sees the default template."
|