@erdoai/cli 0.130.0 → 0.131.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.
Files changed (2) hide show
  1. package/dist/index.js +61 -4
  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
  }
@@ -4679,6 +4689,7 @@ function renderApproval(r) {
4679
4689
  }
4680
4690
  facts.push(["created", r.created_at]);
4681
4691
  if (r.decided_at) facts.push(["decided", r.decided_at]);
4692
+ if (r.decision_reason) facts.push(["because", r.decision_reason]);
4682
4693
  if (r.expires_at) facts.push(["expires", r.expires_at]);
4683
4694
  if (r.execution) {
4684
4695
  const reason = r.execution.summary ? ` \u2014 ${r.execution.summary}` : "";
@@ -4738,6 +4749,9 @@ approvalsCmd.command("decide <id>").description("Approve or reject a pending app
4738
4749
  ).option(
4739
4750
  "--constraints <json>",
4740
4751
  `explicit parameter constraints for the standing policy, e.g. '{"spreadsheet_id":{"values":["..."]}}'`
4752
+ ).option(
4753
+ "--reason <text>",
4754
+ "why you decided this way. On --reject it holds the same proposal back for 7 days: a re-proposal of the same subject that states no new reason is answered with your decline instead of a new approval, and the decision record keeps it as the decider's rationale"
4741
4755
  ).action(
4742
4756
  async (id, opts) => {
4743
4757
  try {
@@ -4774,7 +4788,8 @@ ${listing}`);
4774
4788
  const res = await client.decideApproval(id, {
4775
4789
  decision: opts.approve ? "approved" : "rejected",
4776
4790
  scope: opts.scope,
4777
- parameter_constraints: constraints
4791
+ parameter_constraints: constraints,
4792
+ ...opts.reason?.trim() ? { reason: opts.reason.trim() } : {}
4778
4793
  });
4779
4794
  console.log(res.message);
4780
4795
  } catch (e) {
@@ -4828,7 +4843,7 @@ approvalsCmd.command("settings [mode]").description(
4828
4843
  });
4829
4844
  var attnCmd = program.command("attention").description("The attention feed \u2014 digests, choices, escalations awaiting a human");
4830
4845
  function attentionItemLine(item) {
4831
- return [item.slug, item.severity, item.kind, item.status, item.title].join(" ");
4846
+ return [item.slug, item.severity, item.kind, item.ask ?? "", item.status, item.title].join(" ");
4832
4847
  }
4833
4848
  function renderAttentionItemReport(item, report) {
4834
4849
  console.log("");
@@ -5359,6 +5374,12 @@ pagesCmd.command("update <id>").description(
5359
5374
  ).option(
5360
5375
  "--meta-image-asset <attachmentId>",
5361
5376
  'new public share image: a Brand Style attachment id (og-image / hero photo) (omit to keep current; pass "" to clear)'
5377
+ ).option(
5378
+ "--build-brief <textOr@file>",
5379
+ "new judge build brief (or @path to a UTF-8 file; omit to keep current, pass an empty string to clear)"
5380
+ ).option(
5381
+ "--required-modules <csv>",
5382
+ 'replacement runtime module list (comma-separated; "[]" clears)'
5362
5383
  ).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
5384
  "--request-review",
5364
5385
  "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 +5395,8 @@ pagesCmd.command("update <id>").description(
5374
5395
  meta_description: opts.metaDescription,
5375
5396
  meta_icon_asset_id: opts.metaIconAsset,
5376
5397
  meta_image_asset_id: opts.metaImageAsset,
5398
+ build_brief: readMaybeFile(opts.buildBrief),
5399
+ required_modules: grantList(opts.requiredModules),
5377
5400
  dataset_slugs: grantList(opts.datasets),
5378
5401
  writable_dataset_slugs: grantList(opts.writableDatasets),
5379
5402
  kv_slugs: grantList(opts.kv),
@@ -7402,7 +7425,7 @@ filterCmd.command("list <slug>").description("List the default filters on a data
7402
7425
  }
7403
7426
  });
7404
7427
  var leadsCmd = program.command("leads").description(
7405
- "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"
7428
+ "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"
7406
7429
  );
7407
7430
  leadsCmd.command("get <dataset> <lead>").description(
7408
7431
  "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."
@@ -7516,6 +7539,40 @@ leadsCmd.command("merge <dataset> <survivor>").description(
7516
7539
  }
7517
7540
  }
7518
7541
  );
7542
+ leadsCmd.command("delete <dataset> <lead>").description(
7543
+ "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."
7544
+ ).option("-y, --yes", "skip the confirmation prompt").option("--json", "print the raw JSON result instead of a summary").action(async (dataset, lead, opts) => {
7545
+ try {
7546
+ if (!opts.yes) {
7547
+ const answer = await promptLine(
7548
+ `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: `
7549
+ );
7550
+ if (answer.trim() !== "delete") {
7551
+ console.error("Not deleted.");
7552
+ process.exit(1);
7553
+ }
7554
+ }
7555
+ const res = await new ErdoClient().deleteLead(dataset, lead);
7556
+ if (opts.json) {
7557
+ print(res);
7558
+ return;
7559
+ }
7560
+ console.log(
7561
+ res.state === "already_deleted" ? `Lead ${res.reference} was already deleted. That delete removed:` : `Deleted lead ${res.reference}. Removed:`
7562
+ );
7563
+ const removed = Object.entries(res.removed ?? {}).filter(([, count]) => count > 0).sort(([a], [b]) => a.localeCompare(b));
7564
+ if (removed.length === 0) console.log(" nothing");
7565
+ for (const [kind, count] of removed) console.log(` ${kind.replace(/_/g, " ")}: ${count}`);
7566
+ const shared = (res.shared_phones ?? 0) + (res.shared_emails ?? 0);
7567
+ if (shared > 0) {
7568
+ console.log(
7569
+ `${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"}.`
7570
+ );
7571
+ }
7572
+ } catch (e) {
7573
+ fail(e);
7574
+ }
7575
+ });
7519
7576
  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");
7520
7577
  leadPlaybookCmd.command("get").description(
7521
7578
  "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.130.0",
3
+ "version": "0.131.0",
4
4
  "description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
5
5
  "type": "module",
6
6
  "bin": {