@ghostystudio/cli 0.57.1 → 0.58.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 (3) hide show
  1. package/README.md +6 -2
  2. package/dist/index.js +99 -35
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -84,7 +84,7 @@ ghosty schedule add|ls|rm <id> … --teams <workspace>#<room>[/<thread-id>] #
84
84
 
85
85
  ghosty me files ls|get|export|upload|rm [--q TEXT] [--kind image|video|audio|document|other] [--cursor C]
86
86
  ghosty me memories ls|add|edit|rm
87
- ghosty conversations archive|rm <id> --kind test|channel [--before 7d] [--dry-run]
87
+ ghosty conversations archive|rm <id> --kind test|channel|personal [--before 7d] [--dry-run]
88
88
  ghosty agents docs dedupe [--dry-run] # your account: merge documents with the same content
89
89
  # Ghosty staff
90
90
  ghosty conversations ls --user EMAIL [--since 30d] · conversations show <id> <conv> --why|--tools
@@ -109,6 +109,10 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
109
109
 
110
110
  Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
111
111
 
112
+ ### Changes in 0.58.1
113
+
114
+ - `agents docs add|replace --file` sends the file type from its extension. Before, the server stored every CLI upload as `application/octet-stream`, and a PDF reached WhatsApp customers as `.bin`.
115
+
112
116
  ### Changes in 0.57.1 (0.57.0 + the 0.56.1 factory fix)
113
117
 
114
118
  - `sites pin <slug>` / `sites unpin <slug>` / `sites pins [--cancel|--resume]`: «Pinned sites» — $49 USD/yr or $5 USD/mo ($990 / $99 MXN), includes up to 3 sites per pack; a pinned site never pauses and has no banner. `--interval year|month`, `--currency usd|mxn`, `--add-pack`.
@@ -136,7 +140,7 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
136
140
  ### Changes in 0.52.0
137
141
 
