@ghostystudio/cli 0.103.0 → 0.104.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 +16 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1190,7 +1190,7 @@ async function postLong(path2, body, timeoutMs = 9e5) {
1190
1190
  const req = httpsRequest(`${base}${path2}`, {
1191
1191
  method: "POST",
1192
1192
  timeout: timeoutMs,
1193
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.103.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
1193
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.104.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
1194
1194
  }, (res) => {
1195
1195
  let buf = "";
1196
1196
  res.on("data", (c) => buf += c);
@@ -1232,7 +1232,7 @@ async function request(path2, opts = {}, retried = false) {
1232
1232
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
1233
1233
  headers: {
1234
1234
  authorization: `Bearer ${token}`,
1235
- "user-agent": `ghosty-cli/${"0.103.0"}`,
1235
+ "user-agent": `ghosty-cli/${"0.104.0"}`,
1236
1236
  "accept-language": lang(),
1237
1237
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
1238
1238
  ...opts.headers
@@ -11297,7 +11297,7 @@ async function call(path2, v, init = {}) {
11297
11297
  "x-ghosty-ts": ts,
11298
11298
  "x-ghosty-sig": sig,
11299
11299
  "content-type": "application/json",
11300
- "user-agent": `ghosty-cli/${"0.103.0"}`
11300
+ "user-agent": `ghosty-cli/${"0.104.0"}`
11301
11301
  },
11302
11302
  body: method === "GET" ? void 0 : raw
11303
11303
  });
@@ -13155,29 +13155,30 @@ ${t(`Cards appear on the team's board (no "new lead" alerts).`, "Las tarjetas ap
13155
13155
  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)}`);
13156
13156
  }
13157
13157
  async function contactsCmd(id, v) {
13158
- 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] [--signature \u{1F47E}|off] [--seed] [--list] [--q TEXT]");
13158
+ 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] [--team PHONE] [--unmute PHONE] [--signature \u{1F47E}|off] [--seed] [--list] [--q TEXT]");
13159
13159
  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."));
13160
- 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."));
13160
+ if ([v.mute, v.unmute, v.allow, v.team].filter((x) => x !== void 0).length > 1) throw usage(t("Use only one of --mute, --allow, --team or --unmute.", "Usa s\xF3lo uno de --mute, --allow, --team o --unmute."));
13161
13161
  const path2 = `/api/v2/agents/${enc11(id)}/whatsapp/contacts`;
13162
13162
  if (v.list || v.q !== void 0) {
13163
13163
  const l = await api(`${path2}?number=${enc11(v.integration)}&list=1&limit=200${v.q ? `&q=${enc11(v.q)}` : ""}`);
13164
13164
  return out(l, () => {
13165
13165
  if (!l.rows.length) return dim(t("No contacts yet (Meta sends the phone contacts within 24 h of connecting).", "Sin contactos todav\xEDa (Meta manda la agenda del tel\xE9fono en las primeras 24 h)."));
13166
- const rule = (r2) => r2.agentOff ? t("never", "nunca") : r2.agentAlways ? t("always", "siempre") : "";
13166
+ const rule = (r2) => r2.agentOff ? t("never", "nunca") : r2.team ? t("team", "equipo") : r2.agentAlways ? t("always", "siempre") : "";
13167
13167
  const lines = l.rows.map((r2) => `${r2.key.padEnd(12)} ${r2.name ?? dim(t("(no name)", "(sin nombre)"))}${rule(r2) ? ` ${lavender(rule(r2))}` : ""}`);
13168
13168
  if (l.next != null) lines.push(dim(t(`\u2026 ${l.counts.all - l.rows.length} more; narrow it with --q`, `\u2026 ${l.counts.all - l.rows.length} m\xE1s; acota con --q`)));
13169
13169
  return lines.join("\n");
13170
13170
  });
13171
13171
  }
13172
- const contact = v.mute ?? v.allow ?? v.unmute;
13172
+ const contact = v.mute ?? v.allow ?? v.team ?? v.unmute;
13173
13173
  const write = v["new-only"] !== void 0 || contact !== void 0 || v.seed || v.signature !== void 0;
13174
- 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 } : {}, ...v.signature !== void 0 ? { signature: v.signature === "off" ? null : v.signature } : {} } }) : await api(`${path2}?number=${enc11(v.integration)}`);
13174
+ 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 } : v.team !== void 0 ? { contact, team: true } : { contact, off: !!v.mute } : {}, ...v.seed ? { seed: true } : {}, ...v.signature !== void 0 ? { signature: v.signature === "off" ? null : v.signature } : {} } }) : await api(`${path2}?number=${enc11(v.integration)}`);
13175
13175
  return out(r, () => {
13176
13176
  const lines = [
13177
13177
  `${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})`) : ""}`,
13178
13178
  `${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)"))}`,
13179
13179
  `${t("Signature", "Firma")}: ${r.signature ? `${r.signature}:` : dim(t("none", "sin firma"))}`,
13180
13180
  `${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")}`,
13181
+ `${t("Team (answers in team mode)", "Equipo (le contesta en modo equipo)")}: ${r.team?.length ? r.team.map((o) => o.name ? `${o.name} ${o.key}` : o.key).join(", ") : dim("\u2014")}`,
13181
13182
  `${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")}`
13182
13183
  ];
13183
13184
  return lines.join("\n");
@@ -13878,7 +13879,7 @@ var HELPS_ES = {
13878
13879
  ["--cached", "groups ls: no le pide a WhatsApp la lista en vivo (m\xE1s r\xE1pido; puede faltar alg\xFAn grupo nuevo)"],
13879
13880
  ["--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)"],
13880
13881
  ["--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)"],
13881
- ["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), --signature \u{1F47E}|off (los mensajes del agente empiezan con \xAB\u{1F47E}:\xBB para que el cliente lo distinga de ti), --seed (vuelve a traer los contactos conocidos de WhatsApp), --list [--q TEXTO] (la agenda del tel\xE9fono con la regla de cada contacto; --q busca por nombre o n\xFAmero)"],
13882
+ ["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), --team TEL (ese n\xFAmero es de tu equipo: el agente le contesta en modo equipo, sin guion de venta, sin tarjeta de lead y sin campa\xF1as), --unmute TEL (vuelve a la regla general), --signature \u{1F47E}|off (los mensajes del agente empiezan con \xAB\u{1F47E}:\xBB para que el cliente lo distinga de ti), --seed (vuelve a traer los contactos conocidos de WhatsApp), --list [--q TEXTO] (la agenda del tel\xE9fono con la regla de cada contacto; --q busca por nombre o n\xFAmero)"],
13882
13883
  ["--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"],
13883
13884
  ["--list", "history --dry-run: CU\xC1LES conversaciones (tel\xE9fono, nombre, primer mensaje, mensajes, si vino de anuncio)"],
13884
13885
  ["--only-ads / --phones a,b", "history: importa (o lista) s\xF3lo las de anuncio y/o esos tel\xE9fonos"],
@@ -14805,7 +14806,7 @@ HELPS.channels = {
14805
14806
  ["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
14806
14807
  ["--ads-only on|off", "link: only conversations that come from an ad open a card (also alone, to change it on a linked number)"],
14807
14808
  ["--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)"],
14808
- ["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), --signature \u{1F47E}|off (the agent's messages start with \u201C\u{1F47E}:\u201D so customers can tell it from you), --seed (bring the known contacts from WhatsApp again), --list [--q TEXT] (the phone contacts with their rule; --q searches by name or number)"],
14809
+ ["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), --team PHONE (that number is your team's: the agent answers it in team mode, with no sales script, no lead card and no campaigns), --unmute PHONE (back to the general rule), --signature \u{1F47E}|off (the agent's messages start with \u201C\u{1F47E}:\u201D so customers can tell it from you), --seed (bring the known contacts from WhatsApp again), --list [--q TEXT] (the phone contacts with their rule; --q searches by name or number)"],
14809
14810
  ["--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"],
14810
14811
  ["--list", "history --dry-run: WHICH conversations (phone, name, first message, messages, from an ad)"],
14811
14812
  ["--only-ads / --phones a,b", "history: import (or list) only the ones from an ad and/or those phones"],
@@ -17644,7 +17645,7 @@ ${bold("Configure an agent")}
17644
17645
  channels messenger on|off <id> <page> Answer a page with this agent, or stop (off keeps logging to the board) [--take --dry-run --yes]
17645
17646
  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]
17646
17647
  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]
17647
- 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] [--signature \u{1F47E}|off] [--seed] [--list] [--q TEXT]
17648
+ 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] [--team PHONE] [--unmute PHONE] [--signature \u{1F47E}|off] [--seed] [--list] [--q TEXT]
17648
17649
  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]
