@ghostystudio/cli 0.30.0 → 0.31.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.
- package/README.md +8 -0
- package/dist/index.js +144 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,6 +95,14 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
95
95
|
|
|
96
96
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
97
97
|
|
|
98
|
+
### Changes in 0.31.0
|
|
99
|
+
|
|
100
|
+
- `spaces ls [--user EMAIL]`; staff: `spaces join|leave <workspace>` and `spaces set <workspace> --combo sales` (`workspaces` is an alias). `boards create` warns when the workspace has no Sales (nobody would see the board).
|
|
101
|
+
- `channels whatsapp history --dry-run --list` (which conversations, whether from an ad), `--only-ads`, `--phones a,b`, `--column NAME`; `channels whatsapp link --ads-only on|off` (also alone, on a linked number) and `whatsapp ls` shows it.
|
|
102
|
+
- After a `--dry-run`, the suggested command already carries `--yes` when there's no terminal. A 404 says it may be missing access.
|
|
103
|
+
- Staff: `agents ls --user EMAIL --app all|android|ios|desktop` (their list as their app gets it), `usage --boxes --by template` (whole fleet).
|
|
104
|
+
- The «new version» notice checks npm every hour (it was once a day).
|
|
105
|
+
|
|
98
106
|
### Changes in 0.30.0
|
|
99
107
|
|
|
100
108
|
- Support (Ghosty staff): `conversations ls --user EMAIL`, `conversations show <agent> <conv> --why` (who sees it and why), `turns stats` (real failure rate), `push log --user EMAIL`, `novedades ls|create|set|test|send|rm` (Android app announcements, `--image FILE`).
|
package/dist/index.js
CHANGED
|
@@ -8044,6 +8044,17 @@ var SERVER_MESSAGES = [
|
|
|
8044
8044
|
["Esta novedad ya se mand\xF3.", "This announcement was already sent."],
|
|
8045
8045
|
["no hay novedad {}", "no announcement {}"],
|
|
8046
8046
|
["lista, b\xFAsqueda y push: s\xF3lo kind=personal de quien lo abri\xF3; por liga: el suyo, y el due\xF1o/admin lee los de canal y prueba", "list, search and push: only kind=personal of whoever opened it; by link: their own, and the owner/admin reads channel and test threads"],
|
|
8047
|
+
["los espacios de otra persona: s\xF3lo el staff de Ghosty", "someone else's workspaces: Ghosty staff only"],
|
|
8048
|
+
["entrar al espacio de un cliente: s\xF3lo el staff de Ghosty", "entering a client's workspace: Ghosty staff only"],
|
|
8049
|
+
["cambiar el combo de un espacio: s\xF3lo el staff de Ghosty", "changing a workspace's combo: Ghosty staff only"],
|
|
8050
|
+
["ver la app de otra persona: s\xF3lo el staff de Ghosty", "seeing someone else's app: Ghosty staff only"],
|
|
8051
|
+
["las cajas de la flota: s\xF3lo el staff de Ghosty", "the fleet's boxes: Ghosty staff only"],
|
|
8052
|
+
["combo: {}", "combo: {}"],
|
|
8053
|
+
["{} no trae Ventas (combo {}): nadie va a ver este tablero; staff: ghosty spaces set {} --combo sales", "{} has no Sales (combo {}): nobody will see this board; staff: ghosty spaces set {} --combo sales"],
|
|
8054
|
+
["no hay columna \xAB{}\xBB en ese tablero; hay: {}", "no column \xAB{}\xBB on that board; there are: {}"],
|
|
8055
|
+
["~ WabaLink {}: s\xF3lo anuncios {} \u2192 {}", "~ WabaLink {}: ads only {} \u2192 {}"],
|
|
8056
|
+
[" s\xF3lo anuncios: on (s\xF3lo abre tarjeta lo que viene de un anuncio)", " ads only: on (only what comes from an ad opens a card)"],
|
|
8057
|
+
[" s\xF3lo anuncios: off", " ads only: off"],
|
|
8047
8058
|
// — Infra interna (casi nunca llega al CLI, pero por si acaso)
|
|
8048
8059
|
["SESSION_SECRET ausente en producci\xF3n \u2014 las cookies quedar\xEDan firmadas con un valor p\xFAblico. Inyectarlo con ./scripts/set_gs_secret.sh SESSION_SECRET (openssl rand -hex 32).", "SESSION_SECRET missing in production \u2014 cookies would be signed with a public value. Inject it with ./scripts/set_gs_secret.sh SESSION_SECRET (openssl rand -hex 32)."],
|
|
8049
8060
|
["sqld: fetch sin namespace", "sqld: fetch without namespace"],
|
|
@@ -8346,7 +8357,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8346
8357
|
const req = httpsRequest(`${base}${path}`, {
|
|
8347
8358
|
method: "POST",
|
|
8348
8359
|
timeout: timeoutMs,
|
|
8349
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8360
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.31.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8350
8361
|
}, (res) => {
|
|
8351
8362
|
let buf = "";
|
|
8352
8363
|
res.on("data", (c) => buf += c);
|
|
@@ -8388,7 +8399,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8388
8399
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8389
8400
|
headers: {
|
|
8390
8401
|
authorization: `Bearer ${token}`,
|
|
8391
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8402
|
+
"user-agent": `ghosty-cli/${"0.31.0"}`,
|
|
8392
8403
|
"accept-language": lang(),
|
|
8393
8404
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8394
8405
|
...opts.headers
|
|
@@ -8415,7 +8426,7 @@ async function toError(res) {
|
|
|
8415
8426
|
if (res.status === 401) return new CliError(t("Your session is not valid anymore.", "Tu sesi\xF3n ya no es v\xE1lida."), EXIT.AUTH, t("Run: ghosty login", "Corre: ghosty login"));
|
|
8416
8427
|
if (res.status === 403) return new CliError(t(`Not allowed: ${detail}`, `No permitido: ${detail}`), EXIT.API, t("If the agent was shared with you, its workspace (board, channels, numbers) stays with the owner: ask them to add you to that workspace. Otherwise run: ghosty login", "Si el agente se comparti\xF3 contigo, su espacio (tablero, canales, n\xFAmeros) sigue siendo del due\xF1o: p\xEDdele que te agregue a ese espacio. Si no, corre: ghosty login"));
|
|
8417
8428
|
if (res.status === 404 && (body.message || body.error)) return new CliError(detail, EXIT.API);
|
|
8418
|
-
if (res.status === 404) return new CliError(t("Not found.", "No se encontr\xF3."), EXIT.API, t("Check the agent id (ghosty agents ls) or that your token belongs to that agent.", "Revisa el id del agente (ghosty agents ls) o que tu token sea de ese agente."));
|
|
8429
|
+
if (res.status === 404) return new CliError(t("Not found \u2014 or you don't have access to it.", "No se encontr\xF3, o no tienes acceso."), EXIT.API, t("Check the agent id (ghosty agents ls) or that your token belongs to that agent. Ghosty staff on a client's workspace: ghosty spaces join <workspace>", "Revisa el id del agente (ghosty agents ls) o que tu token sea de ese agente. Staff de Ghosty en el espacio de un cliente: ghosty spaces join <espacio>"));
|
|
8419
8430
|
if (body.error === "restart_no_aplica" || body.error === "agente_sin_maquina") {
|
|
8420
8431
|
return new CliError(t("This agent doesn't need a restart: new files, skills and MCP load on its next turn.", "Este agente no necesita reinicio: los archivos, skills y MCP nuevos se cargan en su siguiente turno."), EXIT.API);
|
|
8421
8432
|
}
|
|
@@ -8430,6 +8441,9 @@ import { dirname as dirname2, join as join3, relative, resolve } from "node:path
|
|
|
8430
8441
|
// src/prompt.ts
|
|
8431
8442
|
import { createInterface } from "node:readline/promises";
|
|
8432
8443
|
var canPrompt = () => Boolean(process.stdin.isTTY && process.stderr.isTTY);
|
|
8444
|
+
function applyHint(cmd) {
|
|
8445
|
+
return t(`Apply: ${cmd}${canPrompt() ? "" : " --yes"}`, `Para aplicarlo: ${cmd}${canPrompt() ? "" : " --yes"}`);
|
|
8446
|
+
}
|
|
8433
8447
|
function requireYesIfHeadless(yes, retry) {
|
|
8434
8448
|
if (!yes && !canPrompt()) throw usage(t("--yes required when not running interactively.", "Hace falta --yes cuando no corre en una terminal."), t(`Run: ${retry} --yes`, `Corre: ${retry} --yes`));
|
|
8435
8449
|
}
|
|
@@ -9515,7 +9529,7 @@ async function call(path, v, init = {}) {
|
|
|
9515
9529
|
"x-ghosty-ts": ts,
|
|
9516
9530
|
"x-ghosty-sig": sig,
|
|
9517
9531
|
"content-type": "application/json",
|
|
9518
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9532
|
+
"user-agent": `ghosty-cli/${"0.31.0"}`
|
|
9519
9533
|
},
|
|
9520
9534
|
body: method === "GET" ? void 0 : raw
|
|
9521
9535
|
});
|
|
@@ -10333,9 +10347,10 @@ async function channelsCmd(kind, sub, id, ref, v) {
|
|
|
10333
10347
|
number: n2.phoneNumber ? `+${n2.phoneNumber}${n2.label !== n2.phoneNumber && n2.label !== n2.id ? ` ${n2.label}` : ""}` : n2.label,
|
|
10334
10348
|
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)")),
|
|
10335
10349
|
"formmy forwards": fwd(n2),
|
|
10350
|
+
"ads only": n2.adsOnly ? lavender(t("yes", "s\xED")) : "",
|
|
10336
10351
|
id: n2.id
|
|
10337
10352
|
}));
|
|
10338
|
-
return out(cur, () => rows.length ? table(rows, ["number", "answers", "formmy forwards", "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}`)));
|
|
10353
|
+
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}`)));
|
|
10339
10354
|
}
|
|
10340
10355
|
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");
|
|
10341
10356
|
if (!ref) throw usage(t("Missing number.", "Falta el n\xFAmero."), t("Example", "Ejemplo") + `: ghosty channels whatsapp ${sub} ${id} +52 55 1234 5678`);
|
|
@@ -10479,17 +10494,27 @@ ${dim(eb ? t(`Next: ghosty channels wa groups ls ${id}`, `Sigue: ghosty channels
|
|
|
10479
10494
|
}
|
|
10480
10495
|
async function linkCmd(sub, id, v) {
|
|
10481
10496
|
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`);
|
|
10497
|
+
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."));
|
|
10498
|
+
const adsOnly = v["ads-only"] === void 0 ? void 0 : v["ads-only"] === "on";
|
|
10499
|
+
if (sub === "link" && adsOnly !== void 0 && v.answer === void 0) {
|
|
10500
|
+
const path2 = `/api/v2/agents/${enc7(id)}/whatsapp/link`;
|
|
10501
|
+
const r2 = await api(path2, { method: "POST", body: { integrationId: v.integration, adsOnly, ...v["dry-run"] ? { dryRun: true } : {} } });
|
|
10502
|
+
return out(r2, () => `${r2.plan.map((x) => serverText(x)).join("\n")}${v["dry-run"] ? `
|
|
10503
|
+
${dim(applyHint(`ghosty channels whatsapp link ${id} --integration ${v.integration} --ads-only ${v["ads-only"]}`))}` : ""}`);
|
|
10504
|
+
}
|
|
10482
10505
|
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)"));
|
|
10483
10506
|
const path = `/api/v2/agents/${enc7(id)}/whatsapp/link`;
|
|
10484
|
-
const body = { integrationId: v.integration, ...sub === "unlink" ? { unlink: true } : { answer: v.answer === "on", ...v.forward ? { forward: true } : {} } };
|
|
10507
|
+
const body = { integrationId: v.integration, ...sub === "unlink" ? { unlink: true } : { answer: v.answer === "on", ...v.forward ? { forward: true } : {}, ...adsOnly !== void 0 ? { adsOnly } : {} } };
|
|
10508
|
+
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"]}` : ""}` : ""}`;
|
|
10485
10509
|
const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
|
|
10486
10510
|
const text = `${plan.plan.map((x) => serverText(x)).join("\n")}
|
|
10487
10511
|
${dim(`Formmy: ${plan.formmy.before.url ?? "null"} \u2192 ${plan.formmy.after.url ?? "null"} (${t("as", "como")} ${plan.formmy.by})`)}`;
|
|
10488
10512
|
if (v["dry-run"]) return out(plan, () => `${text}
|
|
10489
|
-
${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}
|
|
10513
|
+
${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}
|
|
10514
|
+
${dim(applyHint(cmd))}`);
|
|
10490
10515
|
const digits4 = (plan.number.phoneNumberId ?? v.integration).slice(-4);
|
|
10491
10516
|
await confirm(`${bold(text)}
|
|
10492
|
-
${sub === "link" ? t("From now on this number's messages reach this agent in gs.", "Desde ahora los mensajes de este n\xFAmero le llegan a este agente en gs.") : t("This number goes back to where it was before.", "Este n\xFAmero regresa a donde estaba antes.")}`, { yes: v.yes, expect: digits4, retry:
|
|
10517
|
+
${sub === "link" ? t("From now on this number's messages reach this agent in gs.", "Desde ahora los mensajes de este n\xFAmero le llegan a este agente en gs.") : t("This number goes back to where it was before.", "Este n\xFAmero regresa a donde estaba antes.")}`, { yes: v.yes, expect: digits4, retry: cmd });
|
|
10493
10518
|
const r = await api(path, { method: "POST", body });
|
|
10494
10519
|
return out(r, () => `${lavender("\u2713")} ${sub === "link" ? t(`Linked (link ${r.linkId}). Check: ghosty channels whatsapp ls ${id} \xB7 ghosty turns ls ${id} --since 10m`, `Enlazado (enlace ${r.linkId}). Revisa: ghosty channels whatsapp ls ${id} \xB7 ghosty turns ls ${id} --since 10m`) : t("Unlinked: Formmy points where it did before.", "Desenlazado: Formmy apunta a donde apuntaba antes.")}`);
|
|
10495
10520
|
}
|
|
@@ -10543,7 +10568,9 @@ async function historyCmd(id, v) {
|
|
|
10543
10568
|
if (!Number.isInteger(days) || days < 1 || days > 30) throw usage(t("--days takes 1 to 30.", "--days acepta de 1 a 30."));
|
|
10544
10569
|
const path = `/api/v2/agents/${enc7(id)}/whatsapp/history`;
|
|
10545
10570
|
const media = v.media !== void 0;
|
|
10546
|
-
const
|
|
10571
|
+
const filters = { ...v["only-ads"] ? { onlyAds: true } : {}, ...v.phones ? { phones: v.phones.split(",").map((x) => x.trim()).filter(Boolean) } : {}, ...v.column ? { column: v.column } : {} };
|
|
10572
|
+
const cmd = `ghosty channels whatsapp history ${id} --integration ${v.integration} --days ${days}${v["only-ads"] ? " --only-ads" : ""}${v.phones ? ` --phones ${v.phones}` : ""}${v.column ? ` --column "${v.column}"` : ""}`;
|
|
10573
|
+
const c = await api(path, { method: "POST", body: { integrationId: v.integration, days, dryRun: true, media, ...v.list ? { list: true } : {}, ...filters } });
|
|
10547
10574
|
const files = (p) => (p.mediaCopied ?? 0) + (p.mediaFailed ?? 0) ? ` \xB7 ${t(`${p.mediaCopied ?? 0} file(s) copied`, `${p.mediaCopied ?? 0} archivo(s) copiado(s)`)}${p.mediaFailed ? lavender(t(`, ${p.mediaFailed} failed (retry: --media)`, `, ${p.mediaFailed} fallaron (reintenta: --media)`)) : ""}` : "";
|
|
10548
10575
|
if (media) {
|
|
10549
10576
|
const missing = c.mediaMissing ?? 0;
|
|
@@ -10574,17 +10601,23 @@ ${dim(v["dry-run"] ? t("Dry run: nothing copied.", "Simulacro: no se copi\xF3 na
|
|
|
10574
10601
|
);
|
|
10575
10602
|
const missingHint = c.mediaMissing ? `
|
|
10576
10603
|
${lavender(t(`${c.mediaMissing} file(s) already imported have no copy in gs: add --media to copy them (Formmy keeps them 7 days).`, `${c.mediaMissing} archivo(s) ya importado(s) no tienen copia en gs: agrega --media para copiarlos (Formmy los guarda 7 d\xEDas).`))}` : "";
|
|
10577
|
-
|
|
10578
|
-
${
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
10604
|
+
const listText = c.list ? `
|
|
10605
|
+
${table(c.list.map((x) => ({ phone: x.phone, name: (x.name ?? "").slice(0, 20), ad: x.fromAd ? lavender(t("ad", "anuncio")) : "", msgs: x.messages, first: x.firstMessage.slice(0, 60) })), ["phone", "name", "ad", "msgs", "first"])}
|
|
10606
|
+
${dim(t(`${c.matching} match the filters`, `${c.matching} pasan los filtros`))}` : "";
|
|
10607
|
+
const filtered = Boolean(v["only-ads"] || v.phones);
|
|
10608
|
+
if (v["dry-run"]) return out(c, () => `${plan}${listText}${missingHint}
|
|
10609
|
+
${dim(t("Dry run: nothing imported. Re-running later never duplicates.", "Simulacro: no se import\xF3 nada. Correrlo otra vez nunca duplica."))}
|
|
10610
|
+
${dim(applyHint(cmd))}`);
|
|
10611
|
+
if (!c.conversations || filtered && c.matching === 0) return out({ ...c, imported: 0 }, () => dim(t(`Nothing to import: ${plan}.`, `Nada que importar: ${plan}.`)));
|
|
10612
|
+
await confirm(`${bold(plan)}${filtered ? `
|
|
10613
|
+
${t(`only the ${c.matching} that match the filters`, `s\xF3lo las ${c.matching} que pasan los filtros`)}` : ""}
|
|
10614
|
+
${t(`Cards appear on the team's board (no "new lead" alerts).`, "Las tarjetas aparecen en el tablero del equipo (sin avisos de \xABlead nuevo\xBB).")}`, { yes: v.yes, retry: cmd });
|
|
10582
10615
|
const sum = { newCards: 0, updatedCards: 0, messages: 0, orders: 0, suppressed: 0, mediaCopied: 0, mediaFailed: 0, paused: 0, pausedForever: 0, manual: 0 };
|
|
10583
10616
|
let cursor = 0;
|
|
10584
10617
|
let done = 0;
|
|
10585
10618
|
while (cursor !== null) {
|
|
10586
10619
|
const at = cursor;
|
|
10587
|
-
const p = await pageWithRetry(() => api(path, { method: "POST", body: { integrationId: v.integration, days, cursor: at } }), `${done}/${c.conversations}`);
|
|
10620
|
+
const p = await pageWithRetry(() => api(path, { method: "POST", body: { integrationId: v.integration, days, cursor: at, ...filters } }), `${done}/${c.conversations}`);
|
|
10588
10621
|
for (const k of Object.keys(sum)) sum[k] += p[k] ?? 0;
|
|
10589
10622
|
done += p.page;
|
|
10590
10623
|
if (!globals.json) info(dim(` ${done}/${p.total} ${t("conversations", "conversaciones")}`));
|
|
@@ -10662,7 +10695,8 @@ async function boardsCmd(sub, a1, a2, v, context) {
|
|
|
10662
10695
|
case "create": {
|
|
10663
10696
|
if (!a1 || !a2) throw usage(t("Missing workspace or name.", "Falta el espacio o el nombre."), t("Example", "Ejemplo") + ': ghosty boards create <workspace> "Siiqtec - Ixmiquilpan" --columns-from main');
|
|
10664
10697
|
const r = await api(`/api/v2/workspaces/${enc8(a1)}/boards`, { method: "POST", body: { name: a2, ...v["columns-from"] ? { columnsFrom: v["columns-from"] } : {} } });
|
|
10665
|
-
return out(r, () => `${lavender("\u2713")} ${t(`Board \xAB${r.name}\xBB created${r.columnsFrom ? ` with the ${r.columns} column(s) of \xAB${r.columnsFrom}\xBB` : ""}.`, `Tablero \xAB${r.name}\xBB creado${r.columnsFrom ? ` con las ${r.columns} columna(s) de \xAB${r.columnsFrom}\xBB` : ""}.`)} ${dim(`${t("Send an agent's cards there", "Manda ah\xED las tarjetas de un agente")}: ghosty boards assign <agent-id> "${r.name}"`)}`
|
|
10698
|
+
return out(r, () => `${lavender("\u2713")} ${t(`Board \xAB${r.name}\xBB created${r.columnsFrom ? ` with the ${r.columns} column(s) of \xAB${r.columnsFrom}\xBB` : ""}.`, `Tablero \xAB${r.name}\xBB creado${r.columnsFrom ? ` con las ${r.columns} columna(s) de \xAB${r.columnsFrom}\xBB` : ""}.`)} ${dim(`${t("Send an agent's cards there", "Manda ah\xED las tarjetas de un agente")}: ghosty boards assign <agent-id> "${r.name}"`)}${r.warning ? `
|
|
10699
|
+
${lavender(`\u26A0\uFE0F ${serverText(r.warning)}`)}` : ""}`);
|
|
10666
10700
|
}
|
|
10667
10701
|
case "rm": {
|
|
10668
10702
|
if (!a1 || !a2) throw usage(t("Missing workspace or board.", "Falta el espacio o el tablero."), t("Example", "Ejemplo") + ': ghosty boards rm <workspace> "Prueba"');
|
|
@@ -10752,7 +10786,8 @@ var HELPS_ES = {
|
|
|
10752
10786
|
["--from-workspace", "rm: lo desactiva en cada Teams donde est\xE9 y luego lo borra (due\xF1o o staff)"],
|
|
10753
10787
|
["--prompt-mode M", "set: append (default: su prompt + la persona de Ghosty) o replace (s\xF3lo su prompt)"],
|
|
10754
10788
|
["--sponsor/--owner EMAIL", "ls: agentes de CUALQUIER cuenta que paga / de quien es ese correo (staff de Ghosty); luego se operan por id"],
|
|
10755
|
-
["--ids", "ls: s\xF3lo los ids, uno por rengl\xF3n (para xargs)"]
|
|
10789
|
+
["--ids", "ls: s\xF3lo los ids, uno por rengl\xF3n (para xargs)"],
|
|
10790
|
+
["--user EMAIL --app A", "ls (staff de Ghosty): la lista de esa persona tal como la recibe su app (tono, espacio, \xFAltimo uso); A = all|android|ios|desktop"]
|
|
10756
10791
|
],
|
|
10757
10792
|
notes: [
|
|
10758
10793
|
"doctor sale con 1 cuando encuentra un problema, as\xED puede frenar un script o un CI.",
|
|
@@ -10880,6 +10915,10 @@ var HELPS_ES = {
|
|
|
10880
10915
|
["--yes", "enable / groups enable: se salta la confirmaci\xF3n (obligatorio sin terminal)"],
|
|
10881
10916
|
["--role R", "groups: equipo (el equipo: escribe en bases de datos, tools de admin) o cliente (como cualquier cliente)"],
|
|
10882
10917
|
["--cached", "groups ls: no le pide a WhatsApp la lista en vivo (m\xE1s r\xE1pido; puede faltar alg\xFAn grupo nuevo)"],
|
|
10918
|
+
["--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)"],
|
|
10919
|
+
["--list", "history --dry-run: CU\xC1LES conversaciones (tel\xE9fono, nombre, primer mensaje, mensajes, si vino de anuncio)"],
|
|
10920
|
+
["--only-ads / --phones a,b", "history: importa (o lista) s\xF3lo las de anuncio y/o esos tel\xE9fonos"],
|
|
10921
|
+
["--column NAME", "history: las tarjetas nuevas nacen en esa columna en vez de la primera"],
|
|
10883
10922
|
["--name N", "groups set: el nombre con que lo ve gs (p. ej. un grupo guardado con su jid de nombre); el asunto en WhatsApp no cambia"],
|
|
10884
10923
|
["--integration ID", "link/unlink/history: el id de la Integration del n\xFAmero de WhatsApp Business en Formmy"],
|
|
10885
10924
|
["--email E", "integrations (staff): lista los n\xFAmeros de otra cuenta de Formmy"],
|
|
@@ -10994,7 +11033,7 @@ var HELPS_ES = {
|
|
|
10994
11033
|
flags: [
|
|
10995
11034
|
["--boxes", "las cajas del espacio contra su tope (spares, dormidas) y, por agente: conversaciones por caja, cajas, despiertas, spares, turnos corriendo ahora"],
|
|
10996
11035
|
["--workspace SLUG", "qu\xE9 espacio (due\xF1o o staff); con --boxes el default es el de ghosty use"],
|
|
10997
|
-
["--by agent|day|agent,day", "--workspace: filas por agente (default), por d\xEDa (CDMX) o las dos"],
|
|
11036
|
+
["--by agent|day|agent,day", "--workspace: filas por agente (default), por d\xEDa (CDMX) o las dos \xB7 con --boxes: template = toda la flota por template (staff)"],
|
|
10998
11037
|
["--days N", "--workspace: cu\xE1ntos d\xEDas hacia atr\xE1s (default 14, m\xE1x. 92)"]
|
|
10999
11038
|
],
|
|
11000
11039
|
notes: ["failed = turnos que terminaron en error o se detuvieron. \xABno balance\xBB = mensajes que no abrieron turno porque se acab\xF3 la bolsa (ghosty turns ls --workspace X --quota-hits los lista)."]
|
|
@@ -11037,8 +11076,10 @@ var HELPS_ES = {
|
|
|
11037
11076
|
notes: ["--image se publica en img.ghosty.studio (WebP 1200 px). --to vac\xEDo = todos. send sin --at la manda ya (pregunta); --at '' quita la programaci\xF3n."]
|
|
11038
11077
|
},
|
|
11039
11078
|
spaces: {
|
|
11040
|
-
summary: "
|
|
11079
|
+
summary: "Espacios: lista los tuyos (o, staff, los de cualquiera) y operaciones del staff de Ghosty \u2014 entrar/salir del espacio de un cliente, cambiar su combo, mover \xABpagado hasta\xBB, renombrar su slug. Cada cambio queda en la bit\xE1cora del espacio.",
|
|
11041
11080
|
flags: [
|
|
11081
|
+
["--user EMAIL", "ls (staff): los espacios de esa persona"],
|
|
11082
|
+
["--combo C", "set (staff): teams | sales | both \u2014 sin Ventas sus tableros existen pero nadie los ve"],
|
|
11042
11083
|
["--until DATE", "renew: el nuevo \xABpagado hasta\xBB (AAAA-MM-DD)"],
|
|
11043
11084
|
["--note TEXT", "renew: por qu\xE9 (va a la bit\xE1cora)"],
|
|
11044
11085
|
["-n, --dry-run", "ense\xF1a qu\xE9 cambia, no cambia nada"],
|
|
@@ -11047,7 +11088,7 @@ var HELPS_ES = {
|
|
|
11047
11088
|
notes: ["Con suscripci\xF3n de Stripe renew se niega: la fecha la mueve la factura pagada.", "rename cambia s\xF3lo el slug (id y namespace se quedan, as\xED que tablero, Stripe y WhatsApp siguen funcionando); las ligas viejas <slug>.sales dejan de servir."]
|
|
11048
11089
|
}
|
|
11049
11090
|
};
|
|
11050
|
-
var HELP_ALIASES = { creds: "credentials", board: "cards", conversations: "chat" };
|
|
11091
|
+
var HELP_ALIASES = { creds: "credentials", board: "cards", conversations: "chat", workspaces: "spaces" };
|
|
11051
11092
|
|
|
11052
11093
|
// src/help.ts
|
|
11053
11094
|
var DOCS = "https://www.ghosty.studio/docs/cli";
|
|
@@ -11122,7 +11163,8 @@ var HELPS = {
|
|
|
11122
11163
|
["--from-workspace", "rm: turn it off in every Teams where it's active, then delete it (owner or staff)"],
|
|
11123
11164
|
["--prompt-mode M", "set: append (default: its prompt + Ghosty's persona) or replace (its prompt only)"],
|
|
11124
11165
|
["--sponsor/--owner EMAIL", "ls: agents of ANY account paid by / owned by that email (Ghosty staff); then operate them by id"],
|
|
11125
|
-
["--ids", "ls: only the ids, one per line (for xargs)"]
|
|
11166
|
+
["--ids", "ls: only the ids, one per line (for xargs)"],
|
|
11167
|
+
["--user EMAIL --app A", "ls (Ghosty staff): that person's list exactly as their app gets it (tone, space, last use); A = all|android|ios|desktop"]
|
|
11126
11168
|
],
|
|
11127
11169
|
examples: [
|
|
11128
11170
|
"ghosty agents doctor cmu7pgbyr0001gee3ruk172mm",
|
|
@@ -11369,6 +11411,10 @@ HELPS.channels = {
|
|
|
11369
11411
|
["--yes", "enable / groups enable: skip the confirmation (required without a terminal)"],
|
|
11370
11412
|
["--role R", "groups: equipo (the team: writes to databases, admin tools) or cliente (like any customer)"],
|
|
11371
11413
|
["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
|
|
11414
|
+
["--ads-only on|off", "link: only conversations that come from an ad open a card (also alone, to change it on a linked number)"],
|
|
11415
|
+
["--list", "history --dry-run: WHICH conversations (phone, name, first message, messages, from an ad)"],
|
|
11416
|
+
["--only-ads / --phones a,b", "history: import (or list) only the ones from an ad and/or those phones"],
|
|
11417
|
+
["--column NAME", "history: new cards are born in that column instead of the first one"],
|
|
11372
11418
|
["--name N", "groups set: the name gs shows (e.g. a group saved with its jid as name); WhatsApp's subject doesn't change"],
|
|
11373
11419
|
["--integration ID", "link/unlink/history: the WhatsApp Business number's Integration id in Formmy"],
|
|
11374
11420
|
["--email E", "integrations (staff): list the numbers of another Formmy account"],
|
|
@@ -11516,7 +11562,7 @@ HELPS.usage = {
|
|
|
11516
11562
|
flags: [
|
|
11517
11563
|
["--boxes", "the workspace's boxes against its limit (spares, asleep) and, per agent: conversations per box, boxes, awake, spares, turns running now"],
|
|
11518
11564
|
["--workspace SLUG", "which workspace (owner or staff); with --boxes the default is the one from ghosty use"],
|
|
11519
|
-
["--by agent|day|agent,day", "--workspace: rows per agent (default), per day (CDMX) or both"],
|
|
11565
|
+
["--by agent|day|agent,day", "--workspace: rows per agent (default), per day (CDMX) or both \xB7 with --boxes: template = the whole fleet by template (staff)"],
|
|
11520
11566
|
["--days N", "--workspace: how far back (default 14, max 92)"]
|
|
11521
11567
|
],
|
|
11522
11568
|
examples: ["ghosty usage", "ghosty usage --json", "ghosty usage --workspace descti --by agent,day --days 14", "ghosty usage --boxes --workspace siiqtec"],
|
|
@@ -11591,18 +11637,21 @@ HELPS.novedades = {
|
|
|
11591
11637
|
learn: `${REF}#soporte`
|
|
11592
11638
|
};
|
|
11593
11639
|
HELPS.spaces = {
|
|
11594
|
-
summary: "Ghosty staff operations
|
|
11595
|
-
usage: ["ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]"],
|
|
11640
|
+
summary: "Workspaces: list yours (or, staff, anyone's), and Ghosty staff operations \u2014 join/leave a client's workspace, change its combo, move \xABpaid until\xBB, rename its slug. Each change is logged in the workspace's audit.",
|
|
11641
|
+
usage: ["ghosty spaces ls [--user EMAIL]", "ghosty spaces join|leave <workspace>", "ghosty spaces set <workspace> --combo teams|sales|both [--dry-run]", "ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]"],
|
|
11596
11642
|
flags: [
|
|
11643
|
+
["--user EMAIL", "ls (staff): that person's workspaces"],
|
|
11644
|
+
["--combo C", "set (staff): teams | sales | both \u2014 without Sales its boards exist but nobody sees them"],
|
|
11597
11645
|
["--until DATE", "renew: the new \xABpaid until\xBB (YYYY-MM-DD)"],
|
|
11598
11646
|
["--note TEXT", "renew: why (goes into the audit)"],
|
|
11599
11647
|
["-n, --dry-run", "show what changes, change nothing"],
|
|
11600
11648
|
["--yes", "skip the confirmation (required without a terminal)"]
|
|
11601
11649
|
],
|
|
11602
|
-
examples: ['ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run"],
|
|
11650
|
+
examples: ["ghosty spaces ls", "ghosty spaces ls --user brenda@siiqtec.com", "ghosty spaces join denik", "ghosty spaces set denik --combo sales --dry-run", 'ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run"],
|
|
11603
11651
|
notes: ["With a Stripe subscription renew refuses: the paid invoice moves the date.", "rename changes only the slug (id and namespace stay, so board, Stripe and WhatsApp keep working); old <slug>.sales links stop working."],
|
|
11604
11652
|
learn: `${REF}#espacios`
|
|
11605
11653
|
};
|
|
11654
|
+
HELPS.workspaces = HELPS.spaces;
|
|
11606
11655
|
function helpText(cmd) {
|
|
11607
11656
|
const base = HELPS[cmd];
|
|
11608
11657
|
if (!base) return void 0;
|
|
@@ -11630,7 +11679,7 @@ import { mkdir as mkdir4, readFile as readFile9, writeFile as writeFile6 } from
|
|
|
11630
11679
|
import { homedir as homedir3 } from "node:os";
|
|
11631
11680
|
import { dirname as dirname3, join as join6 } from "node:path";
|
|
11632
11681
|
var FILE = join6(process.env.XDG_CONFIG_HOME || join6(homedir3(), ".config"), "ghosty", "update.json");
|
|
11633
|
-
var
|
|
11682
|
+
var TTL = 60 * 60 * 1e3;
|
|
11634
11683
|
var UPGRADE = "npm i -g @ghostystudio/cli@latest";
|
|
11635
11684
|
function newer(a, b) {
|
|
11636
11685
|
const pa = a.split(".").map(Number);
|
|
@@ -11638,14 +11687,15 @@ function newer(a, b) {
|
|
|
11638
11687
|
for (let i = 0; i < 3; i++) if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) > (pb[i] ?? 0);
|
|
11639
11688
|
return false;
|
|
11640
11689
|
}
|
|
11641
|
-
async function latestVersion() {
|
|
11690
|
+
async function latestVersion(current2) {
|
|
11642
11691
|
if (process.env.GHOSTY_NO_UPDATE_CHECK || process.env.CI) return null;
|
|
11643
11692
|
let cache2 = {};
|
|
11644
11693
|
try {
|
|
11645
11694
|
cache2 = JSON.parse(await readFile9(FILE, "utf8"));
|
|
11646
11695
|
} catch {
|
|
11647
11696
|
}
|
|
11648
|
-
if (
|
|
11697
|
+
if (current2 && cache2.latest && newer(current2, cache2.latest)) cache2.checkedAt = 0;
|
|
11698
|
+
if (!cache2.checkedAt || Date.now() - cache2.checkedAt > TTL) {
|
|
11649
11699
|
try {
|
|
11650
11700
|
const r = await fetch("https://registry.npmjs.org/@ghostystudio/cli/latest", { signal: AbortSignal.timeout(1500) });
|
|
11651
11701
|
const latest = (await r.json()).version;
|
|
@@ -11658,7 +11708,7 @@ async function latestVersion() {
|
|
|
11658
11708
|
return cache2.latest ?? null;
|
|
11659
11709
|
}
|
|
11660
11710
|
async function updateNotice(current2) {
|
|
11661
|
-
const latest = await latestVersion();
|
|
11711
|
+
const latest = await latestVersion(current2);
|
|
11662
11712
|
return latest && newer(latest, current2) ? t(`Update available: ${current2} \u2192 ${latest}. Run: ${UPGRADE}`, `Hay versi\xF3n nueva: ${current2} \u2192 ${latest}. Corre: ${UPGRADE}`) : null;
|
|
11663
11713
|
}
|
|
11664
11714
|
|
|
@@ -12083,7 +12133,33 @@ ${bagsText(r)}`);
|
|
|
12083
12133
|
}
|
|
12084
12134
|
}
|
|
12085
12135
|
async function spacesCmd(sub, slug, arg, v, context) {
|
|
12086
|
-
const ex = "ghosty spaces renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run]";
|
|
12136
|
+
const ex = "ghosty spaces ls [--user EMAIL] \xB7 join|leave <workspace> \xB7 set <workspace> --combo teams|sales|both \xB7 renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run]";
|
|
12137
|
+
if (!sub || sub === "ls" || sub === "list") {
|
|
12138
|
+
const r = await api(`/api/v2/workspaces${v.user ? `?user=${enc10(v.user)}` : ""}`);
|
|
12139
|
+
const rows = r.workspaces.map((w) => ({ workspace: w.slug, role: w.role ?? "", plan: w.plan, combo: w.combo, owner: w.owner ?? "", paid: w.paidUntil ?? "" }));
|
|
12140
|
+
return out(r, () => rows.length ? `${bold(r.user)}
|
|
12141
|
+
${table(rows, ["workspace", "role", "plan", "combo", "owner", "paid"])}` : dim(t(`${r.user} has no workspaces.`, `${r.user} no tiene espacios.`)));
|
|
12142
|
+
}
|
|
12143
|
+
if (sub === "join" || sub === "leave") {
|
|
12144
|
+
slug = needSpace(slug, `ghosty spaces ${sub} <workspace>`);
|
|
12145
|
+
const r = await api(wsPath(slug, "/staff"), { method: "POST", body: { join: sub === "join" } });
|
|
12146
|
+
return out(r, () => `${lavender("\u2713")} ${r.joined ? t(`You're in ${r.workspace} as staff (no seat; logged). Leave: ghosty spaces leave ${r.workspace}`, `Entraste a ${r.workspace} como staff (sin asiento; queda en bit\xE1cora). Salir: ghosty spaces leave ${r.workspace}`) : t(`You left ${r.workspace}.`, `Saliste de ${r.workspace}.`)}`);
|
|
12147
|
+
}
|
|
12148
|
+
if (sub === "set") {
|
|
12149
|
+
slug = needSpace(slug ?? context, "ghosty spaces set <workspace> --combo sales");
|
|
12150
|
+
if (!v.combo) throw usage(t("Nothing to change.", "Nada que cambiar."), "--combo teams|sales|both");
|
|
12151
|
+
const path = wsPath(slug, "/combo");
|
|
12152
|
+
const plan = await api(path, { method: "POST", body: { combo: v.combo, dryRun: true } });
|
|
12153
|
+
const line2 = `${plan.workspace}: combo ${plan.from} \u2192 ${plan.to}`;
|
|
12154
|
+
if (!plan.changed) return out(plan, () => dim(t(`Nothing to do: ${plan.workspace} is already ${plan.to}.`, `Nada que hacer: ${plan.workspace} ya es ${plan.to}.`)));
|
|
12155
|
+
if (v["dry-run"]) return out(plan, () => `${line2}
|
|
12156
|
+
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
12157
|
+
requireYesIfHeadless(v.yes, `ghosty spaces set ${slug} --combo ${v.combo}`);
|
|
12158
|
+
await confirm(`${line2}
|
|
12159
|
+
${t("Change it?", "\xBFCambiarlo?")}`, { yes: v.yes, retry: `ghosty spaces set ${slug} --combo ${v.combo}` });
|
|
12160
|
+
const r = await api(path, { method: "POST", body: { combo: v.combo } });
|
|
12161
|
+
return out(r, () => `${lavender("\u2713")} ${r.workspace}: combo ${r.to}`);
|
|
12162
|
+
}
|
|
12087
12163
|
if (sub === "renew") {
|
|
12088
12164
|
slug = needSpace(slug ?? context, ex);
|
|
12089
12165
|
if (!v.until) throw usage(t("Missing --until YYYY-MM-DD.", "Falta --until AAAA-MM-DD."), `ghosty spaces renew ${slug} --until 2026-11-01`);
|
|
@@ -12335,6 +12411,18 @@ async function turnStats(v) {
|
|
|
12335
12411
|
].join("\n")
|
|
12336
12412
|
);
|
|
12337
12413
|
}
|
|
12414
|
+
async function fleetBoxes() {
|
|
12415
|
+
const r = await api("/api/v2/support/boxes?by=template");
|
|
12416
|
+
const rows = r.templates.map((x) => ({ template: x.template, boxes: x.boxes, on: x.on, asleep: x.asleep, ram: x.memMb.map((m) => `${m / 1024} GB`).join(", "), vcpu: x.vcpus.join(", "), "ram on": `${(x.ramOnMb / 1024).toFixed(1)} GB` }));
|
|
12417
|
+
out(r, () => `${bold(t(`${r.total} box(es), ${r.on} awake`, `${r.total} caja(s), ${r.on} despiertas`))}
|
|
12418
|
+
${table(rows, ["template", "boxes", "on", "asleep", "ram", "vcpu", "ram on"])}`);
|
|
12419
|
+
}
|
|
12420
|
+
async function appAgentsOf(email, client) {
|
|
12421
|
+
const r = await api(`/api/v2/support/app-agents?user=${enc12(email)}${client && client !== "all" ? `&client=${enc12(client)}` : ""}`);
|
|
12422
|
+
const rows = r.agentes.map((a) => ({ id: a.id, name: a.nombre, engine: a.motor ?? "", model: a.modelo ?? "", tone: a.tono ?? t("(by position)", "(por posici\xF3n)"), space: a.espacio?.tipo === "workspace" ? a.espacio.nombre : a.espacio?.tipo ?? "", last: a.ultimaActividad ? cdmx(a.ultimaActividad) : "" }));
|
|
12423
|
+
out(r, () => `${bold(r.user)} ${dim(r.client ? t(`as the ${r.client} app sees it`, `como lo ve la app de ${r.client}`) : t("all apps", "todas las apps"))}
|
|
12424
|
+
${table(rows, ["id", "name", "engine", "model", "tone", "space", "last"])}`);
|
|
12425
|
+
}
|
|
12338
12426
|
async function pushLog(email, v) {
|
|
12339
12427
|
if (!email) throw usage(t("Missing --user.", "Falta --user."), "ghosty push log --user ana@empresa.com [--since 2d]");
|
|
12340
12428
|
const r = await api(`/api/v2/support/push-log?user=${enc12(email)}&since=${enc12(v.since ?? "2d")}${v.limit ? `&limit=${enc12(v.limit)}` : ""}`);
|
|
@@ -12415,18 +12503,21 @@ ${bold("Session")}
|
|
|
12415
12503
|
whoami Show who you are signed in as
|
|
12416
12504
|
use [workspace] Default workspace for boards, agents create and agents ls [--clear]
|
|
12417
12505
|
completion zsh|bash|fish Tab completion for commands, flags, agents and workspaces
|
|
12418
|
-
usage Your plan and how much you've used this week and month [--boxes: boxes vs the workspace limit]
|
|
12506
|
+
usage Your plan and how much you've used this week and month [--boxes: boxes vs the workspace limit \xB7 --boxes --by template: the whole fleet (staff)]
|
|
12419
12507
|
usage --workspace SLUG A workspace's turns and tokens per agent/day [--by agent,day] [--days 14]
|
|
12420
12508
|
plan apply [workspace] Apply a scheduled plan now, showing what changes [--dry-run]
|
|
12421
12509
|
engines ls Engines and models valid for --engine/--model [--models]
|
|
12422
12510
|
|
|
12423
12511
|
${bold("Workspaces")} ${dim("(changing them: Ghosty staff)")}
|
|
12424
12512
|
bags ls|create|set|rm <workspace> ["bag"] Token bags its agents spend from [--tokens 20M] [--recurring on|off] [--hard-cap on|off] [--note]
|
|
12513
|
+
spaces ls Your workspaces (role, plan, combo) [--user EMAIL: someone else's]
|
|
12514
|
+
spaces join|leave <workspace> Enter or leave a client's workspace as staff (no seat; logged)
|
|
12515
|
+
spaces set <workspace> --combo sales teams | sales | both (without Sales its boards are invisible)
|
|
12425
12516
|
spaces renew <workspace> --until DATE Move \xABpaid until\xBB (logged) [--note] [--dry-run]
|
|
12426
12517
|
spaces rename <old> <new> Change its slug only [--dry-run]
|
|
12427
12518
|
|
|
12428
12519
|
${bold("Agents")}
|
|
12429
|
-
agents ls List your agents [--engine E] [--ids: only ids, for xargs]
|
|
12520
|
+
agents ls List your agents [--engine E] [--ids: only ids, for xargs] \xB7 --user EMAIL --app all|android|ios|desktop: as their app sees it (staff)
|
|
12430
12521
|
[--sponsor EMAIL | --owner EMAIL] any account's agents (Ghosty staff)
|
|
12431
12522
|
agents create Create an agent --name [--engine] [--model] [--prompt | --prompt-file FILE] [--env K=V,\u2026]
|
|
12432
12523
|
[--workspace SLUG] [--channels teams=off,\u2026] born in a workspace, active in its Teams
|
|
@@ -12468,8 +12559,8 @@ ${bold("Configure an agent")}
|
|
|
12468
12559
|
mcp get|set <id> MCP servers [--file servers.json]
|
|
12469
12560
|
channels whatsapp ls|enable|disable <id> [number] Which WhatsApp Business numbers it answers [--dry-run]
|
|
12470
12561
|
channels whatsapp integrations <id> Its owner's WhatsApp Business numbers in Formmy (number, who answers today, Integration id)
|
|
12471
|
-
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] [--dry-run]
|
|
12472
|
-
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]
|
|
12562
|
+
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]
|
|
12563
|
+
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]
|
|
12473
12564
|
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]
|
|
12474
12565
|
channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
|
|
12475
12566
|
channels wa status|pause <id> Personal WhatsApp: how it is / pause it (keeps the session)
|
|
@@ -12539,18 +12630,21 @@ ${bold("Sesi\xF3n")}
|
|
|
12539
12630
|
whoami Muestra con qu\xE9 cuenta entraste
|
|
12540
12631
|
use [espacio] Espacio por default para boards, agents create y agents ls [--clear]
|
|
12541
12632
|
completion zsh|bash|fish Autocompletado con TAB: comandos, opciones, agentes y espacios
|
|
12542
|
-
usage Tu plan y cu\xE1nto llevas usado esta semana y este mes [--boxes: cajas contra el tope del espacio]
|
|
12633
|
+
usage Tu plan y cu\xE1nto llevas usado esta semana y este mes [--boxes: cajas contra el tope del espacio \xB7 --boxes --by template: toda la flota (staff)]
|
|
12543
12634
|
usage --workspace SLUG Turnos y tokens de un espacio por agente/d\xEDa [--by agent,day] [--days 14]
|
|
12544
12635
|
plan apply [espacio] Aplica ya un plan programado, ense\xF1ando qu\xE9 cambia [--dry-run]
|
|
12545
12636
|
engines ls Motores y modelos v\xE1lidos para --engine/--model [--models]
|
|
12546
12637
|
|
|
12547
12638
|
${bold("Espacios")} ${dim("(cambiarlos: staff de Ghosty)")}
|
|
12548
12639
|
bags ls|create|set|rm <espacio> ["bolsa"] Bolsas de tokens de las que gastan sus agentes [--tokens 20M] [--recurring on|off] [--hard-cap on|off] [--note]
|
|
12640
|
+
spaces ls Tus espacios (rol, plan, combo) [--user CORREO: los de alguien m\xE1s]
|
|
12641
|
+
spaces join|leave <espacio> Entra o sal del espacio de un cliente como staff (sin asiento; queda en bit\xE1cora)
|
|
12642
|
+
spaces set <espacio> --combo sales teams | sales | both (sin Ventas sus tableros no se ven)
|
|
12549
12643
|
spaces renew <espacio> --until FECHA Mueve \xABpagado hasta\xBB (queda en bit\xE1cora) [--note] [--dry-run]
|
|
12550
12644
|
spaces rename <viejo> <nuevo> Cambia s\xF3lo su slug [--dry-run]
|
|
12551
12645
|
|
|
12552
12646
|
${bold("Agentes")}
|
|
12553
|
-
agents ls Lista tus agentes [--engine E] [--ids: s\xF3lo ids, para xargs]
|
|
12647
|
+
agents ls Lista tus agentes [--engine E] [--ids: s\xF3lo ids, para xargs] \xB7 --user CORREO --app all|android|ios|desktop: como lo ve su app (staff)
|
|
12554
12648
|
[--sponsor CORREO | --owner CORREO] agentes de cualquier cuenta (staff de Ghosty)
|
|
12555
12649
|
agents create Crea un agente --name [--engine] [--model] [--prompt | --prompt-file ARCHIVO] [--env K=V,\u2026]
|
|
12556
12650
|
[--workspace SLUG] [--channels teams=off,\u2026] nace en un espacio, activo en su Teams
|
|
@@ -12592,8 +12686,8 @@ ${bold("Configurar un agente")}
|
|
|
12592
12686
|
mcp get|set <id> Servidores MCP [--file servers.json]
|
|
12593
12687
|
channels whatsapp ls|enable|disable <id> [n\xFAmero] Qu\xE9 n\xFAmeros de WhatsApp Business contesta [--dry-run]
|
|
12594
12688
|
channels whatsapp integrations <id> Los n\xFAmeros de WhatsApp Business de su due\xF1o en Formmy (n\xFAmero, qui\xE9n contesta hoy, id de la Integration)
|
|
12595
|
-
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] [--dry-run]
|
|
12596
|
-
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]
|
|
12689
|
+
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]
|
|
12690
|
+
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]
|
|
12597
12691
|
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]
|
|
12598
12692
|
channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
|
|
12599
12693
|
channels wa status|pause <id> WhatsApp personal: c\xF3mo est\xE1 / pausarlo (conserva la sesi\xF3n)
|
|
@@ -12654,7 +12748,7 @@ ${bold("Opciones globales")}
|
|
|
12654
12748
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
12655
12749
|
${dim("Nota: `ghosty` puede chocar con otro binario en tu PATH (p. ej. la TUI de Ghosty). Rev\xEDsalo con `which -a ghosty`; corre `npx @ghostystudio/cli` para ir a la segura.")}
|
|
12656
12750
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
12657
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "demos", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
12751
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "demos", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
12658
12752
|
var AGENTS_SUBS = ["ls", "create", "get", "set", "export", "doctor", "docs", "share", "unshare", "shares", "rm", "restart", "demo", "box"];
|
|
12659
12753
|
function unknown(what, input, known, helpCmd) {
|
|
12660
12754
|
const s = suggest(input, known);
|
|
@@ -12836,6 +12930,12 @@ async function main() {
|
|
|
12836
12930
|
button: { type: "string" },
|
|
12837
12931
|
publish: { type: "boolean" },
|
|
12838
12932
|
draft: { type: "boolean" },
|
|
12933
|
+
"ads-only": { type: "string" },
|
|
12934
|
+
list: { type: "boolean" },
|
|
12935
|
+
"only-ads": { type: "boolean" },
|
|
12936
|
+
phones: { type: "string" },
|
|
12937
|
+
combo: { type: "string" },
|
|
12938
|
+
app: { type: "string" },
|
|
12839
12939
|
ids: { type: "boolean" },
|
|
12840
12940
|
force: { type: "boolean" },
|
|
12841
12941
|
reenv: { type: "boolean" },
|
|
@@ -12852,7 +12952,7 @@ async function main() {
|
|
|
12852
12952
|
const tokens = splitTokens(pos, v);
|
|
12853
12953
|
globals.token = tokens.session;
|
|
12854
12954
|
globals.profile = v.profile;
|
|
12855
|
-
if (v.version) return out({ version: "0.
|
|
12955
|
+
if (v.version) return out({ version: "0.31.0" }, () => "0.31.0");
|
|
12856
12956
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
12857
12957
|
const context = (await readConfig()).workspace;
|
|
12858
12958
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -12898,6 +12998,7 @@ async function main() {
|
|
|
12898
12998
|
case "apply":
|
|
12899
12999
|
return applyCmd(sub, v);
|
|
12900
13000
|
case "agents":
|
|
13001
|
+
if ((sub === "ls" || sub === "list" || !sub) && v.user && v.app !== void 0) return appAgentsOf(v.user, v.app);
|
|
12901
13002
|
if (sub === "ls" || sub === "list" || !sub) return agentsLs(v.all ? void 0 : v.workspace ?? context, { sponsor: v.sponsor, owner: v.owner, engine: v.engine, ids: v.ids, all: v.all });
|
|
12902
13003
|
if (sub === "create" || sub === "new") {
|
|
12903
13004
|
if (v.workspace === "none") delete v.workspace;
|
|
@@ -12964,6 +13065,7 @@ async function main() {
|
|
|
12964
13065
|
case "partner":
|
|
12965
13066
|
return partnerCmd(sub, a1, a2, { ...v, token: tokens.turn });
|
|
12966
13067
|
case "usage":
|
|
13068
|
+
if (v.boxes && v.by === "template") return fleetBoxes();
|
|
12967
13069
|
if (v.boxes) return boxesUsage(v.workspace ?? context);
|
|
12968
13070
|
if (v.workspace || v.by || v.days) return workspaceUsage(v.workspace ?? context, v);
|
|
12969
13071
|
return meUsage();
|
|
@@ -12977,6 +13079,7 @@ async function main() {
|
|
|
12977
13079
|
return bagsCmd(sub, a1, a2, v, context);
|
|
12978
13080
|
case "spaces":
|
|
12979
13081
|
case "space":
|
|
13082
|
+
case "workspaces":
|
|
12980
13083
|
return spacesCmd(sub, a1, a2, v, context);
|
|
12981
13084
|
case "plan":
|
|
12982
13085
|
return planCmd(sub, a1, v, context);
|
|
@@ -12994,7 +13097,7 @@ async function main() {
|
|
|
12994
13097
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
12995
13098
|
}
|
|
12996
13099
|
}
|
|
12997
|
-
var notice = updateNotice("0.
|
|
13100
|
+
var notice = updateNotice("0.31.0").catch(() => null);
|
|
12998
13101
|
main().then(
|
|
12999
13102
|
async () => {
|
|
13000
13103
|
const n = await notice;
|
package/package.json
CHANGED