138
142
  - `agents embed <id>` customizes the web widget: `--title`, `--subtitle`, `--avatar URL|none`, `--icon chat|avatar|ghost`, `--placeholder`, `--position right|left`, `--offset X[,Y]`, `--theme light|dark|auto`, `--bubble-text`, `--auto-open S|off`, `--starters "a|b"` (up to 4 of yours; Ghosty's never show). The pasted `<script>` doesn't change.
139
- - Fine style without free CSS: `--style '{json}'|none` (fontFamily from a Google Fonts allow-list, radius 0–24, header/bubble/input/send/launcher colors, titleUppercase, statusDot, sendArrow), shortcuts `--font` and `--radius`.
143
+ - Fine style without free CSS: `--style '{json}'|none` (fontFamily from a Google Fonts allow-list, radius 0–24, header/bubble/input/send/launcher colors, titleUppercase, statusDot, sendArrow, typing, typingColor), shortcuts `--font`, `--radius`, `--typing bounce|flow|flashing|shimmer` and `--typing-color`.
140
144
  - Theme source: `--theme-source teams` (your Ghosty Teams workspace brand, `--workspace SLUG`) or `--theme-source site --site-url URL` (your site's CSS colors), imported as editable values. `agents embed <id> theme` exports it as JSON; `--style-file theme.json` applies it back.
141
145
 
142
146
  ### Changes in 0.51.0
package/dist/index.js CHANGED
@@ -7863,11 +7863,11 @@ var SERVER_MESSAGES = [
7863
7863
  ["la caja ACP {} arranc\xF3 en error: {}", "ACP box {} started in error: {}"],
7864
7864
  ["la caja ACP {} no lleg\xF3 a running en 90s", "ACP box {} didn't reach running in 90s"],
7865
7865
  ["la caja {} no lleg\xF3 a running (\xFAltimo estado: {})", "box {} didn't reach running (last state: {})"],
7866
+ ["la caja {} no arranc\xF3: {}", "box {} failed to boot: {}"],
7866
7867
  ["la caja del agente naci\xF3 muda (no arranc\xF3 {}-runtime): {}", "the agent's box came up mute ({}-runtime didn't start): {}"],
7867
7868
  ["no se pudo re-rutear tras caja muerta", "couldn't re-route after a dead box"],
7868
7869
  ["fleet route race sin ganador", "fleet route race with no winner"],
7869
7870
  ["create: respuesta sin sandboxId", "create: response without sandboxId"],
7870
- ["warmpool.claim: respuesta sin sandboxId", "warmpool.claim: response without sandboxId"],
7871
7871
  ["expose: respuesta sin url", "expose: response without url"],
7872
7872
  ["expose-raw: respuesta sin hostPort", "expose-raw: response without hostPort"],
7873
7873
  // — Cajas compartidas: voz, render, video, ortografía, eventos
@@ -8468,7 +8468,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
8468
8468
  const req = httpsRequest(`${base}${path}`, {
8469
8469
  method: "POST",
8470
8470
  timeout: timeoutMs,
8471
- headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.57.1"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8471
+ headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.58.1"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
8472
8472
  }, (res) => {
8473
8473
  let buf = "";
8474
8474
  res.on("data", (c) => buf += c);
@@ -8510,7 +8510,7 @@ async function request(path, opts = {}, retried = false) {
8510
8510
  method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
8511
8511
  headers: {
8512
8512
  authorization: `Bearer ${token}`,
8513
- "user-agent": `ghosty-cli/${"0.57.1"}`,
8513
+ "user-agent": `ghosty-cli/${"0.58.1"}`,
8514
8514
  "accept-language": lang(),
8515
8515
  ...opts.body !== void 0 ? { "content-type": "application/json" } : {},
8516
8516
  ...opts.headers
@@ -9095,7 +9095,7 @@ ${dim(t(`only ${workspace} (ghosty use) \xB7 all: --all`, `s\xF3lo ${workspace}
9095
9095
  function parseEnv(raw) {
9096
9096
  if (!raw) return void 0;
9097
9097
  const env = {};
9098
- for (const pair of raw.split(",")) {
9098
+ for (const pair of raw.split(/,(?=[A-Za-z_][A-Za-z0-9_]*=)/)) {
9099
9099
  const i = pair.indexOf("=");
9100
9100
  if (i < 1) throw usage(t(`Bad --env entry: ${pair}.`, `Entrada de --env inv\xE1lida: ${pair}.`), t("Example", "Ejemplo") + ": --env GHOSTY_THINKING_EFFORT=max");
9101
9101
  env[pair.slice(0, i).trim()] = pair.slice(i + 1);
@@ -9731,10 +9731,12 @@ async function agentsEmbed(id, v, action) {
9731
9731
  if (body.themeSource === void 0) body.themeSource = "site";
9732
9732
  }
9733
9733
  if (body.themeSource === "teams" && v.workspace) body.teamsWorkspace = v.workspace;
9734
- if (v.font !== void 0 || v.radius !== void 0) {
9734
+ if (v.font !== void 0 || v.radius !== void 0 || v.typing !== void 0 || v["typing-color"] !== void 0) {
9735
9735
  const st = body.style && typeof body.style === "object" ? body.style : {};
9736
9736
  if (v.font !== void 0) st.fontFamily = clear(v.font);
9737
9737
  if (v.radius !== void 0) st.radius = v.radius === "" || v.radius === "none" ? null : Number(v.radius);
9738
+ if (v.typing !== void 0) st.typing = clear(v.typing);
9739
+ if (v["typing-color"] !== void 0) st.typingColor = clear(v["typing-color"]);
9738
9740
  body.style = st;
9739
9741
  }
9740
9742
  if (v.rotate) body.rotate = true;
@@ -10303,6 +10305,7 @@ ${bold("Partner space")} ${dim("(you, signed in as the space owner)")}
10303
10305
  partner credentials rotate <keyId> [--grace MIN] New secret, same keyId (old one valid MIN, default 60)
10304
10306
  partner credentials revoke <keyId> Revoke it (idempotent)
10305
10307
  partner audit Who did what, when
10308
+ partner usage [--tenant ID] [--from YYYY-MM-DD] [--to YYYY-MM-DD] Turns, tokens, cost and errors per business (30 days by default)
10306
10309
 
10307
10310
  ${bold("Shared agents (solo-MCP)")} ${dim("(--agent SLUG picks another agent of the partner: nik-admin, nik-public\u2026)")}
10308
10311
  partner agent get [--agent SLUG] The agent template and its version
@@ -10348,6 +10351,7 @@ ${bold("Espacio de partner")} ${dim("(t\xFA, con sesi\xF3n como due\xF1a del es
10348
10351
  partner credentials rotate <keyId> [--grace MIN] Secreto nuevo, mismo keyId (el viejo vale MIN, default 60)
10349
10352
  partner credentials revoke <keyId> La revoca (idempotente)
10350
10353
  partner audit Qui\xE9n hizo qu\xE9 y cu\xE1ndo
10354
+ partner usage [--tenant ID] [--from AAAA-MM-DD] [--to AAAA-MM-DD] Turnos, tokens, costo y fallas por negocio (30 d\xEDas por default)
10351
10355
 
10352
10356
  ${bold("Agentes compartidos (solo-MCP)")} ${dim("(--agent SLUG elige otro agente del partner: nik-admin, nik-public\u2026)")}
10353
10357
  partner agent get [--agent SLUG] La plantilla del agente y su versi\xF3n
@@ -10404,7 +10408,7 @@ async function call(path, v, init = {}) {
10404
10408
  "x-ghosty-ts": ts,
10405
10409
  "x-ghosty-sig": sig,
10406
10410
  "content-type": "application/json",
10407
- "user-agent": `ghosty-cli/${"0.57.1"}`
10411
+ "user-agent": `ghosty-cli/${"0.58.1"}`
10408
10412
  },
10409
10413
  body: method === "GET" ? void 0 : raw
10410
10414
  });
@@ -10647,6 +10651,29 @@ async function partnerCmd(sub, a1, a2, v, a3) {
10647
10651
  const r = await json("/api/v2/partner/audit", v, { ownerOnly: true });
10648
10652
  return out(r, () => table(r.events.map((e) => ({ when: fmt(e.ts), action: e.action, externalId: e.externalId, actor: e.actor })), ["when", "action", "externalId", "actor"]));
10649
10653
  }
10654
+ case "usage": {
10655
+ const q = new URLSearchParams();
10656
+ if (v.tenant) q.set("externalId", v.tenant);
10657
+ if (v.from) q.set("from", v.from);
10658
+ if (v.to) q.set("to", v.to);
10659
+ const r = await json(
10660
+ `/api/v2/partner/usage${q.size ? `?${q}` : ""}`,
10661
+ v
10662
+ );
10663
+ return out(
10664
+ r,
10665
+ () => r.tenants.length ? table(
10666
+ r.tenants.map((x) => ({
10667
+ tenant: x.name ? `${x.name} (${x.externalId})` : x.externalId,
10668
+ turns: x.turns,
10669
+ tokens: x.tokens,
10670
+ usd: x.costUsd.toFixed(4),
10671
+ errors: Object.entries(x.errors).map(([k, n]) => `${k}\xD7${n}`).join(" ") || "\u2014"
10672
+ })),
10673
+ ["tenant", "turns", "tokens", "usd", "errors"]
10674
+ ) : dim(t("No usage in that range.", "Sin consumo en ese rango."))
10675
+ );
10676
+ }
10650
10677
  case "try":
10651
10678
  return tryCmd(a1, a2, v);
10652
10679
  case "wa":
@@ -10984,7 +11011,7 @@ async function agentDocsCmd(sub, id, docs, v) {
10984
11011
  if (!v.url === !v.file) throw usage(t("Use --url URL or --file PATH (one of them).", "Usa --url URL o --file RUTA (uno de los dos)."), t("Example", "Ejemplo") + ': ghosty agents docs add <agent-id> --url https://\u2026/lista.pdf --description "Precios 2026"');
10985
11012
  if (v.grant && !["read", "write", "no"].includes(v.grant)) throw usage(t("--grant takes read, write or no.", "--grant acepta read, write o no."));
10986
11013
  const body = {
10987
- ...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64"), name: v.name ?? basename3(v.file) },
11014
+ ...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64"), name: v.name ?? basename3(v.file), ...mimeField(v.file) },
10988
11015
  ...v.url && v.name ? { name: v.name } : {},
10989
11016
  ...v.description ? { description: v.description } : {},
10990
11017
  grant: v.grant === "no" ? false : v.grant === "write" || v.write ? "write" : "read",
@@ -11003,7 +11030,7 @@ async function agentDocsCmd(sub, id, docs, v) {
11003
11030
  doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), ex);
11004
11031
  if (!v.url === !v.file) throw usage(t("Use --url URL or --file PATH (one of them).", "Usa --url URL o --file RUTA (uno de los dos)."), t("Example", "Ejemplo") + ": " + ex);
11005
11032
  const body = {
11006
- ...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64") },
11033
+ ...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64"), ...mimeField(v.file) },
11007
11034
  ...v.note ? { note: v.note } : {}
11008
11035
  };
11009
11036
  const r = await api(agentPath2(id, `/documents/${enc6(doc)}/versions`), { method: "POST", body });
@@ -11119,6 +11146,33 @@ async function turnsGrep(id, text, v) {
11119
11146
  out(r, () => rows.length ? `${table(rows, ["at", "who", "turn", "state", "session", "text"])}
11120
11147
  ${dim(`${t("open one", "abre uno")}: ghosty turns show ${id} <turn>`)}` : dim(t(`Nothing says \xAB${text}\xBB${v.since ? ` since ${v.since}` : ""}.`, `Nada dice \xAB${text}\xBB${v.since ? ` desde ${v.since}` : ""}.`)));
11121
11148
  }
11149
+ function mimeField(path) {
11150
+ const types = {
11151
+ pdf: "application/pdf",
11152
+ png: "image/png",
11153
+ jpg: "image/jpeg",
11154
+ jpeg: "image/jpeg",
11155
+ gif: "image/gif",
11156
+ webp: "image/webp",
11157
+ mp3: "audio/mpeg",
11158
+ ogg: "audio/ogg",
11159
+ wav: "audio/wav",
11160
+ m4a: "audio/mp4",
11161
+ mp4: "video/mp4",
11162
+ mov: "video/quicktime",
11163
+ csv: "text/csv",
11164
+ md: "text/markdown",
11165
+ txt: "text/plain",
11166
+ html: "text/html",
11167
+ json: "application/json",
11168
+ zip: "application/zip",
11169
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
11170
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
11171
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
11172
+ };
11173
+ const mime = types[path.split(".").pop()?.toLowerCase() ?? ""];
11174
+ return mime ? { mime } : {};
11175
+ }
11122
11176
  async function turnShow(id, turnId, full = false) {
11123
11177
  id = need2(id, t("agent id", "el id del agente"), "ghosty turns show <agent-id> [turn-id|last] [--full]");
11124
11178
  const r = await api(agentPath2(id, `/turns/${enc6(turnId ?? "last")}`));
@@ -11487,9 +11541,10 @@ async function channelsCmd(kind, sub, id, ref, v) {
11487
11541
  answers: n2.mine ? lavender(t("this agent", "este agente")) : n2.recordsOnly ? t("this agent (records only)", "este agente (s\xF3lo registra)") : n2.takenByOther ? t("another agent", "otro agente") : dim(t("nobody (silent)", "nadie (en silencio)")),
11488
11542
  "formmy forwards": fwd(n2),
11489
11543
  "ads only": n2.adsOnly ? lavender(t("yes", "s\xED")) : "",
11544
+ board: n2.board ? n2.board.name : dim(t("the agent's", "el del agente")),
11490
11545
  id: n2.id
11491
11546
  }));
11492
- return out(cur, () => rows.length ? table(rows, ["number", "answers", "formmy forwards", "ads only", "id"]) : dim(t(`No WhatsApp Business numbers yet. Pair one at ${cur.salesUrl}`, `Todav\xEDa no hay n\xFAmeros de WhatsApp Business. Parea uno en ${cur.salesUrl}`)));
11547
+ return out(cur, () => rows.length ? table(rows, ["number", "answers", "formmy forwards", "ads only", "board", "id"]) : dim(t(`No WhatsApp Business numbers yet. Pair one at ${cur.salesUrl}`, `Todav\xEDa no hay n\xFAmeros de WhatsApp Business. Parea uno en ${cur.salesUrl}`)));
11493
11548
  }
11494
11549
  if (sub !== "enable" && sub !== "disable") throw usage(t(`Unknown subcommand: ${sub}.`, `Subcomando desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty channels whatsapp ls | enable | disable | integrations | link | unlink | history \xB7 ghosty channels wa import | status | pause | groups");
11495
11550
  if (!ref) throw usage(t("Missing number.", "Falta el n\xFAmero."), t("Example", "Ejemplo") + `: ghosty channels whatsapp ${sub} ${id} +52 55 1234 5678`);
@@ -11652,19 +11707,21 @@ async function linkCmd(sub, id, v) {
11652
11707
  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 ${sub} <agent-id> --integration <formmy-integration-id> --answer off --dry-run`);
11653
11708
  if (v["ads-only"] !== void 0 && v["ads-only"] !== "on" && v["ads-only"] !== "off") throw usage(t("--ads-only takes on or off.", "--ads-only acepta on u off."));
11654
11709
  const adsOnly = v["ads-only"] === void 0 ? void 0 : v["ads-only"] === "on";
11655
- if (sub === "link" && adsOnly !== void 0 && v.answer === void 0) {
11710
+ const board = v.board === void 0 ? void 0 : /^(agent|agente)$/i.test(v.board) ? null : v.board;
11711
+ const boardFlag = v.board !== void 0 ? ` --board ${JSON.stringify(v.board)}` : "";
11712
+ if (sub === "link" && (adsOnly !== void 0 || board !== void 0) && v.answer === void 0) {
11656
11713
  const path2 = `/api/v2/agents/${enc7(id)}/whatsapp/link`;
11657
- const r2 = await api(path2, { method: "POST", body: { integrationId: v.integration, adsOnly, ...v["dry-run"] ? { dryRun: true } : {} } });
11714
+ const r2 = await api(path2, { method: "POST", body: { integrationId: v.integration, ...adsOnly !== void 0 ? { adsOnly } : {}, ...board !== void 0 ? { board } : {}, ...v["dry-run"] ? { dryRun: true } : {} } });
11658
11715
  return out(r2, () => `${r2.plan.map((x) => serverText(x)).join("\n")}${v["dry-run"] ? `
11659
- ${dim(applyHint(`ghosty channels whatsapp link ${id} --integration ${v.integration} --ads-only ${v["ads-only"]}`))}` : ""}`);
11716
+ ${dim(applyHint(`ghosty channels whatsapp link ${id} --integration ${v.integration}${v["ads-only"] ? ` --ads-only ${v["ads-only"]}` : ""}${boardFlag}`))}` : ""}`);
11660
11717
  }
11661
11718
  if (sub === "link" && v.answer !== "on" && v.answer !== "off") throw usage(t("Missing --answer on|off.", "Falta --answer on|off."), t("on = this agent answers real customers \xB7 off = it only records (add --forward so gs still sees the conversations)", "on = este agente contesta a clientes reales \xB7 off = s\xF3lo registra (agrega --forward para que gs siga viendo las conversaciones)"));
11662
11719
  const path = `/api/v2/agents/${enc7(id)}/whatsapp/link`;
11663
11720
  if (v["partner-agent"] && !v["partner-tenant"]) throw usage(t("--partner-agent needs --partner-tenant <externalId>.", "--partner-agent necesita --partner-tenant <externalId>."));
11664
11721
  const partner = v["partner-tenant"] ? { partnerExternalId: v["partner-tenant"], ...v["partner-agent"] ? { partnerAgentSlug: v["partner-agent"] } : {} } : {};
11665
- const body = { integrationId: v.integration, ...sub === "unlink" ? { unlink: true } : { answer: v.answer === "on", ...v.forward ? { forward: true } : {}, ...adsOnly !== void 0 ? { adsOnly } : {}, ...partner } };
11722
+ const body = { integrationId: v.integration, ...sub === "unlink" ? { unlink: true } : { answer: v.answer === "on", ...v.forward ? { forward: true } : {}, ...adsOnly !== void 0 ? { adsOnly } : {}, ...board !== void 0 ? { board } : {}, ...partner } };
11666
11723
  const partnerFlags = v["partner-tenant"] ? ` --partner-tenant ${v["partner-tenant"]}${v["partner-agent"] ? ` --partner-agent ${v["partner-agent"]}` : ""}` : "";
11667
- const cmd = `ghosty channels whatsapp ${sub} ${id} --integration ${v.integration}${sub === "link" ? ` --answer ${v.answer}${v.forward ? " --forward" : ""}${v["ads-only"] ? ` --ads-only ${v["ads-only"]}` : ""}${partnerFlags}` : ""}`;
11724
+ const cmd = `ghosty channels whatsapp ${sub} ${id} --integration ${v.integration}${sub === "link" ? ` --answer ${v.answer}${v.forward ? " --forward" : ""}${v["ads-only"] ? ` --ads-only ${v["ads-only"]}` : ""}${boardFlag}${partnerFlags}` : ""}`;
11668
11725
  const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
11669
11726
  const text = `${plan.plan.map((x) => serverText(x)).join("\n")}
11670
11727
  ${dim(`Formmy: ${plan.formmy.before.url ?? "null"} \u2192 ${plan.formmy.after.url ?? "null"} (${t("as", "como")} ${plan.formmy.by})`)}`;
@@ -12039,7 +12096,7 @@ var HELPS_ES = {
12039
12096
  ["--engine E --dry-run", "set: qu\xE9 hace cambiar de motor (sesiones que se olvidan, caja que se levanta y cu\xE1l se destruye, si est\xE1 trabajando) sin cambiarlo; cada hilo conserva su conversaci\xF3n"],
12040
12097
  ["--toolsets LIST", "set: grupos de tools de gs que recibe (crm, sales\u2026): una lista reemplaza; +x / -x agrega o quita"],
12041
12098
  ["--prompt-base ID", "set: el prompt de este agente = el prompt de ese agente + --prompt-overlay; se regenera cuando cambia la base. none = desliga (conserva el prompt de hoy)"],
12042
- ["--prompt-overlay FILE", 'set: JSON {"edits":[{"what","find","replace","regex"?,"all"?}]}, aplicados en orden; cada uno debe encontrar su lugar o no cambia nada'],
12099
+ ["--prompt-overlay FILE", 'set: JSON {"edits":[{"what","find","replace","regex"?,"all"?}]}, aplicados en orden; cada uno debe encontrar su lugar o no cambia nada. {"edits":[]} = espejo exacto: un solo prompt y una sola memoria para los dos'],
12043
12100
  ["--prompt-patch FILE", 'set: el mismo formato {"edits":[\u2026]}, aplicado UNA vez sobre el prompt actual del agente en el servidor (no sobre una copia local: nunca pisa el cambio de alguien m\xE1s). Si una edici\xF3n no encuentra su lugar, no cambia nada. --dry-run imprime el diff'],
12044
12101
  ["--prompt-append FILE", "set: agrega ese texto al final del prompt (con --prompt-patch tambi\xE9n: primero las ediciones, luego el texto)"],
12045
12102
  ["--voice E:ID", "set: voz de las notas de voz: elevenlabs:<voiceId> o kokoro:em_santa|em_alex|ef_dora"],
@@ -12079,8 +12136,9 @@ var HELPS_ES = {
12079
12136
  ["--bubble-text T", "embed: etiqueta junto al bot\xF3n (\xAB\xBFDudas?\xBB)"],
12080
12137
  ["--auto-open S|off", "embed: se abre solo a los S segundos (1-120), una vez por visita, nunca en el tel\xE9fono"],
12081
12138
  ['--starters "a|b|c"', 'embed: hasta 4 sugerencias TUYAS ("" las quita; nunca salen las de Ghosty)'],
12082
- ["--style '{json}'|none", "embed: estilo fino, s\xF3lo llaves conocidas: fontFamily, radius, headerBg, headerText, titleUppercase, statusDot, headerAvatar, panelBg, userBubbleBg/Text, agentBubbleBg/Text, inputBg, sendBg, sendIcon, sendArrow (up|right), launcherBg, launcherIcon. PATCH por llave (null resetea una); none resetea todo"],
12139
+ ["--style '{json}'|none", "embed: estilo fino, s\xF3lo llaves conocidas: fontFamily, radius, headerBg, headerText, titleUppercase, statusDot, headerAvatar, panelBg, userBubbleBg/Text, agentBubbleBg/Text, inputBg, sendBg, sendIcon, sendArrow (up|right), launcherBg, launcherIcon, typing (bounce|flow|flashing|shimmer), typingColor. PATCH por llave (null resetea una); none resetea todo"],
12083
12140
  ["--font NAME / --radius 0-24", "embed: atajos de style.fontFamily (lista de Google Fonts: Barlow, Inter, Poppins\u2026) y style.radius"],
12141
+ ["--typing STYLE / --typing-color #rrggbb|none", "embed: el indicador de \xABescribiendo\xBB mientras responde: bounce (el de siempre), flow, flashing o shimmer (\xABPensando\u2026\xBB con brillo), y su color (por defecto, el de la marca)"],
12084
12142
  ["--theme-source custom|teams|site", "embed: importa el tema de la marca de tu espacio de Ghosty Teams (--workspace SLUG para elegir) o de tu sitio (--site-url); queda editable"],
12085
12143
  ["--site-url URL", "embed: el sitio del que se sacan los colores (implica --theme-source site)"],
12086
12144
  ["--style-file FILE", "embed: aplica un tema exportado con `agents embed <id> theme` (o s\xF3lo el objeto style)"],
@@ -12089,7 +12147,7 @@ var HELPS_ES = {
12089
12147
  ["--out FILE.tsx|.jsx [--force]", "embed: escribe en tu proyecto un componente de React COMPLETO y editable (sin depender de ning\xFAn paquete) con tu token y tu tema horneados arriba; no pisa un archivo que ya existe sin --force"],
12090
12148
  ["--check PATH", "box: este archivo debe existir en la m\xE1quina (repetible), p. ej. un archivo que trae la imagen nueva"],
12091
12149
  ["--contains PATH=TEXT", "box: ese archivo debe contener TEXT (un cambio que no agrega archivos)"],
12092
- ["--recycle", "box: reemplaza sus m\xE1quinas (nunca a media respuesta); pool: la nueva nace en el siguiente turno; ACP (goose, lite): su caja se recrea ya con la imagen actual"],
12150
+ ["--recycle", "box: reemplaza sus m\xE1quinas (nunca a media respuesta); pool: la nueva nace en el siguiente turno; ACP (goose, lite) igual, con el historial del \xFAltimo respaldo"],
12093
12151
  ["--reenv / --force", "box --recycle en ACP: --reenv s\xF3lo re-hornea el env en la misma caja; --force la recrea aunque est\xE9 ocupada (due\xF1o o staff)"],
12094
12152
  ["--unset-env K1,K2", "set: borra esas variables del env (--env K= tambi\xE9n borra K)"],
12095
12153
  ["--tool [ch:]tool=on|off", "set: prende/apaga una tool en un canal (wa:actualizar_identidad=off) o en todos (crm_buscar=off); separadas por comas"],
@@ -12205,7 +12263,7 @@ var HELPS_ES = {
12205
12263
  flags: [
12206
12264
  ["--media FILE", "chat: adjunta un archivo local (se repite)"],
12207
12265
  ["--file ID", "chat: adjunta de tus archivos (ghosty me files ls --q \u2026), separados por comas"],
12208
- ["--kind K", "archive|rm: test (pruebas) o channel (clientes: s\xF3lo archivar, su memoria los sigue atendiendo)"],
12266
+ ["--kind K", "archive|rm: test (pruebas), channel (clientes: s\xF3lo archivar, su memoria los sigue atendiendo) o personal (s\xF3lo TUS chats con ese agente; tambi\xE9n se van de tus apps)"],
12209
12267
  ["--before T", "archive|rm: s\xF3lo conversaciones sin mensajes desde T (7d, 12h o ISO)"],
12210
12268
  ["--user EMAIL", "ls/show (staff de Ghosty): las conversaciones de esa persona en cualquier agente; queda en bit\xE1cora"],
12211
12269
  ["--why", "show (staff de Ghosty): clase, de qui\xE9n es, qu\xE9 turno la fij\xF3, y si el due\xF1o / su usuario la ven en su lista o por liga"],
@@ -12265,6 +12323,7 @@ var HELPS_ES = {
12265
12323
  ["--text T / --sender N", "groups inject (staff): un mensaje que se perdi\xF3 (grupo apagado, deploy) entra como si N lo hubiera escrito ah\xED, y el agente contesta en el grupo"],
12266
12324
  ["--cached", "groups ls: no le pide a WhatsApp la lista en vivo (m\xE1s r\xE1pido; puede faltar alg\xFAn grupo nuevo)"],
12267
12325
  ["--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)"],
12326
+ ["--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"],
12268
12327
  ["--list", "history --dry-run: CU\xC1LES conversaciones (tel\xE9fono, nombre, primer mensaje, mensajes, si vino de anuncio)"],
12269
12328
  ["--only-ads / --phones a,b", "history: importa (o lista) s\xF3lo las de anuncio y/o esos tel\xE9fonos"],
12270
12329
  ["--column NAME", "history: las tarjetas nuevas nacen en esa columna en vez de la primera"],
@@ -12566,7 +12625,7 @@ var HELPS = {
12566
12625
  "ghosty agents embed enable|disable <agent-id> [--domains a.com,*.b.com] # web widget: chat bubble on your site, only on those domains",
12567
12626
  "ghosty agents embed <agent-id> [--domains \u2026] [--per-visitor N] [--per-ip N] [--welcome TEXT] [--color #hex] [--rotate] # state + the <script> to paste",
12568
12627
  'ghosty agents embed <agent-id> [--title T] [--subtitle T] [--avatar URL] [--icon chat|avatar|ghost] [--placeholder T] [--position right|left] [--offset X,Y] [--theme light|dark|auto] [--bubble-text T] [--auto-open S] [--starters "a|b"] # its look',
12569
- "ghosty agents embed <agent-id> [--theme-source custom|teams|site] [--site-url URL] [--style '{json}'] [--style-file theme.json] [--font NAME] [--radius 0-24] # its theme",
12628
+ "ghosty agents embed <agent-id> [--theme-source custom|teams|site] [--site-url URL] [--style '{json}'] [--style-file theme.json] [--font NAME] [--radius 0-24] [--typing flow] [--typing-color #rrggbb] # its theme",
12570
12629
  "ghosty agents embed <agent-id> [--copy '{json}'|none] [--copy-file texts.json] # what it says while it works",
12571
12630
  "ghosty agents embed <agent-id> theme # export the theme as JSON (edit it, apply it back with --style-file)",
12572
12631
  "ghosty agents embed <agent-id> --out src/components/GhostyChat.tsx [--force] # your own React component, token and theme baked in",
@@ -12584,7 +12643,7 @@ var HELPS = {
12584
12643
  ["--engine E --dry-run", "set: what changing the engine does (sessions forgotten, machine started and which one is destroyed, whether it is busy) without changing it; each conversation keeps its history"],
12585
12644
  ["--toolsets LIST", "set: gs tool groups it gets (crm, sales\u2026): a list replaces; +x / -x add or remove"],
12586
12645
  ["--prompt-base ID", "set: this agent's prompt = that agent's prompt + --prompt-overlay; regenerates when the base changes. none = unlink (keeps today's prompt)"],
12587
- ["--prompt-overlay FILE", 'set: JSON {"edits":[{"what","find","replace","regex"?,"all"?}]}, applied in order; each must find its place or nothing changes'],
12646
+ ["--prompt-overlay FILE", 'set: JSON {"edits":[{"what","find","replace","regex"?,"all"?}]}, applied in order; each must find its place or nothing changes. {"edits":[]} = an exact mirror: one prompt and one memory for both agents'],
12588
12647
  ["--prompt-patch FILE", `set: the same {"edits":[\u2026]} format, applied ONCE on the agent's current prompt on the server (not a local copy: it never overwrites someone else's change). If one edit doesn't find its place, nothing changes. --dry-run prints the diff`],
12589
12648
  ["--prompt-append FILE", "set: add that text at the end of the prompt (with --prompt-patch too: edits first, then the text)"],
12590
12649
  ["--voice E:ID", "set: voice for voice notes: elevenlabs:<voiceId> or kokoro:em_santa|em_alex|ef_dora"],
@@ -12624,8 +12683,9 @@ var HELPS = {
12624
12683
  ["--bubble-text T", 'embed: label next to the button ("Questions?")'],
12625
12684
  ["--auto-open S|off", "embed: open by itself after S seconds (1-120), once per visit, never on phones"],
12626
12685
  ['--starters "a|b|c"', `embed: up to 4 of YOUR suggestions ("" removes them; Ghosty's are never shown)`],
12627
- ["--style '{json}'|none", "embed: fine style, only known keys: fontFamily, radius, headerBg, headerText, titleUppercase, statusDot, headerAvatar, panelBg, userBubbleBg/Text, agentBubbleBg/Text, inputBg, sendBg, sendIcon, sendArrow (up|right), launcherBg, launcherIcon. Patch per key (null resets one); none resets all"],
12686
+ ["--style '{json}'|none", "embed: fine style, only known keys: fontFamily, radius, headerBg, headerText, titleUppercase, statusDot, headerAvatar, panelBg, userBubbleBg/Text, agentBubbleBg/Text, inputBg, sendBg, sendIcon, sendArrow (up|right), launcherBg, launcherIcon, typing (bounce|flow|flashing|shimmer), typingColor. Patch per key (null resets one); none resets all"],
12628
12687
  ["--font NAME / --radius 0-24", "embed: shortcuts for style.fontFamily (Google Fonts allow-list: Barlow, Inter, Poppins\u2026) and style.radius"],
12688
+ ["--typing STYLE / --typing-color #rrggbb|none", 'embed: the "typing" indicator while the agent replies: bounce (default), flow, flashing or shimmer (glowing "Thinking\u2026" text), and its color (default: the brand color)'],
12629
12689
  ["--theme-source custom|teams|site", "embed: import the theme from the brand of your Ghosty Teams workspace (--workspace SLUG to pick one) or from your site (--site-url); it stays editable"],
12630
12690
  ["--site-url URL", "embed: the site to take colors from (implies --theme-source site)"],
12631
12691
  ["--style-file FILE", "embed: apply a theme exported with `agents embed <id> theme` (or a plain style object)"],
@@ -12634,7 +12694,7 @@ var HELPS = {
12634
12694
  ["--out FILE.tsx|.jsx [--force]", "embed: write a COMPLETE, editable React component into your project (no package needed) with your token and theme baked in at the top; won't overwrite an existing file without --force"],
12635
12695
  ["--check PATH", "box: this file must exist in the machine (repeatable), e.g. a file the new image brings"],
12636
12696
  ["--contains PATH=TEXT", "box: that file must contain TEXT (a change that adds no files)"],
12637
- ["--recycle", "box: replace its machines (not mid-turn); pool: the new one starts on the next turn; ACP (goose, lite): its box is recreated now with the current image"],
12697
+ ["--recycle", "box: replace its machines (not mid-turn); pool: the new one starts on the next turn; ACP (goose, lite) too, history from its last backup"],
12638
12698
  ["--reenv / --force", "box --recycle on ACP: --reenv only re-bakes the env in the same box; --force recreates it even if busy (owner or staff)"],
12639
12699
  ["--unset-env K1,K2", "set: delete these env variables (--env K= also deletes K)"],
12640
12700
  ["--tool [ch:]tool=on|off", "set: one tool on/off in one channel (wa:actualizar_identidad=off) or everywhere (crm_buscar=off); comma-separated"],
@@ -12837,7 +12897,7 @@ var HELPS = {
12837
12897
  'ghosty chat <agent-id> ["message"] [--conversation ID] [--media FILE]\u2026 [--file ID[,ID\u2026]]',
12838
12898
  "ghosty conversations ls <agent-id>",
12839
12899
  "ghosty conversations show <agent-id> <conversation-id> [--limit 20]",
12840
- "ghosty conversations archive|rm <agent-id> --kind test|channel [--before 7d] [--dry-run] [--yes]",
12900
+ "ghosty conversations archive|rm <agent-id> --kind test|channel|personal [--before 7d] [--dry-run] [--yes]",
12841
12901
  "ghosty conversations ls --user <email> [--since 30d] [--surface android] # staff",
12842
12902
  "ghosty conversations show <agent-id> <conversation-id> --why # staff: who sees it and why",
12843
12903
  "ghosty conversations show <agent-id> <conversation-id> --tools # staff: what it ran and where it went"
@@ -12845,13 +12905,13 @@ var HELPS = {
12845
12905
  flags: [
12846
12906
  ["--media FILE", "chat: attach a local file (repeatable)"],
12847
12907
  ["--file ID", "chat: attach from your files (ghosty me files ls --q \u2026), comma-separated"],
12848
- ["--kind K", "archive|rm: test (test chats) or channel (customers: archive only, their memory keeps serving them)"],
12908
+ ["--kind K", "archive|rm: test (test chats), channel (customers: archive only, their memory keeps serving them) or personal (only YOUR own chats with that agent; also gone from your apps)"],
12849
12909
  ["--before T", "archive|rm: only conversations with no message since T (7d, 12h or ISO)"],
12850
12910
  ["--user EMAIL", "ls/show (Ghosty staff): that person's conversations in any agent; logged"],
12851
12911
  ["--why", "show (Ghosty staff): kind, whose it is, which turn set it, and whether the owner / its user see it in their list or by link"],
12852
12912
  ["--tools", "show (Ghosty staff): every tool the agent ran with its command/args (secrets masked) and the external domains it named; never while a turn runs in that thread"]
12853
12913
  ],
12854
- examples: ['ghosty chat <id> "hola"', "ghosty chat <id>", 'ghosty chat <id> "p\xE1salo a PDF" --media foto.jpg', "ghosty conversations rm <id> --kind test --before 7d --dry-run", "ghosty conversations archive <id> --kind channel --before 30d", "ghosty conversations ls --user david@empresa.com --since 7d"],
12914
+ examples: ['ghosty chat <id> "hola"', "ghosty chat <id>", 'ghosty chat <id> "p\xE1salo a PDF" --media foto.jpg', "ghosty conversations rm <id> --kind test --before 7d --dry-run", "ghosty conversations archive <id> --kind channel --before 30d", "ghosty conversations rm <id> --kind personal --dry-run", "ghosty conversations ls --user david@empresa.com --since 7d"],
12855
12915
  learn: `${REF}#conversaciones`
12856
12916
  },
12857
12917
  schedule: {
@@ -12947,6 +13007,7 @@ HELPS.channels = {
12947
13007
  ["--text T / --sender N", "groups inject (staff): a message that got lost (group off, deploy) enters as if N wrote it there, and the agent answers in the group"],
12948
13008
  ["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
12949
13009
  ["--ads-only on|off", "link: only conversations that come from an ad open a card (also alone, to change it on a linked number)"],
13010
+ ["--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"],
12950
13011
  ["--list", "history --dry-run: WHICH conversations (phone, name, first message, messages, from an ad)"],
12951
13012
  ["--only-ads / --phones a,b", "history: import (or list) only the ones from an ad and/or those phones"],
12952
13013
  ["--column NAME", "history: new cards are born in that column instead of the first one"],
@@ -14390,9 +14451,10 @@ async function supportThread(agent, sid, v) {
14390
14451
  });
14391
14452
  }
14392
14453
  async function cleanupConversations(action, agent, v) {
14393
- const ex = `ghosty conversations ${action} <agent> --kind ${action === "rm" ? "test" : "test|channel"} [--before 7d] [--dry-run]`;
14454
+ const ex = `ghosty conversations ${action} <agent> --kind ${action === "rm" ? "test|personal" : "test|channel|personal"} [--before 7d] [--dry-run]`;
14394
14455
  if (!agent) throw usage(t("Missing agent.", "Falta el agente."), ex);
14395
- if (v.kind !== "test" && v.kind !== "channel") throw usage(t("--kind takes test or channel.", "--kind acepta test o channel."), ex);
14456
+ if (v.kind !== "test" && v.kind !== "channel" && v.kind !== "personal") throw usage(t("--kind takes test, channel or personal.", "--kind acepta test, channel o personal."), ex);
14457
+ if (action === "rm" && v.kind === "channel") throw usage(t("Customer conversations (channel) are archived, never deleted: their memory keeps serving them.", "Los hilos de clientes (channel) se archivan, nunca se borran: su memoria los sigue atendiendo."), ex);
14396
14458
  const path = `/api/v2/agents/${enc13(agent)}/conversations/cleanup`;
14397
14459
  const body = { kind: v.kind, action, ...v.before ? { before: v.before } : {} };
14398
14460
  const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
@@ -14623,7 +14685,7 @@ ${bold("Agents")}
14623
14685
  agents demo <id> Public demo link /p/<slug>: anyone tries it without an account, "Keep it" clones it to theirs
14624
14686
  agents embed enable|disable <id> Web widget: a chat bubble on YOUR site (one <script>), only on the domains you list [--domains a.com,*.b.com] [--per-visitor N] [--per-ip N] [--welcome T] [--color #hex] [--rotate]
14625
14687
  look: [--title T] [--subtitle T] [--avatar URL|none] [--icon chat|avatar|ghost] [--placeholder T] [--position right|left] [--offset X[,Y]]
14626
- [--theme light|dark|auto] [--bubble-text T] [--auto-open SECONDS|off] [--starters "a|b|c"] [--font NAME] [--radius 0-24] [--style '{json}'|none]
14688
+ [--theme light|dark|auto] [--bubble-text T] [--auto-open SECONDS|off] [--starters "a|b|c"] [--font NAME] [--radius 0-24] [--typing bounce|flow|flashing|shimmer] [--typing-color #rrggbb|none] [--style '{json}'|none]
14627
14689
  theme: [--theme-source custom|teams|site] [--workspace SLUG] [--site-url URL] [--style-file theme.json]
14628
14690
  texts while it works: [--copy '{"waking":"One moment, please","thinking":["Reviewing your question\u2026"]}'|none] [--copy-file copy.json]
14629
14691
  agents embed <id> theme Export the widget theme + texts as JSON (edit it and apply it back with --style-file)
@@ -14644,7 +14706,7 @@ ${bold("Configure an agent")}
14644
14706
  channels whatsapp ls|enable|disable <id> [number] Which WhatsApp Business numbers it answers [--dry-run]
14645
14707
  channels whatsapp integrations <id> Its owner's WhatsApp Business numbers in Formmy (number, who answers today, who answered last, Integration id) [--orphans: answered by nobody but with turns]
14646
14708
  channels messenger ls <id> The workspace's Messenger pages: who answers now and who answered last [--orphans]
14647
- 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] [--dry-run]
14709
+ 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] [--board NAME|agent] [--dry-run]
14648
14710
  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]
14649
14711
  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]
14650
14712
  channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
@@ -14684,7 +14746,7 @@ ${bold("Talk to an agent")}
14684
14746
  chat <id> ["message"] Stream a conversation (no message = interactive) [--conversation ID] [--media FILE] [--file ID: from your files]
14685
14747
  conversations ls <id> List conversations
14686
14748
  conversations show <id> <conversation> What was said [--limit 20] (0 = all)
14687
- conversations archive|rm <id> --kind test|channel Clean its history (rm: tests only) [--before 7d] [--dry-run]
14749
+ conversations archive|rm <id> --kind test|channel|personal Clean its history (rm: never customers) [--before 7d] [--dry-run]
14688
14750
  turns ls <id> Recent turns and why they failed [--errors] [--skipped] [--since 1h] [--limit N]
14689
14751
  turns ls --workspace SLUG --quota-hits Messages that got no turn because the balance ran out [--days 14]
14690
14752
  turns show <id> [turn] What came in, what it answered, and where the time went (default: last) [--full]
@@ -14790,7 +14852,7 @@ ${bold("Agentes")}
14790
14852
  agents demo <id> Liga p\xFAblica de demo /p/<slug>: cualquiera lo prueba sin cuenta y \xABQued\xE1rmelo\xBB lo clona a la suya
14791
14853
  agents embed enable|disable <id> Widget web: burbuja de chat en TU sitio (un <script>), s\xF3lo en los dominios que pongas [--domains a.com,*.b.com] [--per-visitor N] [--per-ip N] [--welcome T] [--color #hex] [--rotate]
14792
14854
  look: [--title T] [--subtitle T] [--avatar URL|none] [--icon chat|avatar|ghost] [--placeholder T] [--position right|left] [--offset X[,Y]]
14793
- [--theme light|dark|auto] [--bubble-text T] [--auto-open SEGUNDOS|off] [--starters "a|b|c"] [--font NOMBRE] [--radius 0-24] [--style '{json}'|none]
14855
+ [--theme light|dark|auto] [--bubble-text T] [--auto-open SEGUNDOS|off] [--starters "a|b|c"] [--font NOMBRE] [--radius 0-24] [--typing bounce|flow|flashing|shimmer] [--typing-color #rrggbb|none] [--style '{json}'|none]
14794
14856
  tema: [--theme-source custom|teams|site] [--workspace SLUG] [--site-url URL] [--style-file tema.json]
14795
14857
  textos mientras trabaja: [--copy '{"waking":"Un momento, por favor","thinking":["Revisando su consulta\u2026"]}'|none] [--copy-file textos.json]
14796
14858
  agents embed <id> theme Exporta el tema y los textos del widget en JSON (ed\xEDtalo y vuelve a aplicarlo con --style-file)
@@ -14811,7 +14873,7 @@ ${bold("Configurar un agente")}
14811
14873
  channels whatsapp ls|enable|disable <id> [n\xFAmero] Qu\xE9 n\xFAmeros de WhatsApp Business contesta [--dry-run]
14812
14874
  channels whatsapp integrations <id> Los n\xFAmeros de WhatsApp Business de su due\xF1o en Formmy (n\xFAmero, qui\xE9n contesta hoy, qui\xE9n contest\xF3 al \xFAltimo, id de la Integration) [--orphans: sin qui\xE9n conteste pero con turnos]
14813
14875
  channels messenger ls <id> Las p\xE1ginas de Messenger del espacio: qui\xE9n contesta hoy y qui\xE9n contest\xF3 al \xFAltimo [--orphans]
14814
- 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] [--dry-run]
14876
+ 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] [--board NOMBRE|agent] [--dry-run]
14815
14877
  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]
14816
14878
  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]
14817
14879
  channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
@@ -14851,7 +14913,7 @@ ${bold("Hablar con un agente")}
14851
14913
  chat <id> ["mensaje"] Conversaci\xF3n en streaming (sin mensaje = interactivo) [--conversation ID] [--media ARCHIVO] [--file ID: de tus archivos]
14852
14914
  conversations ls <id> Lista conversaciones
14853
14915
  conversations show <id> <conversaci\xF3n> Qu\xE9 se dijo [--limit 20] (0 = todo)
14854
- conversations archive|rm <id> --kind test|channel Limpia su historial (rm: s\xF3lo pruebas) [--before 7d] [--dry-run]
14916
+ conversations archive|rm <id> --kind test|channel|personal Limpia su historial (rm: nunca clientes) [--before 7d] [--dry-run]
14855
14917
  turns ls <id> Turnos recientes y por qu\xE9 fallaron [--errors] [--skipped] [--since 1h] [--limit N]
14856
14918
  turns ls --workspace SLUG --quota-hits Mensajes que no abrieron turno porque se acab\xF3 el saldo [--days 14]
14857
14919
  turns show <id> [turno] Qu\xE9 lleg\xF3, qu\xE9 contest\xF3 y en qu\xE9 se fue el tiempo (default: el \xFAltimo) [--full]
@@ -14997,6 +15059,8 @@ async function main() {
14997
15059
  style: { type: "string" },
14998
15060
  font: { type: "string" },
14999
15061
  radius: { type: "string" },
15062
+ typing: { type: "string" },
15063
+ "typing-color": { type: "string" },
15000
15064
  "style-file": { type: "string" },
15001
15065
  copy: { type: "string" },
15002
15066
  "copy-file": { type: "string" },
@@ -15157,7 +15221,7 @@ async function main() {
15157
15221
  const tokens = splitTokens(pos, v);
15158
15222
  globals.token = tokens.session;
15159
15223
  globals.profile = v.profile;
15160
- if (v.version) return out({ version: "0.57.1" }, () => "0.57.1");
15224
+ if (v.version) return out({ version: "0.58.1" }, () => "0.58.1");
15161
15225
  for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
15162
15226
  const context = (await readConfig()).workspace;
15163
15227
  if (!explicitToken()?.startsWith("gat_")) {
@@ -15323,7 +15387,7 @@ async function main() {
15323
15387
  throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
15324
15388
  }
15325
15389
  }
15326
- var notice = updateNotice("0.57.1").catch(() => null);
15390
+ var notice = updateNotice("0.58.1").catch(() => null);
15327
15391
  main().then(
15328
15392
  async () => {
15329
15393
  const n = await notice;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostystudio/cli",
3
- "version": "0.57.1",
3
+ "version": "0.58.1",
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": {
@@ -16,7 +16,7 @@
16
16
  "scripts": {
17
17
  "build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node22 --outfile=dist/index.js --banner:js='#!/usr/bin/env node\nimport { createRequire as __cr } from \"node:module\"; const require = __cr(import.meta.url);' --define:__VERSION__=\\\"$npm_package_version\\\"",
18
18
  "prepack": "npm run build",
19
- "test": "for t in flags prompt api rows wa-auth resolve aliases i18n help completion photos channels-retry spaces sites diff cards; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
19
+ "test": "for t in flags prompt api rows wa-auth resolve aliases i18n help completion photos channels-retry spaces sites diff cards env; do esbuild src/$t.test.ts --bundle --platform=node --format=esm --outfile=/tmp/ghosty-cli-$t.test.mjs --log-level=error || exit 1; done && node --test /tmp/ghosty-cli-*.test.mjs"
20
20
  },
21
21
  "keywords": [
22
22
  "ghosty",