@erdoai/cli 0.104.1 → 0.107.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 +149 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -576,6 +576,31 @@ var ErdoClient = class {
576
576
  `/v1/voice/sms-conversations/${encodeURIComponent(sessionID)}${qs ? `?${qs}` : ""}`
577
577
  );
578
578
  }
579
+ // WhatsApp conversations on those same numbers — their own resource rather
580
+ // than a channel on the SMS reads, addressed and filtered the same way.
581
+ listWhatsAppConversations(params) {
582
+ const q = new URLSearchParams();
583
+ if (params?.phone_number) q.set("phone_number", params.phone_number);
584
+ if (params?.address) q.set("address", params.address);
585
+ if (params?.canonical_lead_id) q.set("canonical_lead_id", params.canonical_lead_id);
586
+ if (params?.limit !== void 0) q.set("limit", String(params.limit));
587
+ if (params?.cursor) q.set("cursor", params.cursor);
588
+ const qs = q.toString();
589
+ return this.request(
590
+ "GET",
591
+ `/v1/voice/whatsapp-conversations${qs ? `?${qs}` : ""}`
592
+ );
593
+ }
594
+ getWhatsAppConversation(sessionID, params) {
595
+ const q = new URLSearchParams();
596
+ if (params?.limit !== void 0) q.set("limit", String(params.limit));
597
+ if (params?.cursor) q.set("cursor", params.cursor);
598
+ const qs = q.toString();
599
+ return this.request(
600
+ "GET",
601
+ `/v1/voice/whatsapp-conversations/${encodeURIComponent(sessionID)}${qs ? `?${qs}` : ""}`
602
+ );
603
+ }
579
604
  // The organization's business profile: its legal identity, which every A2P
580
605
  // registration on the account is checked against. It follows the org
581
606
  // convention rather than the voice one because it is the organization's, not
@@ -5203,8 +5228,8 @@ pagesCmd.command("restore <id>").description("Restore a previously deleted page
5203
5228
  }
5204
5229
  });