17649
17650
  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]
17650
17651
  channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
@@ -17878,7 +17879,7 @@ ${bold("Configurar un agente")}
17878
17879
  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]
17879
17880
  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]
17880
17881
  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]
17881
- 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] [--signature \u{1F47E}|off] [--seed] [--list] [--q TEXT]
17882
+ 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] [--team TEL] [--unmute TEL] [--signature \u{1F47E}|off] [--seed] [--list] [--q TEXT]
17882
17883
  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]
17883
17884
  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]
17884
17885
  channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
@@ -18281,6 +18282,7 @@ async function main() {
18281
18282
  mute: { type: "string" },
18282
18283
  unmute: { type: "string" },
18283
18284
  allow: { type: "string" },
18285
+ team: { type: "string" },
18284
18286
  signature: { type: "string" },
18285
18287
  seed: { type: "boolean" },
18286
18288
  "partner-tenant": { type: "string" },
@@ -18325,7 +18327,7 @@ async function main() {
18325
18327
  const tokens = splitTokens(pos, v);
18326
18328
  globals.token = tokens.session;
18327
18329
  globals.profile = v.profile;
18328
- if (v.version) return out({ version: "0.103.0" }, () => "0.103.0");
18330
+ if (v.version) return out({ version: "0.104.0" }, () => "0.104.0");
18329
18331
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
18330
18332
  const context = (await readConfig()).workspace;
18331
18333
  if (!explicitToken()?.startsWith("gat_")) {
@@ -18535,7 +18537,7 @@ async function main() {
18535
18537
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
18536
18538
  }
18537
18539
  }
18538
- var notice = updateNotice("0.103.0").catch(() => null);
18540
+ var notice = updateNotice("0.104.0").catch(() => null);
18539
18541
  main().then(
18540
18542
  async () => {
18541
18543
  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.103.0",
3
+ "version": "0.104.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": {