@ghostystudio/cli 0.58.0 → 0.59.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 +10 -1
- package/dist/index.js +125 -39
- 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,15 @@ 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.59.0
|
|
113
|
+
|
|
114
|
+
- `spaces archive <workspace>` / `spaces restore <workspace>`: archive a workspace (same as Settings → Archive: it leaves `/app` and `spaces ls`; agents and data stay) or bring it back. Owner or staff; dry run first, archiving asks you to type the slug; logged as `workspace.archive` / `workspace.restore`.
|
|
115
|
+
- `spaces ls` no longer lists archived workspaces you are a member of (same as `/app`).
|
|
116
|
+
|
|
117
|
+
### Changes in 0.58.1
|
|
118
|
+
|
|
119
|
+
- `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`.
|
|
120
|
+
|
|
112
121
|
### Changes in 0.57.1 (0.57.0 + the 0.56.1 factory fix)
|
|
113
122
|
|
|
114
123
|
- `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`.
|
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
|
|
@@ -8169,7 +8169,17 @@ var SERVER_MESSAGES = [
|
|
|
8169
8169
|
["s\xF3lo el due\xF1o del agente maneja su widget web", "only the agent's owner manages its web widget"],
|
|
8170
8170
|
["dominio no v\xE1lido: {} (usa ejemplo.com o *.ejemplo.com, sin https:// ni rutas)", "invalid domain: {} (use example.com or *.example.com, no https:// or paths)"],
|
|
8171
8171
|
["el widget web s\xF3lo corre con Claude o MiniGhosty (ghosty-lite): son los que a\xEDslan a cada visitante", "the web widget only runs on Claude or MiniGhosty (ghosty-lite): they isolate each visitor"],
|
|
8172
|
-
["pon al menos un dominio donde va a vivir el widget (--domains ejemplo.com)", "add at least one domain where the widget will live (--domains example.com)"]
|
|
8172
|
+
["pon al menos un dominio donde va a vivir el widget (--domains ejemplo.com)", "add at least one domain where the widget will live (--domains example.com)"],
|
|
8173
|
+
// — Espacios (`workspaces.server.ts`: crear y archivar)
|
|
8174
|
+
["Ahora mismo no tenemos sitio para espacios nuevos. Te apuntamos en la lista y te avisamos en cuanto se libere \u2014 normalmente es cuesti\xF3n de horas.", "We have no room for new workspaces right now. You're on the list and we'll let you know as soon as there is \u2014 usually within hours."],
|
|
8175
|
+
["Para archivar escribe el nombre exacto: {}", "To archive, type the exact name: {}"],
|
|
8176
|
+
['correo inv\xE1lido: "{}"', 'invalid email: "{}"'],
|
|
8177
|
+
['el workspace "{}" ya existe', 'workspace "{}" already exists'],
|
|
8178
|
+
["slug inv\xE1lido (2-40, min\xFAsculas/n\xFAmeros/guiones, no reservado)", "invalid slug (2-40, lowercase/numbers/hyphens, not reserved)"],
|
|
8179
|
+
["{} rebot\xF3 antes y dejamos de escribirle. Hay que revisar esa direcci\xF3n.", "{} bounced before and we stopped writing to it. Check that address."],
|
|
8180
|
+
["{} tiene que confirmar su correo antes de crear un espacio, y no pudimos mandarte la liga ahora mismo. P\xEDdela otra vez desde /verify-email.", "{} must confirm their email before creating a workspace, and we couldn't send the link right now. Ask for it again from /verify-email."],
|
|
8181
|
+
["{} tiene que confirmar su correo antes de crear un espacio. Acabamos de mandarte la liga; \xE1brela y vuelve a intentarlo.", "{} must confirm their email before creating a workspace. We just sent you the link; open it and try again."],
|
|
8182
|
+
["{} ya tiene {} workspace(s) y su plan ({}) permite {}.", "{} already has {} workspace(s) and their plan ({}) allows {}."]
|
|
8173
8183
|
];
|
|
8174
8184
|
|
|
8175
8185
|
// src/i18n.ts
|
|
@@ -8468,7 +8478,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8468
8478
|
const req = httpsRequest(`${base}${path}`, {
|
|
8469
8479
|
method: "POST",
|
|
8470
8480
|
timeout: timeoutMs,
|
|
8471
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8481
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.59.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8472
8482
|
}, (res) => {
|
|
8473
8483
|
let buf = "";
|
|
8474
8484
|
res.on("data", (c) => buf += c);
|
|
@@ -8510,7 +8520,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8510
8520
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8511
8521
|
headers: {
|
|
8512
8522
|
authorization: `Bearer ${token}`,
|
|
8513
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8523
|
+
"user-agent": `ghosty-cli/${"0.59.0"}`,
|
|
8514
8524
|
"accept-language": lang(),
|
|
8515
8525
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8516
8526
|
...opts.headers
|
|
@@ -9095,7 +9105,7 @@ ${dim(t(`only ${workspace} (ghosty use) \xB7 all: --all`, `s\xF3lo ${workspace}
|
|
|
9095
9105
|
function parseEnv(raw) {
|
|
9096
9106
|
if (!raw) return void 0;
|
|
9097
9107
|
const env = {};
|
|
9098
|
-
for (const pair of raw.split(
|
|
9108
|
+
for (const pair of raw.split(/,(?=[A-Za-z_][A-Za-z0-9_]*=)/)) {
|
|
9099
9109
|
const i = pair.indexOf("=");
|
|
9100
9110
|
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
9111
|
env[pair.slice(0, i).trim()] = pair.slice(i + 1);
|
|
@@ -10305,6 +10315,7 @@ ${bold("Partner space")} ${dim("(you, signed in as the space owner)")}
|
|
|
10305
10315
|
partner credentials rotate <keyId> [--grace MIN] New secret, same keyId (old one valid MIN, default 60)
|
|
10306
10316
|
partner credentials revoke <keyId> Revoke it (idempotent)
|
|
10307
10317
|
partner audit Who did what, when
|
|
10318
|
+
partner usage [--tenant ID] [--from YYYY-MM-DD] [--to YYYY-MM-DD] Turns, tokens, cost and errors per business (30 days by default)
|
|
10308
10319
|
|
|
10309
10320
|
${bold("Shared agents (solo-MCP)")} ${dim("(--agent SLUG picks another agent of the partner: nik-admin, nik-public\u2026)")}
|
|
10310
10321
|
partner agent get [--agent SLUG] The agent template and its version
|
|
@@ -10350,6 +10361,7 @@ ${bold("Espacio de partner")} ${dim("(t\xFA, con sesi\xF3n como due\xF1a del es
|
|
|
10350
10361
|
partner credentials rotate <keyId> [--grace MIN] Secreto nuevo, mismo keyId (el viejo vale MIN, default 60)
|
|
10351
10362
|
partner credentials revoke <keyId> La revoca (idempotente)
|
|
10352
10363
|
partner audit Qui\xE9n hizo qu\xE9 y cu\xE1ndo
|
|
10364
|
+
partner usage [--tenant ID] [--from AAAA-MM-DD] [--to AAAA-MM-DD] Turnos, tokens, costo y fallas por negocio (30 d\xEDas por default)
|
|
10353
10365
|
|
|
10354
10366
|
${bold("Agentes compartidos (solo-MCP)")} ${dim("(--agent SLUG elige otro agente del partner: nik-admin, nik-public\u2026)")}
|
|
10355
10367
|
partner agent get [--agent SLUG] La plantilla del agente y su versi\xF3n
|
|
@@ -10406,7 +10418,7 @@ async function call(path, v, init = {}) {
|
|
|
10406
10418
|
"x-ghosty-ts": ts,
|
|
10407
10419
|
"x-ghosty-sig": sig,
|
|
10408
10420
|
"content-type": "application/json",
|
|
10409
|
-
"user-agent": `ghosty-cli/${"0.
|
|
10421
|
+
"user-agent": `ghosty-cli/${"0.59.0"}`
|
|
10410
10422
|
},
|
|
10411
10423
|
body: method === "GET" ? void 0 : raw
|
|
10412
10424
|
});
|
|
@@ -10649,6 +10661,29 @@ async function partnerCmd(sub, a1, a2, v, a3) {
|
|
|
10649
10661
|
const r = await json("/api/v2/partner/audit", v, { ownerOnly: true });
|
|
10650
10662
|
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"]));
|
|
10651
10663
|
}
|
|
10664
|
+
case "usage": {
|
|
10665
|
+
const q = new URLSearchParams();
|
|
10666
|
+
if (v.tenant) q.set("externalId", v.tenant);
|
|
10667
|
+
if (v.from) q.set("from", v.from);
|
|
10668
|
+
if (v.to) q.set("to", v.to);
|
|
10669
|
+
const r = await json(
|
|
10670
|
+
`/api/v2/partner/usage${q.size ? `?${q}` : ""}`,
|
|
10671
|
+
v
|
|
10672
|
+
);
|
|
10673
|
+
return out(
|
|
10674
|
+
r,
|
|
10675
|
+
() => r.tenants.length ? table(
|
|
10676
|
+
r.tenants.map((x) => ({
|
|
10677
|
+
tenant: x.name ? `${x.name} (${x.externalId})` : x.externalId,
|
|
10678
|
+
turns: x.turns,
|
|
10679
|
+
tokens: x.tokens,
|
|
10680
|
+
usd: x.costUsd.toFixed(4),
|
|
10681
|
+
errors: Object.entries(x.errors).map(([k, n]) => `${k}\xD7${n}`).join(" ") || "\u2014"
|
|
10682
|
+
})),
|
|
10683
|
+
["tenant", "turns", "tokens", "usd", "errors"]
|
|
10684
|
+
) : dim(t("No usage in that range.", "Sin consumo en ese rango."))
|
|
10685
|
+
);
|
|
10686
|
+
}
|
|
10652
10687
|
case "try":
|
|
10653
10688
|
return tryCmd(a1, a2, v);
|
|
10654
10689
|
case "wa":
|
|
@@ -10986,7 +11021,7 @@ async function agentDocsCmd(sub, id, docs, v) {
|
|
|
10986
11021
|
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"');
|
|
10987
11022
|
if (v.grant && !["read", "write", "no"].includes(v.grant)) throw usage(t("--grant takes read, write or no.", "--grant acepta read, write o no."));
|
|
10988
11023
|
const body = {
|
|
10989
|
-
...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64"), name: v.name ?? basename3(v.file) },
|
|
11024
|
+
...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64"), name: v.name ?? basename3(v.file), ...mimeField(v.file) },
|
|
10990
11025
|
...v.url && v.name ? { name: v.name } : {},
|
|
10991
11026
|
...v.description ? { description: v.description } : {},
|
|
10992
11027
|
grant: v.grant === "no" ? false : v.grant === "write" || v.write ? "write" : "read",
|
|
@@ -11005,7 +11040,7 @@ async function agentDocsCmd(sub, id, docs, v) {
|
|
|
11005
11040
|
doc = need2(doc, t("document (id or exact name)", "el documento (id o nombre exacto)"), ex);
|
|
11006
11041
|
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);
|
|
11007
11042
|
const body = {
|
|
11008
|
-
...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64") },
|
|
11043
|
+
...v.url ? { url: v.url } : { contentBase64: (await readFile6(v.file)).toString("base64"), ...mimeField(v.file) },
|
|
11009
11044
|
...v.note ? { note: v.note } : {}
|
|
11010
11045
|
};
|
|
11011
11046
|
const r = await api(agentPath2(id, `/documents/${enc6(doc)}/versions`), { method: "POST", body });
|
|
@@ -11121,6 +11156,33 @@ async function turnsGrep(id, text, v) {
|
|
|
11121
11156
|
out(r, () => rows.length ? `${table(rows, ["at", "who", "turn", "state", "session", "text"])}
|
|
11122
11157
|
${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}` : ""}.`)));
|
|
11123
11158
|
}
|
|
11159
|
+
function mimeField(path) {
|
|
11160
|
+
const types = {
|
|
11161
|
+
pdf: "application/pdf",
|
|
11162
|
+
png: "image/png",
|
|
11163
|
+
jpg: "image/jpeg",
|
|
11164
|
+
jpeg: "image/jpeg",
|
|
11165
|
+
gif: "image/gif",
|
|
11166
|
+
webp: "image/webp",
|
|
11167
|
+
mp3: "audio/mpeg",
|
|
11168
|
+
ogg: "audio/ogg",
|
|
11169
|
+
wav: "audio/wav",
|
|
11170
|
+
m4a: "audio/mp4",
|
|
11171
|
+
mp4: "video/mp4",
|
|
11172
|
+
mov: "video/quicktime",
|
|
11173
|
+
csv: "text/csv",
|
|
11174
|
+
md: "text/markdown",
|
|
11175
|
+
txt: "text/plain",
|
|
11176
|
+
html: "text/html",
|
|
11177
|
+
json: "application/json",
|
|
11178
|
+
zip: "application/zip",
|
|
11179
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
11180
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
11181
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
11182
|
+
};
|
|
11183
|
+
const mime = types[path.split(".").pop()?.toLowerCase() ?? ""];
|
|
11184
|
+
return mime ? { mime } : {};
|
|
11185
|
+
}
|
|
11124
11186
|
async function turnShow(id, turnId, full = false) {
|
|
11125
11187
|
id = need2(id, t("agent id", "el id del agente"), "ghosty turns show <agent-id> [turn-id|last] [--full]");
|
|
11126
11188
|
const r = await api(agentPath2(id, `/turns/${enc6(turnId ?? "last")}`));
|
|
@@ -11489,9 +11551,10 @@ async function channelsCmd(kind, sub, id, ref, v) {
|
|
|
11489
11551
|
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)")),
|
|
11490
11552
|
"formmy forwards": fwd(n2),
|
|
11491
11553
|
"ads only": n2.adsOnly ? lavender(t("yes", "s\xED")) : "",
|
|
11554
|
+
board: n2.board ? n2.board.name : dim(t("the agent's", "el del agente")),
|
|
11492
11555
|
id: n2.id
|
|
11493
11556
|
}));
|
|
11494
|
-
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}`)));
|
|
11557
|
+
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}`)));
|
|
11495
11558
|
}
|
|
11496
11559
|
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");
|
|
11497
11560
|
if (!ref) throw usage(t("Missing number.", "Falta el n\xFAmero."), t("Example", "Ejemplo") + `: ghosty channels whatsapp ${sub} ${id} +52 55 1234 5678`);
|
|
@@ -11654,19 +11717,21 @@ async function linkCmd(sub, id, v) {
|
|
|
11654
11717
|
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`);
|
|
11655
11718
|
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."));
|
|
11656
11719
|
const adsOnly = v["ads-only"] === void 0 ? void 0 : v["ads-only"] === "on";
|
|
11657
|
-
|
|
11720
|
+
const board = v.board === void 0 ? void 0 : /^(agent|agente)$/i.test(v.board) ? null : v.board;
|
|
11721
|
+
const boardFlag = v.board !== void 0 ? ` --board ${JSON.stringify(v.board)}` : "";
|
|
11722
|
+
if (sub === "link" && (adsOnly !== void 0 || board !== void 0) && v.answer === void 0) {
|
|
11658
11723
|
const path2 = `/api/v2/agents/${enc7(id)}/whatsapp/link`;
|
|
11659
|
-
const r2 = await api(path2, { method: "POST", body: { integrationId: v.integration, adsOnly, ...v["dry-run"] ? { dryRun: true } : {} } });
|
|
11724
|
+
const r2 = await api(path2, { method: "POST", body: { integrationId: v.integration, ...adsOnly !== void 0 ? { adsOnly } : {}, ...board !== void 0 ? { board } : {}, ...v["dry-run"] ? { dryRun: true } : {} } });
|
|
11660
11725
|
return out(r2, () => `${r2.plan.map((x) => serverText(x)).join("\n")}${v["dry-run"] ? `
|
|
11661
|
-
${dim(applyHint(`ghosty channels whatsapp link ${id} --integration ${v.integration} --ads-only ${v["ads-only"]}`))}` : ""}`);
|
|
11726
|
+
${dim(applyHint(`ghosty channels whatsapp link ${id} --integration ${v.integration}${v["ads-only"] ? ` --ads-only ${v["ads-only"]}` : ""}${boardFlag}`))}` : ""}`);
|
|
11662
11727
|
}
|
|
11663
11728
|
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)"));
|
|
11664
11729
|
const path = `/api/v2/agents/${enc7(id)}/whatsapp/link`;
|
|
11665
11730
|
if (v["partner-agent"] && !v["partner-tenant"]) throw usage(t("--partner-agent needs --partner-tenant <externalId>.", "--partner-agent necesita --partner-tenant <externalId>."));
|
|
11666
11731
|
const partner = v["partner-tenant"] ? { partnerExternalId: v["partner-tenant"], ...v["partner-agent"] ? { partnerAgentSlug: v["partner-agent"] } : {} } : {};
|
|
11667
|
-
const body = { integrationId: v.integration, ...sub === "unlink" ? { unlink: true } : { answer: v.answer === "on", ...v.forward ? { forward: true } : {}, ...adsOnly !== void 0 ? { adsOnly } : {}, ...partner } };
|
|
11732
|
+
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 } };
|
|
11668
11733
|
const partnerFlags = v["partner-tenant"] ? ` --partner-tenant ${v["partner-tenant"]}${v["partner-agent"] ? ` --partner-agent ${v["partner-agent"]}` : ""}` : "";
|
|
11669
|
-
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}` : ""}`;
|
|
11734
|
+
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}` : ""}`;
|
|
11670
11735
|
const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
|
|
11671
11736
|
const text = `${plan.plan.map((x) => serverText(x)).join("\n")}
|
|
11672
11737
|
${dim(`Formmy: ${plan.formmy.before.url ?? "null"} \u2192 ${plan.formmy.after.url ?? "null"} (${t("as", "como")} ${plan.formmy.by})`)}`;
|
|
@@ -12041,7 +12106,7 @@ var HELPS_ES = {
|
|
|
12041
12106
|
["--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"],
|
|
12042
12107
|
["--toolsets LIST", "set: grupos de tools de gs que recibe (crm, sales\u2026): una lista reemplaza; +x / -x agrega o quita"],
|
|
12043
12108
|
["--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)"],
|
|
12044
|
-
["--prompt-overlay FILE", 'set: JSON {"edits":[{"what","find","replace","regex"?,"all"?}]}, aplicados en orden; cada uno debe encontrar su lugar o no cambia nada'],
|
|
12109
|
+
["--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'],
|
|
12045
12110
|
["--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'],
|
|
12046
12111
|
["--prompt-append FILE", "set: agrega ese texto al final del prompt (con --prompt-patch tambi\xE9n: primero las ediciones, luego el texto)"],
|
|
12047
12112
|
["--voice E:ID", "set: voz de las notas de voz: elevenlabs:<voiceId> o kokoro:em_santa|em_alex|ef_dora"],
|
|
@@ -12092,7 +12157,7 @@ var HELPS_ES = {
|
|
|
12092
12157
|
["--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"],
|
|
12093
12158
|
["--check PATH", "box: este archivo debe existir en la m\xE1quina (repetible), p. ej. un archivo que trae la imagen nueva"],
|
|
12094
12159
|
["--contains PATH=TEXT", "box: ese archivo debe contener TEXT (un cambio que no agrega archivos)"],
|
|
12095
|
-
["--recycle", "box: reemplaza sus m\xE1quinas (nunca a media respuesta); pool: la nueva nace en el siguiente turno; ACP (goose, lite)
|
|
12160
|
+
["--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"],
|
|
12096
12161
|
["--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)"],
|
|
12097
12162
|
["--unset-env K1,K2", "set: borra esas variables del env (--env K= tambi\xE9n borra K)"],
|
|
12098
12163
|
["--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"],
|
|
@@ -12208,7 +12273,7 @@ var HELPS_ES = {
|
|
|
12208
12273
|
flags: [
|
|
12209
12274
|
["--media FILE", "chat: adjunta un archivo local (se repite)"],
|
|
12210
12275
|
["--file ID", "chat: adjunta de tus archivos (ghosty me files ls --q \u2026), separados por comas"],
|
|
12211
|
-
["--kind K", "archive|rm: test (pruebas)
|
|
12276
|
+
["--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)"],
|
|
12212
12277
|
["--before T", "archive|rm: s\xF3lo conversaciones sin mensajes desde T (7d, 12h o ISO)"],
|
|
12213
12278
|
["--user EMAIL", "ls/show (staff de Ghosty): las conversaciones de esa persona en cualquier agente; queda en bit\xE1cora"],
|
|
12214
12279
|
["--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"],
|
|
@@ -12268,6 +12333,7 @@ var HELPS_ES = {
|
|
|
12268
12333
|
["--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"],
|
|
12269
12334
|
["--cached", "groups ls: no le pide a WhatsApp la lista en vivo (m\xE1s r\xE1pido; puede faltar alg\xFAn grupo nuevo)"],
|
|
12270
12335
|
["--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)"],
|
|
12336
|
+
["--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"],
|
|
12271
12337
|
["--list", "history --dry-run: CU\xC1LES conversaciones (tel\xE9fono, nombre, primer mensaje, mensajes, si vino de anuncio)"],
|
|
12272
12338
|
["--only-ads / --phones a,b", "history: importa (o lista) s\xF3lo las de anuncio y/o esos tel\xE9fonos"],
|
|
12273
12339
|
["--column NAME", "history: las tarjetas nuevas nacen en esa columna en vez de la primera"],
|
|
@@ -12458,7 +12524,7 @@ var HELPS_ES = {
|
|
|
12458
12524
|
]
|
|
12459
12525
|
},
|
|
12460
12526
|
spaces: {
|
|
12461
|
-
summary: "Espacios: crea uno a tu nombre (prueba), 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, cambiar de due\xF1o. Cada cambio queda en la bit\xE1cora del espacio.",
|
|
12527
|
+
summary: "Espacios: crea uno a tu nombre (prueba), 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, cambiar de due\xF1o \u2014 y, due\xF1o o staff, archivarlo o restaurarlo. Cada cambio queda en la bit\xE1cora del espacio.",
|
|
12462
12528
|
flags: [
|
|
12463
12529
|
["--user EMAIL", "ls (staff): los espacios de esa persona"],
|
|
12464
12530
|
["--combo C", "set (staff): teams | sales | both \u2014 sin Ventas sus tableros existen pero nadie los ve; create: distinto de teams, s\xF3lo staff"],
|
|
@@ -12473,7 +12539,7 @@ var HELPS_ES = {
|
|
|
12473
12539
|
["-n, --dry-run", "ense\xF1a qu\xE9 cambia, no cambia nada"],
|
|
12474
12540
|
["--yes", "s\xE1ltate la confirmaci\xF3n (obligatorio sin terminal)"]
|
|
12475
12541
|
],
|
|
12476
|
-
notes: ["create sigue las reglas de la web: queda a tu nombre, nace como prueba de 30 d\xEDas, tu correo debe estar confirmado y tu plan topa cu\xE1ntos tienes. Sigue: ghosty partner enable <slug>.", "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.", "transfer se lleva los agentes con el espacio
|
|
12542
|
+
notes: ["create sigue las reglas de la web: queda a tu nombre, nace como prueba de 30 d\xEDas, tu correo debe estar confirmado y tu plan topa cu\xE1ntos tienes. Sigue: ghosty partner enable <slug>.", "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.", "archive hace lo mismo que Ajustes \u2192 Archivar: el espacio sale de /app y de spaces ls, sus agentes y datos se quedan; pide escribir el slug. Deshacer: ghosty spaces restore <slug>. Queda en bit\xE1cora como workspace.archive / workspace.restore.", "transfer se lleva los agentes con el espacio: las cajas ACP se mudan al due\xF1o nuevo con su /data, las del pool se reciclan (el due\xF1o va horneado en la caja); sin --copy-keys la simulaci\xF3n dice qu\xE9 llaves le faltan al nuevo \u2014 esos agentes dejar\xEDan de contestar."]
|
|
12477
12543
|
},
|
|
12478
12544
|
staff: {
|
|
12479
12545
|
summary: "Staff de Ghosty asignado a CUENTAS cliente (admin delegado que act\xFAa como s\xED mismo): ve y opera con su propia sesi\xF3n los agentes (con y sin espacio), documentos, bases y tableros de esa cuenta. Sin llaves, cobro, traspasos ni borrados. S\xF3lo el staff de Ghosty asigna; el cliente s\xF3lo ve \xABTu ejecutivo de Ghosty\xBB. Cada cambio y cada escritura quedan en la bit\xE1cora.",
|
|
@@ -12587,7 +12653,7 @@ var HELPS = {
|
|
|
12587
12653
|
["--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"],
|
|
12588
12654
|
["--toolsets LIST", "set: gs tool groups it gets (crm, sales\u2026): a list replaces; +x / -x add or remove"],
|
|
12589
12655
|
["--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)"],
|
|
12590
|
-
["--prompt-overlay FILE", 'set: JSON {"edits":[{"what","find","replace","regex"?,"all"?}]}, applied in order; each must find its place or nothing changes'],
|
|
12656
|
+
["--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'],
|
|
12591
12657
|
["--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`],
|
|
12592
12658
|
["--prompt-append FILE", "set: add that text at the end of the prompt (with --prompt-patch too: edits first, then the text)"],
|
|
12593
12659
|
["--voice E:ID", "set: voice for voice notes: elevenlabs:<voiceId> or kokoro:em_santa|em_alex|ef_dora"],
|
|
@@ -12638,7 +12704,7 @@ var HELPS = {
|
|
|
12638
12704
|
["--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"],
|
|
12639
12705
|
["--check PATH", "box: this file must exist in the machine (repeatable), e.g. a file the new image brings"],
|
|
12640
12706
|
["--contains PATH=TEXT", "box: that file must contain TEXT (a change that adds no files)"],
|
|
12641
|
-
["--recycle", "box: replace its machines (not mid-turn); pool: the new one starts on the next turn; ACP (goose, lite)
|
|
12707
|
+
["--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"],
|
|
12642
12708
|
["--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)"],
|
|
12643
12709
|
["--unset-env K1,K2", "set: delete these env variables (--env K= also deletes K)"],
|
|
12644
12710
|
["--tool [ch:]tool=on|off", "set: one tool on/off in one channel (wa:actualizar_identidad=off) or everywhere (crm_buscar=off); comma-separated"],
|
|
@@ -12841,7 +12907,7 @@ var HELPS = {
|
|
|
12841
12907
|
'ghosty chat <agent-id> ["message"] [--conversation ID] [--media FILE]\u2026 [--file ID[,ID\u2026]]',
|
|
12842
12908
|
"ghosty conversations ls <agent-id>",
|
|
12843
12909
|
"ghosty conversations show <agent-id> <conversation-id> [--limit 20]",
|
|
12844
|
-
"ghosty conversations archive|rm <agent-id> --kind test|channel [--before 7d] [--dry-run] [--yes]",
|
|
12910
|
+
"ghosty conversations archive|rm <agent-id> --kind test|channel|personal [--before 7d] [--dry-run] [--yes]",
|
|
12845
12911
|
"ghosty conversations ls --user <email> [--since 30d] [--surface android] # staff",
|
|
12846
12912
|
"ghosty conversations show <agent-id> <conversation-id> --why # staff: who sees it and why",
|
|
12847
12913
|
"ghosty conversations show <agent-id> <conversation-id> --tools # staff: what it ran and where it went"
|
|
@@ -12849,13 +12915,13 @@ var HELPS = {
|
|
|
12849
12915
|
flags: [
|
|
12850
12916
|
["--media FILE", "chat: attach a local file (repeatable)"],
|
|
12851
12917
|
["--file ID", "chat: attach from your files (ghosty me files ls --q \u2026), comma-separated"],
|
|
12852
|
-
["--kind K", "archive|rm: test (test chats)
|
|
12918
|
+
["--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)"],
|
|
12853
12919
|
["--before T", "archive|rm: only conversations with no message since T (7d, 12h or ISO)"],
|
|
12854
12920
|
["--user EMAIL", "ls/show (Ghosty staff): that person's conversations in any agent; logged"],
|
|
12855
12921
|
["--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"],
|
|
12856
12922
|
["--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"]
|
|
12857
12923
|
],
|
|
12858
|
-
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"],
|
|
12924
|
+
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"],
|
|
12859
12925
|
learn: `${REF}#conversaciones`
|
|
12860
12926
|
},
|
|
12861
12927
|
schedule: {
|
|
@@ -12951,6 +13017,7 @@ HELPS.channels = {
|
|
|
12951
13017
|
["--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"],
|
|
12952
13018
|
["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
|
|
12953
13019
|
["--ads-only on|off", "link: only conversations that come from an ad open a card (also alone, to change it on a linked number)"],
|
|
13020
|
+
["--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"],
|
|
12954
13021
|
["--list", "history --dry-run: WHICH conversations (phone, name, first message, messages, from an ad)"],
|
|
12955
13022
|
["--only-ads / --phones a,b", "history: import (or list) only the ones from an ad and/or those phones"],
|
|
12956
13023
|
["--column NAME", "history: new cards are born in that column instead of the first one"],
|
|
@@ -13190,8 +13257,8 @@ HELPS.novedades = {
|
|
|
13190
13257
|
learn: `${REF}#soporte`
|
|
13191
13258
|
};
|
|
13192
13259
|
HELPS.spaces = {
|
|
13193
|
-
summary: "Workspaces: create one of your own (trial), 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, change its owner. Each change is logged in the workspace's audit.",
|
|
13194
|
-
usage: ["ghosty spaces ls [--user EMAIL]", "ghosty spaces create <slug> [--combo teams|sales|both] [--dry-run]", "ghosty spaces join|leave <workspace>", "ghosty spaces set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off [--dry-run]", "ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]", "ghosty spaces transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] [--yes]", "ghosty spaces log <workspace> [--since 30d] [--action workspace] [--limit N]"],
|
|
13260
|
+
summary: "Workspaces: create one of your own (trial), 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, change its owner \u2014 and, owner or staff, archive or restore it. Each change is logged in the workspace's audit.",
|
|
13261
|
+
usage: ["ghosty spaces ls [--user EMAIL]", "ghosty spaces create <slug> [--combo teams|sales|both] [--dry-run]", "ghosty spaces join|leave <workspace>", "ghosty spaces set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off [--dry-run]", "ghosty spaces renew <workspace> --until YYYY-MM-DD [--note TEXT] [--dry-run] [--yes]", "ghosty spaces rename <old> <new> [--dry-run] [--yes]", "ghosty spaces archive|restore <workspace> [--dry-run] [--yes]", "ghosty spaces transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] [--yes]", "ghosty spaces log <workspace> [--since 30d] [--action workspace] [--limit N]"],
|
|
13195
13262
|
flags: [
|
|
13196
13263
|
["--user EMAIL", "ls (staff): that person's workspaces"],
|
|
13197
13264
|
["--combo C", "set (staff): teams | sales | both \u2014 without Sales its boards exist but nobody sees them; create: other than teams, staff only"],
|
|
@@ -13206,8 +13273,8 @@ HELPS.spaces = {
|
|
|
13206
13273
|
["-n, --dry-run", "show what changes, change nothing"],
|
|
13207
13274
|
["--yes", "skip the confirmation (required without a terminal)"]
|
|
13208
13275
|
],
|
|
13209
|
-
examples: ["ghosty spaces ls", "ghosty spaces create mailmask --dry-run", "ghosty spaces ls --user brenda@siiqtec.com", "ghosty spaces join denik", "ghosty spaces set denik --combo sales --dry-run", "ghosty spaces set mailmask --model-cap 4 --house-claude on", 'ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run", "ghosty spaces transfer siiqtec --to siiqtec@gmail.com --keep-as admin --copy-keys --dry-run", "ghosty spaces log siiqtec --since 30d"],
|
|
13210
|
-
notes: ["create follows the same rules as the web: you own it, it starts as a 30-day trial, your email must be confirmed, and your plan caps how many you own. Next: ghosty partner enable <slug>.", "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.", "transfer moves the agents with the workspace
|
|
13276
|
+
examples: ["ghosty spaces ls", "ghosty spaces create mailmask --dry-run", "ghosty spaces ls --user brenda@siiqtec.com", "ghosty spaces join denik", "ghosty spaces set denik --combo sales --dry-run", "ghosty spaces set mailmask --model-cap 4 --house-claude on", 'ghosty spaces renew descti --until 2026-11-01 --note "pag\xF3 octubre"', "ghosty spaces rename totequim siiqtec --dry-run", "ghosty spaces archive box-xfer-test --dry-run", "ghosty spaces transfer siiqtec --to siiqtec@gmail.com --keep-as admin --copy-keys --dry-run", "ghosty spaces log siiqtec --since 30d"],
|
|
13277
|
+
notes: ["create follows the same rules as the web: you own it, it starts as a 30-day trial, your email must be confirmed, and your plan caps how many you own. Next: ghosty partner enable <slug>.", "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.", "archive does what Settings \u2192 Archive does: the workspace leaves /app and spaces ls, its agents and data stay; it asks you to type the slug. Undo: ghosty spaces restore <slug>. Logged as workspace.archive / workspace.restore.", "transfer moves the agents with the workspace: ACP boxes move to the new owner with their /data, pool boxes are recycled (the owner is baked into the box); without --copy-keys the dry run lists which keys the new owner lacks \u2014 those agents would stop answering."],
|
|
13211
13278
|
learn: `${REF}#espacios`
|
|
13212
13279
|
};
|
|
13213
13280
|
HELPS.workspaces = HELPS.spaces;
|
|
@@ -13787,7 +13854,7 @@ ${bagsText(r)}`);
|
|
|
13787
13854
|
}
|
|
13788
13855
|
}
|
|
13789
13856
|
async function spacesCmd(sub, slug, arg, v, context) {
|
|
13790
|
-
const ex = "ghosty spaces ls [--user EMAIL] \xB7 create <slug> [--combo teams|sales|both] [--dry-run] \xB7 join|leave <workspace> \xB7 set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off \xB7 renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run] \xB7 transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] \xB7 log <workspace> [--since 30d] [--action workspace]";
|
|
13857
|
+
const ex = "ghosty spaces ls [--user EMAIL] \xB7 create <slug> [--combo teams|sales|both] [--dry-run] \xB7 join|leave <workspace> \xB7 set <workspace> --combo teams|sales|both | --model-cap 0.25|1|4|none | --house-claude on|off \xB7 renew <workspace> --until 2026-11-01 [--note TEXT] \xB7 rename <old> <new> [--dry-run] \xB7 archive|restore <workspace> [--dry-run] \xB7 transfer <workspace> --to EMAIL [--keep-as admin|member|none] [--copy-keys] [--dry-run] \xB7 log <workspace> [--since 30d] [--action workspace]";
|
|
13791
13858
|
if (!sub || sub === "ls" || sub === "list") {
|
|
13792
13859
|
const r = await api(`/api/v2/workspaces${v.user ? `?user=${enc10(v.user)}` : ""}`);
|
|
13793
13860
|
const rows = r.workspaces.map((w) => ({ workspace: w.slug, role: w.role ?? "", plan: w.plan, combo: w.combo, owner: w.owner ?? "", paid: w.paidUntil ?? "" }));
|
|
@@ -13870,6 +13937,22 @@ ${t("Rename?", "\xBFRenombrar?")}`, { yes: v.yes, expect: plan.from, retry: `gho
|
|
|
13870
13937
|
const r = await api(path, { method: "POST", body: { to: arg } });
|
|
13871
13938
|
return out(r, () => `${lavender("\u2713")} ${t(`Renamed: ${r.from} \u2192 ${r.to}. Undo: ghosty spaces rename ${r.to} ${r.from}`, `Renombrado: ${r.from} \u2192 ${r.to}. Deshacer: ghosty spaces rename ${r.to} ${r.from}`)}`);
|
|
13872
13939
|
}
|
|
13940
|
+
if (sub === "archive" || sub === "restore") {
|
|
13941
|
+
slug = needSpace(slug, `ghosty spaces ${sub} <workspace> --dry-run`);
|
|
13942
|
+
const archive = sub === "archive";
|
|
13943
|
+
const path = wsPath(slug, "/archive");
|
|
13944
|
+
const plan = await api(path, { method: "POST", body: { archive, dryRun: true } });
|
|
13945
|
+
const line2 = `${plan.slug}: ${plan.from} \u2192 ${bold(plan.to)} ${dim(t(`(${plan.agents} agent(s); they and their data stay as they are)`, `(${plan.agents} agente(s); ellos y sus datos se quedan igual)`))}`;
|
|
13946
|
+
if (!plan.changed) return out(plan, () => dim(t(`Nothing to do: ${plan.slug} is already ${plan.from}.`, `Nada que hacer: ${plan.slug} ya est\xE1 ${plan.from}.`)));
|
|
13947
|
+
if (v["dry-run"]) return out(plan, () => `${line2}
|
|
13948
|
+
${dim(t("Dry run: nothing changed.", "Simulaci\xF3n: no cambi\xF3 nada."))}`);
|
|
13949
|
+
const retry = `ghosty spaces ${sub} ${slug}`;
|
|
13950
|
+
requireYesIfHeadless(v.yes, retry);
|
|
13951
|
+
await confirm(`${line2}
|
|
13952
|
+
${archive ? t("Archive it?", "\xBFArchivarlo?") : t("Restore it?", "\xBFRestaurarlo?")}`, { yes: v.yes, retry, ...archive ? { expect: plan.slug } : {} });
|
|
13953
|
+
const r = await api(path, { method: "POST", body: { archive } });
|
|
13954
|
+
return out(r, () => archive ? `${lavender("\u2713")} ${t(`Archived: ${r.slug}. Undo: ghosty spaces restore ${r.slug}`, `Archivado: ${r.slug}. Deshacer: ghosty spaces restore ${r.slug}`)}` : `${lavender("\u2713")} ${t(`Restored: ${r.slug}.`, `Restaurado: ${r.slug}.`)}`);
|
|
13955
|
+
}
|
|
13873
13956
|
if (sub === "transfer") {
|
|
13874
13957
|
slug = needSpace(slug ?? context, "ghosty spaces transfer <workspace> --to EMAIL --dry-run");
|
|
13875
13958
|
const toEmail = v.to ?? arg;
|
|
@@ -14394,9 +14477,10 @@ async function supportThread(agent, sid, v) {
|
|
|
14394
14477
|
});
|
|
14395
14478
|
}
|
|
14396
14479
|
async function cleanupConversations(action, agent, v) {
|
|
14397
|
-
const ex = `ghosty conversations ${action} <agent> --kind ${action === "rm" ? "test" : "test|channel"} [--before 7d] [--dry-run]`;
|
|
14480
|
+
const ex = `ghosty conversations ${action} <agent> --kind ${action === "rm" ? "test|personal" : "test|channel|personal"} [--before 7d] [--dry-run]`;
|
|
14398
14481
|
if (!agent) throw usage(t("Missing agent.", "Falta el agente."), ex);
|
|
14399
|
-
if (v.kind !== "test" && v.kind !== "channel") throw usage(t("--kind takes test or
|
|
14482
|
+
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);
|
|
14483
|
+
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);
|
|
14400
14484
|
const path = `/api/v2/agents/${enc13(agent)}/conversations/cleanup`;
|
|
14401
14485
|
const body = { kind: v.kind, action, ...v.before ? { before: v.before } : {} };
|
|
14402
14486
|
const plan = await api(path, { method: "POST", body: { ...body, dryRun: true } });
|
|
@@ -14583,7 +14667,8 @@ ${bold("Workspaces")} ${dim("(changing them: Ghosty staff)")}
|
|
|
14583
14667
|
spaces set <workspace> --combo sales teams | sales | both (without Sales its boards are invisible) [--model-cap 0.25|1|4|none: most expensive model] [--house-claude on|off]
|
|
14584
14668
|
spaces renew <workspace> --until DATE Move \xABpaid until\xBB (logged) [--note] [--dry-run]
|
|
14585
14669
|
spaces rename <old> <new> Change its slug only [--dry-run]
|
|
14586
|
-
spaces
|
|
14670
|
+
spaces archive|restore <workspace> Archive it (out of /app and spaces ls; agents and data stay) or bring it back (owner or staff) [--dry-run]
|
|
14671
|
+
spaces transfer <workspace> --to EMAIL New owner; agents go with it (ACP boxes move with their /data, pool boxes are recycled) [--keep-as admin|member|none] [--copy-keys] [--dry-run]
|
|
14587
14672
|
spaces log <workspace> Its log: transfers, staff in/out, renewals (owner or staff) [--since 30d] [--action workspace]
|
|
14588
14673
|
staff ls [staff-email] [client-email] Ghosty staff assigned to client accounts (staff) [--all: also revoked]
|
|
14589
14674
|
staff assign|unassign <staff> <client> Staff sees and runs that account's agents, documents, databases and boards as themselves (no keys, billing or transfers; logged) [--reason]
|
|
@@ -14648,7 +14733,7 @@ ${bold("Configure an agent")}
|
|
|
14648
14733
|
channels whatsapp ls|enable|disable <id> [number] Which WhatsApp Business numbers it answers [--dry-run]
|
|
14649
14734
|
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]
|
|
14650
14735
|
channels messenger ls <id> The workspace's Messenger pages: who answers now and who answered last [--orphans]
|
|
14651
|
-
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]
|
|
14736
|
+
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]
|
|
14652
14737
|
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]
|
|
14653
14738
|
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]
|
|
14654
14739
|
channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
|
|
@@ -14688,7 +14773,7 @@ ${bold("Talk to an agent")}
|
|
|
14688
14773
|
chat <id> ["message"] Stream a conversation (no message = interactive) [--conversation ID] [--media FILE] [--file ID: from your files]
|
|
14689
14774
|
conversations ls <id> List conversations
|
|
14690
14775
|
conversations show <id> <conversation> What was said [--limit 20] (0 = all)
|
|
14691
|
-
conversations archive|rm <id> --kind test|channel Clean its history (rm:
|
|
14776
|
+
conversations archive|rm <id> --kind test|channel|personal Clean its history (rm: never customers) [--before 7d] [--dry-run]
|
|
14692
14777
|
turns ls <id> Recent turns and why they failed [--errors] [--skipped] [--since 1h] [--limit N]
|
|
14693
14778
|
turns ls --workspace SLUG --quota-hits Messages that got no turn because the balance ran out [--days 14]
|
|
14694
14779
|
turns show <id> [turn] What came in, what it answered, and where the time went (default: last) [--full]
|
|
@@ -14750,7 +14835,8 @@ ${bold("Espacios")} ${dim("(cambiarlos: staff de Ghosty)")}
|
|
|
14750
14835
|
spaces set <espacio> --combo sales teams | sales | both (sin Ventas sus tableros no se ven) [--model-cap 0.25|1|4|none: el modelo m\xE1s caro] [--house-claude on|off]
|
|
14751
14836
|
spaces renew <espacio> --until FECHA Mueve \xABpagado hasta\xBB (queda en bit\xE1cora) [--note] [--dry-run]
|
|
14752
14837
|
spaces rename <viejo> <nuevo> Cambia s\xF3lo su slug [--dry-run]
|
|
14753
|
-
spaces
|
|
14838
|
+
spaces archive|restore <espacio> Arch\xEDvalo (sale de /app y de spaces ls; agentes y datos se quedan) o tr\xE1elo de vuelta (due\xF1o o staff) [--dry-run]
|
|
14839
|
+
spaces transfer <espacio> --to CORREO Due\xF1o nuevo; los agentes se van con \xE9l (las cajas ACP se mudan con su /data, las del pool se reciclan) [--keep-as admin|member|none] [--copy-keys] [--dry-run]
|
|
14754
14840
|
spaces log <espacio> Su bit\xE1cora: traspasos, staff que entra o sale, renovaciones (due\xF1o o staff) [--since 30d] [--action workspace]
|
|
14755
14841
|
staff ls [correo-staff] [correo-cliente] Staff de Ghosty asignado a cuentas cliente (staff) [--all: tambi\xE9n revocadas]
|
|
14756
14842
|
staff assign|unassign <staff> <cliente> El staff ve y opera agentes, documentos, bases y tableros de esa cuenta como s\xED mismo (sin llaves, cobro ni traspasos; queda en bit\xE1cora) [--reason]
|
|
@@ -14815,7 +14901,7 @@ ${bold("Configurar un agente")}
|
|
|
14815
14901
|
channels whatsapp ls|enable|disable <id> [n\xFAmero] Qu\xE9 n\xFAmeros de WhatsApp Business contesta [--dry-run]
|
|
14816
14902
|
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]
|
|
14817
14903
|
channels messenger ls <id> Las p\xE1ginas de Messenger del espacio: qui\xE9n contesta hoy y qui\xE9n contest\xF3 al \xFAltimo [--orphans]
|
|
14818
|
-
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]
|
|
14904
|
+
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]
|
|
14819
14905
|
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]
|
|
14820
14906
|
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]
|
|
14821
14907
|
channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
|
|
@@ -14855,7 +14941,7 @@ ${bold("Hablar con un agente")}
|
|
|
14855
14941
|
chat <id> ["mensaje"] Conversaci\xF3n en streaming (sin mensaje = interactivo) [--conversation ID] [--media ARCHIVO] [--file ID: de tus archivos]
|
|
14856
14942
|
conversations ls <id> Lista conversaciones
|
|
14857
14943
|
conversations show <id> <conversaci\xF3n> Qu\xE9 se dijo [--limit 20] (0 = todo)
|
|
14858
|
-
conversations archive|rm <id> --kind test|channel Limpia su historial (rm:
|
|
14944
|
+
conversations archive|rm <id> --kind test|channel|personal Limpia su historial (rm: nunca clientes) [--before 7d] [--dry-run]
|
|
14859
14945
|
turns ls <id> Turnos recientes y por qu\xE9 fallaron [--errors] [--skipped] [--since 1h] [--limit N]
|
|
14860
14946
|
turns ls --workspace SLUG --quota-hits Mensajes que no abrieron turno porque se acab\xF3 el saldo [--days 14]
|
|
14861
14947
|
turns show <id> [turno] Qu\xE9 lleg\xF3, qu\xE9 contest\xF3 y en qu\xE9 se fue el tiempo (default: el \xFAltimo) [--full]
|
|
@@ -15163,7 +15249,7 @@ async function main() {
|
|
|
15163
15249
|
const tokens = splitTokens(pos, v);
|
|
15164
15250
|
globals.token = tokens.session;
|
|
15165
15251
|
globals.profile = v.profile;
|
|
15166
|
-
if (v.version) return out({ version: "0.
|
|
15252
|
+
if (v.version) return out({ version: "0.59.0" }, () => "0.59.0");
|
|
15167
15253
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
15168
15254
|
const context = (await readConfig()).workspace;
|
|
15169
15255
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -15329,7 +15415,7 @@ async function main() {
|
|
|
15329
15415
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
15330
15416
|
}
|
|
15331
15417
|
}
|
|
15332
|
-
var notice = updateNotice("0.
|
|
15418
|
+
var notice = updateNotice("0.59.0").catch(() => null);
|
|
15333
15419
|
main().then(
|
|
15334
15420
|
async () => {
|
|
15335
15421
|
const n = await notice;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostystudio/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"description": "Official Ghosty Studio CLI — configure, test and chat with your agents from the terminal. Built for humans and coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -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",
|