@ghostystudio/cli 0.88.0 → 0.90.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 +33 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1175,7 +1175,7 @@ async function postLong(path2, body, timeoutMs = 9e5) {
1175
1175
  const req = httpsRequest(`${base}${path2}`, {
1176
1176
  method: "POST",
1177
1177
  timeout: timeoutMs,
1178
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.88.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
1178
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.90.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
1179
1179
  }, (res) => {
1180
1180
  let buf = "";
1181
1181
  res.on("data", (c) => buf += c);
@@ -1217,7 +1217,7 @@ async function request(path2, opts = {}, retried = false) {
1217
1217
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
1218
1218
  headers: {
1219
1219
  authorization: `Bearer ${token}`,
1220
- "user-agent": `ghosty-cli/${"0.88.0"}`,
1220
+ "user-agent": `ghosty-cli/${"0.90.0"}`,
1221
1221
  "accept-language": lang(),
1222
1222
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
1223
1223
  ...opts.headers
@@ -11278,7 +11278,7 @@ async function call(path2, v, init = {}) {
11278
11278
  "x-ghosty-ts": ts,
11279
11279
  "x-ghosty-sig": sig,
11280
11280
  "content-type": "application/json",
11281
- "user-agent": `ghosty-cli/${"0.88.0"}`
11281
+ "user-agent": `ghosty-cli/${"0.90.0"}`
11282
11282
  },
11283
11283
  body: method === "GET" ? void 0 : raw
11284
11284
  });
@@ -12126,6 +12126,7 @@ function skipReason(r) {
12126
12126
  channel_off: ["WhatsApp channel off for this agent", "canal de WhatsApp apagado en este agente"],
12127
12127
  no_quota: ["out of balance", "sin saldo"],
12128
12128
  own_number: ["sent by another of our agent numbers (bot loop guard)", "lo mand\xF3 otro n\xFAmero nuestro con agente (guarda de bucle)"],
12129
+ contact_off: ["the agent is turned off for this contact (whatsapp contacts --off)", "el agente est\xE1 apagado para este contacto (whatsapp contacts --off)"],
12129
12130
  known_contact: ["contact wrote before --new-only was turned on (only new contacts get the agent)", "el contacto ya escrib\xEDa antes de --new-only (el agente s\xF3lo contesta a contactos nuevos)"],
12130
12131
  partner_token: ["partner did not issue a turn token", "el partner no dio token de turno"],
12131
12132
  partner_busy: ["partner at its concurrent-turn limit", "el partner lleg\xF3 a su tope de turnos a la vez"]
@@ -12630,6 +12631,7 @@ async function channelsCmd(kind, sub, id, ref, v) {
12630
12631
  if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), t("Example", "Ejemplo") + ": ghosty channels whatsapp ls <agent-id>");
12631
12632
  if (sub === "link" || sub === "unlink") return linkCmd(sub, id, v);
12632
12633
  if (sub === "history") return historyCmd(id, v);
12634
+ if (sub === "contacts") return contactsCmd(id, v);
12633
12635
  if (sub === "integrations" && v.orphans) return linksCmd(id, "waba", true);
12634
12636
  if (sub === "integrations") return integrationsCmd(id, v.email);
12635
12637
  if (sub === "import" || sub === "status" || sub === "pause" || sub === "pair") return waCmd2(sub, id, v);
@@ -12997,6 +12999,24 @@ ${t(`Cards appear on the team's board (no "new lead" alerts).`, "Las tarjetas ap
12997
12999
  }
12998
13000
  out({ ...sum, conversations: done }, () => `${lavender("\u2713")} ${t(`${sum.newCards} new card(s), ${sum.updatedCards} updated, ${sum.messages} message(s), ${sum.orders} order(s)`, `${sum.newCards} tarjeta(s) nueva(s), ${sum.updatedCards} actualizada(s), ${sum.messages} mensaje(s), ${sum.orders} pedido(s)`)}${files(sum)}${sum.suppressed ? dim(t(` \xB7 ${sum.suppressed} bot draft(s) the customer never saw, skipped`, ` \xB7 se omitieron ${sum.suppressed} borrador(es) del bot que el cliente nunca vio`)) : ""}${pausedLine(sum, id)}`);
12999
13001
  }
13002
+ async function contactsCmd(id, v) {
13003
+ if (!id || !v.integration) throw usage(t("Missing agent id or --integration.", "Falta el id del agente o --integration."), t("Example", "Ejemplo") + ": ghosty channels whatsapp contacts <agent-id> --integration <id> [--new-only on|off] [--mute PHONE] [--allow PHONE] [--unmute PHONE] [--seed]");
13004
+ if (v["new-only"] !== void 0 && v["new-only"] !== "on" && v["new-only"] !== "off") throw usage(t("--new-only takes on or off.", "--new-only acepta on u off."));
13005
+ if ([v.mute, v.unmute, v.allow].filter((x) => x !== void 0).length > 1) throw usage(t("Use only one of --mute, --allow or --unmute.", "Usa s\xF3lo uno de --mute, --allow o --unmute."));
13006
+ const path2 = `/api/v2/agents/${enc11(id)}/whatsapp/contacts`;
13007
+ const contact = v.mute ?? v.allow ?? v.unmute;
13008
+ const write = v["new-only"] !== void 0 || contact !== void 0 || v.seed;
13009
+ const r = write ? await api(path2, { method: "POST", body: { number: v.integration, ...v["new-only"] !== void 0 ? { newOnly: v["new-only"] === "on" } : {}, ...contact !== void 0 ? v.allow !== void 0 ? { contact, always: true } : { contact, off: !!v.mute } : {}, ...v.seed ? { seed: true } : {} } }) : await api(`${path2}?number=${enc11(v.integration)}`);
13010
+ return out(r, () => {
13011
+ const lines = [
13012
+ `${t("New contacts only", "S\xF3lo contactos nuevos")}: ${r.newOnly ? lavender(t("on", "encendido")) : t("off", "apagado")}${r.newSince ? dim(` (${t("since", "desde")} ${r.newSince})`) : ""}`,
13013
+ `${t("Known contacts", "Contactos conocidos")}: ${r.known}${r.seededAt ? dim(` (${t("brought from Formmy", "tra\xEDdos de Formmy")} ${r.seededAt})`) : dim(t(" (still waiting for the WhatsApp history)", " (todav\xEDa esperando el historial de WhatsApp)"))}`,
13014
+ `${t("Always answers", "Siempre le contesta a")}: ${r.always?.length ? r.always.map((o) => o.name ? `${o.name} ${o.key}` : o.key).join(", ") : dim("\u2014")}`,
13015
+ `${t("Never answers", "Nunca le contesta a")}: ${r.off.length ? r.off.map((o) => o.name ? `${o.name} ${o.key}` : o.key).join(", ") : dim("\u2014")}`
13016
+ ];
13017
+ return lines.join("\n");
13018
+ });
13019
+ }
13000
13020
 
13001
13021
  // src/commands/board.ts
13002
13022
  init_api();
@@ -13670,6 +13690,7 @@ var HELPS_ES = {
13670
13690
  ["--cached", "groups ls: no le pide a WhatsApp la lista en vivo (m\xE1s r\xE1pido; puede faltar alg\xFAn grupo nuevo)"],
13671
13691
  ["--ads-only on|off", "link: s\xF3lo las conversaciones que vienen de un anuncio abren tarjeta (tambi\xE9n solo, para cambiarlo en un n\xFAmero ya ligado)"],
13672
13692
  ["--new-only on|off", "link: el agente s\xF3lo contesta a contactos que escriben por primera vez desde ahora; los de antes quedan en el hilo para ti (tambi\xE9n solo)"],
13693
+ ["contacts <id> --integration ID", "s\xF3lo contactos nuevos y contactos sin agente: --new-only on|off, --mute TEL (el agente nunca le contesta a ese n\xFAmero), --allow TEL (el agente le contesta aunque sea contacto conocido), --unmute TEL (vuelve a la regla general), --seed (vuelve a traer los contactos conocidos de WhatsApp)"],
13673
13694
  ["--board NAME|agent", "link: este n\xFAmero llena su propio tablero de ventas (nombre, id o main) en vez del del agente; las tools del CRM del agente trabajan ese tablero en los turnos de este n\xFAmero. agent = vuelve al del agente. Tambi\xE9n solo, en un n\xFAmero ya ligado"],
13674
13695
  ["--list", "history --dry-run: CU\xC1LES conversaciones (tel\xE9fono, nombre, primer mensaje, mensajes, si vino de anuncio)"],
13675
13696
  ["--only-ads / --phones a,b", "history: importa (o lista) s\xF3lo las de anuncio y/o esos tel\xE9fonos"],
@@ -14562,6 +14583,7 @@ HELPS.channels = {
14562
14583
  ["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
14563
14584
  ["--ads-only on|off", "link: only conversations that come from an ad open a card (also alone, to change it on a linked number)"],
14564
14585
  ["--new-only on|off", "link: the agent answers only contacts writing for the first time from now on; earlier contacts stay in the thread for you (also alone)"],
14586
+ ["contacts <id> --integration ID", "new contacts only and contacts without the agent: --new-only on|off, --mute PHONE (the agent never answers that number), --allow PHONE (the agent answers that number even if it's a known contact), --unmute PHONE (back to the general rule), --seed (bring the known contacts from WhatsApp again)"],
14565
14587
  ["--board NAME|agent", "link: this number fills its own sales board (name, id or main) instead of the agent's; the agent's CRM tools work that board on this number's turns. agent = back to the agent's board. Also alone, on a linked number"],
14566
14588
  ["--list", "history --dry-run: WHICH conversations (phone, name, first message, messages, from an ad)"],
14567
14589
  ["--only-ads / --phones a,b", "history: import (or list) only the ones from an ad and/or those phones"],
@@ -17043,6 +17065,7 @@ ${bold("Configure an agent")}
17043
17065
  channels messenger on|off <id> <page> Answer a page with this agent, or stop (off keeps logging to the board) [--take --dry-run --yes]
17044
17066
  channels mteams ls|connect|zip|on|off|rm <id> [bot] Microsoft Teams bot (Azure Bot in the client's tenant) [--app-id --tenant --secret|GHOSTY_MTEAMS_SECRET --name --out]
17045
17067
  channels whatsapp link|unlink <id> --integration ID --answer on|off Bring a WhatsApp Business number from Formmy/EasyBits to this agent, or send it back [--forward] [--ads-only on|off] [--new-only on|off] [--board NAME|agent] [--dry-run]
17068
+ channels whatsapp contacts <id> --integration ID New contacts only (the agent skips the people you already talk to) and contacts without the agent [--new-only on|off] [--mute PHONE] [--allow PHONE] [--unmute PHONE] [--seed]
17046
17069
  channels whatsapp history <id> --integration ID [--days 7] Bring its recent cards, messages, orders and files from Formmy (no duplicates; Formmy keeps files 7 days) [--media] [--dry-run [--list]] [--only-ads] [--phones a,b] [--column NAME]
17047
17070
  channels wa import <id> --from easybits:ID | --from-file F | --from-dir DIR --own-number yes|no Move a personal WhatsApp session here, no QR [--dry-run] [--replace] [--no-connect]
17048
17071
  channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
@@ -17261,6 +17284,7 @@ ${bold("Configurar un agente")}
17261
17284
  channels messenger on|off <id> <p\xE1gina> Que este agente conteste una p\xE1gina, o que deje de hacerlo (off sigue registrando en el tablero) [--take --dry-run --yes]
17262
17285
  channels mteams ls|connect|zip|on|off|rm <id> [bot] Bot de Microsoft Teams (Azure Bot en el tenant del cliente) [--app-id --tenant --secret|GHOSTY_MTEAMS_SECRET --name --out]
17263
17286
  channels whatsapp link|unlink <id> --integration ID --answer on|off Trae un n\xFAmero de WhatsApp Business de Formmy/EasyBits a este agente, o devu\xE9lvelo [--forward] [--ads-only on|off] [--new-only on|off] [--board NOMBRE|agent] [--dry-run]
17287
+ channels whatsapp contacts <id> --integration ID S\xF3lo contactos nuevos (el agente no le contesta a tu gente de siempre) y contactos sin agente [--new-only on|off] [--mute TEL] [--allow TEL] [--unmute TEL] [--seed]
17264
17288
  channels whatsapp history <id> --integration ID [--days 7] Trae de Formmy sus tarjetas, mensajes, pedidos y archivos recientes (sin duplicar; Formmy guarda los archivos 7 d\xEDas) [--media] [--dry-run [--list]] [--only-ads] [--phones a,b] [--column NOMBRE]
17265
17289
  channels wa import <id> --from easybits:ID | --from-file F | --from-dir DIR --own-number yes|no Mueve aqu\xED una sesi\xF3n de WhatsApp personal, sin QR [--dry-run] [--replace] [--no-connect]
17266
17290
  channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
@@ -17644,6 +17668,10 @@ async function main() {
17644
17668
  draft: { type: "boolean" },
17645
17669
  "ads-only": { type: "string" },
17646
17670
  "new-only": { type: "string" },
17671
+ mute: { type: "string" },
17672
+ unmute: { type: "string" },
17673
+ allow: { type: "string" },
17674
+ seed: { type: "boolean" },
17647
17675
  "partner-tenant": { type: "string" },
17648
17676
  ref: { type: "string", multiple: true },
17649
17677
  "style-ref": { type: "string", multiple: true },
@@ -17686,7 +17714,7 @@ async function main() {
17686
17714
  const tokens = splitTokens(pos, v);
17687
17715
  globals.token = tokens.session;
17688
17716
  globals.profile = v.profile;
17689
- if (v.version) return out({ version: "0.88.0" }, () => "0.88.0");
17717
+ if (v.version) return out({ version: "0.90.0" }, () => "0.90.0");
17690
17718
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
17691
17719
  const context = (await readConfig()).workspace;
17692
17720
  if (!explicitToken()?.startsWith("gat_")) {
@@ -17889,7 +17917,7 @@ async function main() {
17889
17917
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
17890
17918
  }
17891
17919
  }
17892
- var notice = updateNotice("0.88.0").catch(() => null);
17920
+ var notice = updateNotice("0.90.0").catch(() => null);
17893
17921
  main().then(
17894
17922
  async () => {
17895
17923
  const n = process.stderr.isTTY ? await notice : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostystudio/cli",
3
- "version": "0.88.0",
3
+ "version": "0.90.0",
4
4
  "description": "Official Ghosty Studio CLI — configure, test and chat with your agents from the terminal. Built for humans and coding agents.",
5
5
  "type": "module",
6
6
  "bin": {