5205
5230
  pagesCmd.command("monitor <id>").description(
5206
- "Control health monitoring for a published page: --exclude stops all probing (dashboards, trackers, demos); --ack <sig> acknowledges a known failure class (mobile:overflow, desktop:resource, visual:defect, visual:score) so it stops alerting and never auto-repairs while a NEW failure class still alerts"
5207
- ).option("--exclude [reason]", "exclude this page from monitoring entirely (optional reason)").option("--include", "re-include the page in monitoring").option("--ack <sigs>", "comma-separated failure signatures to acknowledge (e.g. mobile:overflow,desktop:resource,visual:defect,visual:score)").option("--unack <sigs>", "comma-separated failure signatures to un-acknowledge").option("--reset", "clear all acknowledged failure signatures").action(async (id, opts) => {
5231
+ "Control health monitoring for a published page: --exclude stops all probing (dashboards, trackers, demos); --canary opts a PRIVATE page into the browser canary (renders it as its owner sees it and reports a broken render to the owner); --ack <sig> acknowledges a known failure class (mobile:overflow, desktop:resource, visual:defect, visual:score) so it stops alerting and never auto-repairs while a NEW failure class still alerts"
5232
+ ).option("--exclude [reason]", "exclude this page from monitoring entirely (optional reason)").option("--include", "re-include the page in monitoring").option("--canary", "opt this private page into the browser canary").option("--no-canary", "opt this private page out of the browser canary").option("--ack <sigs>", "comma-separated failure signatures to acknowledge (e.g. mobile:overflow,desktop:resource,visual:defect,visual:score)").option("--unack <sigs>", "comma-separated failure signatures to un-acknowledge").option("--reset", "clear all acknowledged failure signatures").action(async (id, opts) => {
5208
5233
  try {
5209
5234
  const api = new ErdoClient();
5210
5235
  const body = {};
@@ -5213,6 +5238,7 @@ pagesCmd.command("monitor <id>").description(
5213
5238
  body.excluded = true;
5214
5239
  body.excluded_reason = opts.exclude;
5215
5240
  } else if (opts.exclude === true) body.excluded = true;
5241
+ if (opts.canary !== void 0) body.canary = opts.canary;
5216
5242
  if (opts.ack) body.ack_add = opts.ack.split(",").map((s) => s.trim()).filter(Boolean);
5217
5243
  if (opts.unack) body.ack_remove = opts.unack.split(",").map((s) => s.trim()).filter(Boolean);
5218
5244
  if (opts.reset) body.ack_reset = true;
@@ -5967,6 +5993,127 @@ contact: ${parts.join(" \xB7 ")}`);
5967
5993
  }
5968
5994
  }
5969
5995
  );
5996
+ var voiceWhatsAppCmd = voiceCmd.command("whatsapp").description("WhatsApp conversations on a voice agent's own number");
5997
+ voiceWhatsAppCmd.command("list").description("List the organization's WhatsApp conversations, most recently active first").option("--phone-number <number>", "only conversations on this Erdo number (any format)").option("--address <number>", "only the conversation with this person (any format)").option("--lead <lead>", LEAD_FILTER_HELP).option("--limit <n>", "page size (default 25, max 100)").option("--cursor <cursor>", "next_cursor from the preceding page (stable paging)").option("--json", "print the raw JSON result instead of a table").action(
5998
+ async (opts) => {
5999
+ try {
6000
+ const res = await new ErdoClient().listWhatsAppConversations({
6001
+ phone_number: opts.phoneNumber,
6002
+ address: opts.address,
6003
+ canonical_lead_id: opts.lead,
6004
+ limit: opts.limit ? Number(opts.limit) : void 0,
6005
+ cursor: opts.cursor
6006
+ });
6007
+ if (opts.json) {
6008
+ print(res);
6009
+ return;
6010
+ }
6011
+ const conversations = res.conversations ?? [];
6012
+ if (conversations.length === 0) {
6013
+ console.log("No WhatsApp conversations match those filters.");
6014
+ return;
6015
+ }
6016
+ printAlignedTable(
6017
+ ["session id", "phone", "address", "name", "msgs", "last inbound", "last outbound", "lead ref", "summary"],
6018
+ conversations.map((c) => [
6019
+ c.session_id,
6020
+ c.phone_number,
6021
+ c.suppressed ? `${c.address} (suppressed)` : c.address,
6022
+ c.display_name ?? "",
6023
+ c.message_count,
6024
+ c.last_inbound_at ?? "",
6025
+ c.last_outbound_at ?? "",
6026
+ leadReferenceLabel(c.lead_reference),
6027
+ c.summary
6028
+ ])
6029
+ );
6030
+ process.stderr.write(`showing ${conversations.length} conversation(s)
6031
+ `);
6032
+ if (res.next_cursor) {
6033
+ process.stderr.write(`more available \u2014 re-run with --cursor ${res.next_cursor}
6034
+ `);
6035
+ }
6036
+ } catch (e) {
6037
+ fail(e);
6038
+ }
6039
+ }
6040
+ );
6041
+ function whatsAppMessageText(m) {
6042
+ const parts = [];
6043
+ if (m.body) parts.push(m.body);
6044
+ if (m.location) {
6045
+ const where = [m.location.label, m.location.address].filter(Boolean).join(", ");
6046
+ parts.push(`[location ${m.location.latitude},${m.location.longitude}${where ? ` ${where}` : ""}]`);
6047
+ }
6048
+ if (m.context?.button_text) parts.push(`[tapped "${m.context.button_text}"]`);
6049
+ else if (m.context?.replied_message_id) parts.push(`[reply to ${m.context.replied_message_id}]`);
6050
+ if (m.referral) parts.push(`[from ad${m.referral.Headline ? ` "${m.referral.Headline}"` : ""}]`);
6051
+ if (parts.length === 0 && m.message_type && m.message_type !== "text") parts.push(`[${m.message_type}]`);
6052
+ return parts.join(" ");
6053
+ }
6054
+ voiceWhatsAppCmd.command("get <sessionID>").description("Read one WhatsApp conversation: its metadata, then every message oldest first").option("--limit <n>", "messages per page (default 50, max 200)").option("--cursor <cursor>", "next_cursor from the preceding page of messages").option("--json", "print the raw JSON result instead of a table").action(
6055
+ async (sessionID, opts) => {
6056
+ try {
6057
+ const res = await new ErdoClient().getWhatsAppConversation(sessionID, {
6058
+ limit: opts.limit ? Number(opts.limit) : void 0,
6059
+ cursor: opts.cursor
6060
+ });
6061
+ if (opts.json) {
6062
+ print(res);
6063
+ return;
6064
+ }
6065
+ const c = res.conversation;
6066
+ printAlignedTable(
6067
+ ["session id", "phone", "address", "name", "msgs", "last inbound", "last outbound", "summary"],
6068
+ [
6069
+ [
6070
+ c.session_id,
6071
+ c.phone_number,
6072
+ c.suppressed ? `${c.address} (suppressed)` : c.address,
6073
+ c.display_name ?? "",
6074
+ c.message_count,
6075
+ c.last_inbound_at ?? "",
6076
+ c.last_outbound_at ?? "",
6077
+ c.summary
6078
+ ]
6079
+ ]
6080
+ );
6081
+ const contact = c.contact;
6082
+ if (contact && (contact.name || contact.email || contact.phone || contact.company)) {
6083
+ const parts = [contact.name, contact.email, contact.phone, contact.company].filter(Boolean);
6084
+ console.log(`
6085
+ contact: ${parts.join(" \xB7 ")}`);
6086
+ }
6087
+ if (c.lead_reference) {
6088
+ console.log(`lead: ${c.lead_reference} (${c.canonical_lead_id})`);
6089
+ }
6090
+ const messages = res.messages ?? [];
6091
+ if (messages.length === 0) {
6092
+ console.log("\nNo messages have been recorded in this conversation yet.");
6093
+ return;
6094
+ }
6095
+ console.log("");
6096
+ printAlignedTable(
6097
+ ["at", "direction", "status", "media", "message"],
6098
+ messages.map((m) => [
6099
+ m.created_at,
6100
+ m.direction,
6101
+ m.status,
6102
+ m.num_media > 0 ? m.num_media : "",
6103
+ whatsAppMessageText(m)
6104
+ ])
6105
+ );
6106
+ process.stderr.write(`showing ${messages.length} message(s), oldest first
6107
+ `);
6108
+ if (res.next_cursor) {
6109
+ process.stderr.write(`more available \u2014 re-run with --cursor ${res.next_cursor}
6110
+ `);
6111
+ }
6112
+ } catch (e) {
6113
+ fail(e);
6114
+ }
6115
+ }
6116
+ );
5970
6117
  var voiceNumbersCmd = voiceCmd.command("numbers").description("The organization's phone numbers, their A2P SMS registration and their caller name");
5971
6118
  function printRegistrationState(registration) {
5972
6119
  if (registration === null || typeof registration !== "object") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/cli",
3
- "version": "0.104.1",
3
+ "version": "0.107.0",
4
4
  "description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
5
5
  "type": "module",
6
6
  "bin": {