@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.
Files changed (2) hide show
  1. package/dist/index.js +70 -6
  2. 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 (!value) return void 0;
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 caller name");
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."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/cli",
3
- "version": "0.129.0",
3
+ "version": "0.130.1",
4
4
  "description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
5
5
  "type": "module",
6
6
  "bin": {