@ghostystudio/cli 0.21.0 → 0.23.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 +148 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,14 @@ Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
|
90
90
|
- `try --replay FILE --out DIR`: replays real conversations from your machine. The report is written after every step, survives Ghosty deploys (it honors `Retry-After`), and `--resume` continues a partial run.
|
|
91
91
|
- `agents doctor` speaks English like the rest of the CLI; `channels whatsapp ls` returns a full URL.
|
|
92
92
|
|
|
93
|
+
### Changes in 0.22.0
|
|
94
|
+
|
|
95
|
+
- `try --waba` archives its test card when it ends (`--keep` leaves it) and auto-stage no longer runs on tests.
|
|
96
|
+
- `agents set --unset-env K1,K2`.
|
|
97
|
+
- `ghosty plan apply [workspace] [--dry-run]`: apply a scheduled plan now, showing what changes (staff, or the owner without a subscription).
|
|
98
|
+
- `agents box` shows capacity (conversations per box, boxes on, turns running, plan); `ghosty usage --boxes [--workspace]`: the workspace's boxes against its limit (spares, asleep) and per agent.
|
|
99
|
+
- `channels whatsapp history` also copies photos/PDFs/audio into gs (`--media` re-copies missing ones; Formmy keeps files 7 days).
|
|
100
|
+
|
|
93
101
|
### Changes in 0.21.0
|
|
94
102
|
|
|
95
103
|
- Catalog photos: `dbs tables` shows, per photo/link column, how many links are permanent (img.ghosty.studio), external or empty; `dbs import` warns before replacing permanent links with external ones (and gives the exact `--rehost-images`); `dbs photos put <agent> <db> --table T --key-column sku --dir photos/` uploads loose photos (SKU-123.jpg → its row) to permanent links, with `--dry-run`.
|
package/dist/index.js
CHANGED
|
@@ -7569,6 +7569,14 @@ var SERVER_MESSAGES = [
|
|
|
7569
7569
|
["el almacenamiento no est\xE1 configurado", "storage isn't configured"],
|
|
7570
7570
|
["storage sin configurar (TIGRIS_*/STORAGE_*)", "storage not configured (TIGRIS_*/STORAGE_*)"],
|
|
7571
7571
|
["storage sin configurar", "storage not configured"],
|
|
7572
|
+
// — Plan programado, cajas y altas (`plan apply`, `usage --boxes`, `agents create`)
|
|
7573
|
+
["No puedes bajar de {}: {}.", "You can't go below {}: {}."],
|
|
7574
|
+
["con suscripci\xF3n, el plan nuevo se aplica al pagarse la factura; p\xEDdelo a soporte si urge", "with a subscription, the new plan is applied when the invoice is paid; ask support if it's urgent"],
|
|
7575
|
+
["dryRun va con like (ense\xF1a lo que se copiar\xEDa); sin like no hay ensayo", "dryRun goes with like (it shows what would be copied); without like there is no dry run"],
|
|
7576
|
+
["no existe el espacio {}", "workspace {} doesn't exist"],
|
|
7577
|
+
["no pude leer sus cajas", "couldn't read its boxes"],
|
|
7578
|
+
["s\xF3lo el due\xF1o del espacio o staff de gs aplican su plan", "only the workspace owner or gs staff can apply its plan"],
|
|
7579
|
+
["{} no tiene un plan programado", "{} has no scheduled plan"],
|
|
7572
7580
|
// — Fotos sueltas de un catálogo (`dbs photos put`)
|
|
7573
7581
|
["items: de 1 a 20 fotos por petici\xF3n", "items: 1 to 20 photos per request"],
|
|
7574
7582
|
["no es JPEG/PNG/WebP", "not a JPEG/PNG/WebP"],
|
|
@@ -8253,7 +8261,7 @@ async function postLong(path, body, timeoutMs = 9e5) {
|
|
|
8253
8261
|
const req = httpsRequest(`${base}${path}`, {
|
|
8254
8262
|
method: "POST",
|
|
8255
8263
|
timeout: timeoutMs,
|
|
8256
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
8264
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.23.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
8257
8265
|
}, (res) => {
|
|
8258
8266
|
let buf = "";
|
|
8259
8267
|
res.on("data", (c) => buf += c);
|
|
@@ -8295,7 +8303,7 @@ async function request(path, opts = {}, retried = false) {
|
|
|
8295
8303
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
8296
8304
|
headers: {
|
|
8297
8305
|
authorization: `Bearer ${token}`,
|
|
8298
|
-
"user-agent": `ghosty-cli/${"0.
|
|
8306
|
+
"user-agent": `ghosty-cli/${"0.23.0"}`,
|
|
8299
8307
|
"accept-language": lang(),
|
|
8300
8308
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
8301
8309
|
...opts.headers
|
|
@@ -8433,7 +8441,7 @@ async function tryWaba(id, text, v) {
|
|
|
8433
8441
|
}
|
|
8434
8442
|
const media = v.media?.[0] ? await uploadMedia(v.media[0]) : void 0;
|
|
8435
8443
|
if (process.stderr.isTTY && !globals.json && (text || media)) info(dim(t("Thinking\u2026 (the real WhatsApp path; nothing is sent)", "Pensando\u2026 (el camino real de WhatsApp; no se manda nada)")));
|
|
8436
|
-
const r = await tryPost(id, { as: "waba", session: phone, reset: v.reset, text: text ?? "", ...media ? { media } : {}, ...history ? { history } : {}, ...v["as-formmy"] ? { asFormmy: true } : {} });
|
|
8444
|
+
const r = await tryPost(id, { as: "waba", session: phone, reset: v.reset, text: text ?? "", ...media ? { media } : {}, ...history ? { history } : {}, ...v["as-formmy"] ? { asFormmy: true } : {}, ...v.keep ? { keep: true } : {} });
|
|
8437
8445
|
if (r.status !== 200) throw Object.assign(new Error(`${r.status} ${r.body.error ? serverText(r.body.error) : t("turn failed", "el turno fall\xF3")}`), { code: 1 });
|
|
8438
8446
|
const via = r.body.media ? `
|
|
8439
8447
|
${r.body.media.copied ? dim(t("attachment: downloaded through the Formmy-like proxy and copied to gs", "adjunto: descargado por el proxy tipo Formmy y copiado a gs")) : red(t("attachment: NOT copied to gs (the proxy download failed)", "adjunto: NO se copi\xF3 a gs (fall\xF3 la descarga por el proxy)"))}` : "";
|
|
@@ -8613,7 +8621,8 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
8613
8621
|
body.tools = { toolsets: [...next] };
|
|
8614
8622
|
}
|
|
8615
8623
|
const env = parseEnv(v.env);
|
|
8616
|
-
|
|
8624
|
+
const unset = (v["unset-env"] ?? "").split(",").map((k) => k.trim()).filter(Boolean);
|
|
8625
|
+
if (env || unset.length) body.env = { ...env ?? {}, ...Object.fromEntries(unset.map((k) => [k, ""])) };
|
|
8617
8626
|
const connectors = {};
|
|
8618
8627
|
for (const c of (v.connect ?? "").split(",").map((s) => s.trim()).filter(Boolean)) connectors[c] = {};
|
|
8619
8628
|
for (const c of (v.disconnect ?? "").split(",").map((s) => s.trim()).filter(Boolean)) connectors[c] = null;
|
|
@@ -8634,7 +8643,7 @@ ${dim(t("Dry run: nothing changed.", "Simulacro: no se cambi\xF3 nada."))}`);
|
|
|
8634
8643
|
if (v["auto-stage"] !== "on" && v["auto-stage"] !== "off") throw usage(t("--auto-stage takes on or off.", "--auto-stage acepta on u off."));
|
|
8635
8644
|
body.autoStage = v["auto-stage"] === "on";
|
|
8636
8645
|
}
|
|
8637
|
-
if (!Object.keys(body).length) throw usage(t("Nothing to change.", "Nada que cambiar."), t("Use", "Usa") + " --name, --engine, --model, --prompt, --prompt-file, --env, --channels, --toolsets, --connect, --disconnect, --voice, --voice-replies " + t("or", "o") + " --auto-stage.");
|
|
8646
|
+
if (!Object.keys(body).length) throw usage(t("Nothing to change.", "Nada que cambiar."), t("Use", "Usa") + " --name, --engine, --model, --prompt, --prompt-file, --env, --unset-env, --channels, --toolsets, --connect, --disconnect, --voice, --voice-replies " + t("or", "o") + " --auto-stage.");
|
|
8638
8647
|
const r = await api(agentPath(id), { method: "PATCH", body });
|
|
8639
8648
|
out(r, () => `${lavender("\u2713")} ${t("Updated", "Actualizado")}: ${Object.keys(body).join(", ")}.`);
|
|
8640
8649
|
}
|
|
@@ -8908,15 +8917,20 @@ ${dim(`${t("Verify after one turn", "Verifica despu\xE9s de un turno")}: ghosty
|
|
|
8908
8917
|
for (const c of v.contains ?? []) q.append("contains", c);
|
|
8909
8918
|
const r = await api(agentPath(id, `/box?${q}`));
|
|
8910
8919
|
out(r, () => {
|
|
8911
|
-
|
|
8920
|
+
const cap = r.capacity ? capacityLine(r.capacity) : "";
|
|
8921
|
+
if (!r.boxes.length) return [cap, dim(r.note ? serverText(r.note) : t("No live box.", "No hay caja viva."))].filter(Boolean).join("\n");
|
|
8912
8922
|
const lines = r.boxes.flatMap((b) => [
|
|
8913
8923
|
`${bold(b.sandboxId)} ${dim(b.lastMessageAt ? `${t("last message", "\xFAltimo mensaje")} ${b.lastMessageAt}` : r.kind)}${b.error ? ` ${red(serverText(b.error))}` : ""}`,
|
|
8914
8924
|
...b.checks.map((c) => ` ${c.ok ? lavender("\u2713") : red("\u2717")} ${c.path}${c.contains !== void 0 ? ` \u220B ${c.contains}` : ""}`)
|
|
8915
8925
|
]);
|
|
8916
8926
|
const verdict = !(v.check?.length || v.contains?.length) ? dim(t("Alive. Add --check PATH to confirm the new image.", "Viva. Agrega --check PATH para confirmar la imagen nueva.")) : r.stale ? red(t(`${r.stale} box(es) on an OLD image: ghosty agents box ${id} --recycle`, `${r.stale} caja(s) con imagen VIEJA: ghosty agents box ${id} --recycle`)) : `${lavender("\u2713")} ${t("Serving with the expected image.", "Atiende con la imagen esperada.")}`;
|
|
8917
|
-
return [...lines, verdict].join("\n");
|
|
8927
|
+
return [cap, ...lines, verdict].filter(Boolean).join("\n");
|
|
8918
8928
|
});
|
|
8919
8929
|
}
|
|
8930
|
+
function capacityLine(c) {
|
|
8931
|
+
const plan = c.plan ? t(` \xB7 plan ${c.plan.workspace}: ${c.plan.parallel} in parallel, up to ${c.plan.maxBoxes} box(es)`, ` \xB7 plan de ${c.plan.workspace}: ${c.plan.parallel} en paralelo, hasta ${c.plan.maxBoxes} caja(s)`) : "";
|
|
8932
|
+
return dim(t(`${c.conversationsPerBox} conversation(s) per box \xB7 ${c.boxesOn} box(es) on \xB7 ${c.activeTurns} turn(s) running now${plan}`, `${c.conversationsPerBox} conversaci\xF3n(es) por caja \xB7 ${c.boxesOn} caja(s) encendida(s) \xB7 ${c.activeTurns} turno(s) corriendo ahora${plan}`));
|
|
8933
|
+
}
|
|
8920
8934
|
|
|
8921
8935
|
// src/commands/chat.ts
|
|
8922
8936
|
import { randomUUID } from "node:crypto";
|
|
@@ -9283,7 +9297,7 @@ async function call(path, v, init = {}) {
|
|
|
9283
9297
|
"x-ghosty-ts": ts,
|
|
9284
9298
|
"x-ghosty-sig": sig,
|
|
9285
9299
|
"content-type": "application/json",
|
|
9286
|
-
"user-agent": `ghosty-cli/${"0.
|
|
9300
|
+
"user-agent": `ghosty-cli/${"0.23.0"}`
|
|
9287
9301
|
},
|
|
9288
9302
|
body: method === "GET" ? void 0 : raw
|
|
9289
9303
|
});
|
|
@@ -10046,32 +10060,61 @@ ${sub === "link" ? t("From now on this number's messages reach this agent in gs.
|
|
|
10046
10060
|
const r = await api(path, { method: "POST", body });
|
|
10047
10061
|
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.")}`);
|
|
10048
10062
|
}
|
|
10063
|
+
function bareMediaFlag(argv) {
|
|
10064
|
+
if (argv[0] !== "channels" || !argv.includes("history")) return argv;
|
|
10065
|
+
return argv.map((a, i) => a === "--media" && (argv[i + 1] === void 0 || argv[i + 1].startsWith("-")) ? "--media=" : a);
|
|
10066
|
+
}
|
|
10049
10067
|
async function historyCmd(id, v) {
|
|
10050
10068
|
if (!v.integration) throw usage(t("Missing --integration.", "Falta --integration."), t("Example", "Ejemplo") + `: ghosty channels whatsapp history ${id} --integration <formmy-integration-id> --days 7 --dry-run`);
|
|
10051
10069
|
const days = Number(v.days ?? 7);
|
|
10052
10070
|
if (!Number.isInteger(days) || days < 1 || days > 30) throw usage(t("--days takes 1 to 30.", "--days acepta de 1 a 30."));
|
|
10053
10071
|
const path = `/api/v2/agents/${enc7(id)}/whatsapp/history`;
|
|
10054
|
-
const
|
|
10072
|
+
const media = v.media !== void 0;
|
|
10073
|
+
const c = await api(path, { method: "POST", body: { integrationId: v.integration, days, dryRun: true, media } });
|
|
10074
|
+
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)`)) : ""}` : "";
|
|
10075
|
+
if (media) {
|
|
10076
|
+
const missing = c.mediaMissing ?? 0;
|
|
10077
|
+
const mplan = t(
|
|
10078
|
+
`${missing} file(s) of +${c.phoneNumber ?? "?"} from the last ${days} day(s) have no copy in gs (board ${c.board}); Formmy keeps them 7 days`,
|
|
10079
|
+
`${missing} archivo(s) de +${c.phoneNumber ?? "?"} de los \xFAltimos ${days} d\xEDa(s) no tienen copia en gs (tablero ${c.board}); Formmy los guarda 7 d\xEDas`
|
|
10080
|
+
);
|
|
10081
|
+
if (v["dry-run"] || !missing) return out(c, () => `${mplan}
|
|
10082
|
+
${dim(v["dry-run"] ? t("Dry run: nothing copied.", "Simulacro: no se copi\xF3 nada.") : t("Nothing to copy.", "Nada que copiar."))}`);
|
|
10083
|
+
const sum2 = { mediaCopied: 0, mediaFailed: 0 };
|
|
10084
|
+
let cursor2 = 0;
|
|
10085
|
+
let done2 = 0;
|
|
10086
|
+
while (cursor2 !== null) {
|
|
10087
|
+
const p = await api(path, { method: "POST", body: { integrationId: v.integration, days, cursor: cursor2, media: true } });
|
|
10088
|
+
sum2.mediaCopied += p.mediaCopied ?? 0;
|
|
10089
|
+
sum2.mediaFailed += p.mediaFailed ?? 0;
|
|
10090
|
+
done2 += p.page;
|
|
10091
|
+
if (!globals.json) info(dim(` ${done2}/${p.total} ${t("conversations", "conversaciones")}`));
|
|
10092
|
+
cursor2 = p.nextCursor;
|
|
10093
|
+
}
|
|
10094
|
+
return out({ ...sum2, conversations: done2 }, () => `${lavender("\u2713")} ${t(`${sum2.mediaCopied} file(s) copied`, `${sum2.mediaCopied} archivo(s) copiado(s)`)}${sum2.mediaFailed ? lavender(t(`, ${sum2.mediaFailed} failed (Formmy may have deleted them already)`, `, ${sum2.mediaFailed} fallaron (Formmy pudo haberlos borrado ya)`)) : ""}`);
|
|
10095
|
+
}
|
|
10055
10096
|
const plan = t(
|
|
10056
10097
|
`${c.conversations} conversation(s), ${c.messages} message(s), ${c.orders} order(s) from the last ${days} day(s)${c.phoneNumber ? ` of +${c.phoneNumber}` : ""} \u2192 board ${c.board}`,
|
|
10057
10098
|
`${c.conversations} conversaci\xF3n(es), ${c.messages} mensaje(s), ${c.orders} pedido(s) de los \xFAltimos ${days} d\xEDa(s)${c.phoneNumber ? ` de +${c.phoneNumber}` : ""} \u2192 tablero ${c.board}`
|
|
10058
10099
|
);
|
|
10059
|
-
|
|
10100
|
+
const missingHint = c.mediaMissing ? `
|
|
10101
|
+
${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).`))}` : "";
|
|
10102
|
+
if (v["dry-run"]) return out(c, () => `${plan}${missingHint}
|
|
10060
10103
|
${dim(t("Dry run: nothing imported. Re-running later never duplicates.", "Simulacro: no se import\xF3 nada. Correrlo otra vez nunca duplica."))}`);
|
|
10061
10104
|
if (!c.conversations) return out({ ...c, imported: 0 }, () => dim(t(`Nothing to import: ${plan}.`, `Nada que importar: ${plan}.`)));
|
|
10062
10105
|
await confirm(`${bold(plan)}
|
|
10063
10106
|
${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: `ghosty channels whatsapp history ${id} --integration ${v.integration} --days ${days}` });
|
|
10064
|
-
const sum = { newCards: 0, updatedCards: 0, messages: 0, orders: 0, suppressed: 0 };
|
|
10107
|
+
const sum = { newCards: 0, updatedCards: 0, messages: 0, orders: 0, suppressed: 0, mediaCopied: 0, mediaFailed: 0 };
|
|
10065
10108
|
let cursor = 0;
|
|
10066
10109
|
let done = 0;
|
|
10067
10110
|
while (cursor !== null) {
|
|
10068
10111
|
const p = await api(path, { method: "POST", body: { integrationId: v.integration, days, cursor } });
|
|
10069
|
-
for (const k of Object.keys(sum)) sum[k] += p[k];
|
|
10112
|
+
for (const k of Object.keys(sum)) sum[k] += p[k] ?? 0;
|
|
10070
10113
|
done += p.page;
|
|
10071
10114
|
if (!globals.json) info(dim(` ${done}/${p.total} ${t("conversations", "conversaciones")}`));
|
|
10072
10115
|
cursor = p.nextCursor;
|
|
10073
10116
|
}
|
|
10074
|
-
out({ ...sum, conversations: done }, () => `${lavender("\u2713")} ${t(`${sum.newCards} new card(s), ${sum.updatedCards} updated, ${sum.messages} message(s), ${sum.orders} order(s)`, `${sum.newCards} tarjeta(s) nueva(s), ${sum.updatedCards} actualizada(s), ${sum.messages} mensaje(s), ${sum.orders} pedido(s)`)}${sum.suppressed ? dim(t(` \xB7 ${sum.suppressed} bot draft(s) the customer never saw, skipped`, ` \xB7 se omitieron ${sum.suppressed} borrador(es) del bot que el cliente nunca vio`)) : ""}`);
|
|
10117
|
+
out({ ...sum, conversations: done }, () => `${lavender("\u2713")} ${t(`${sum.newCards} new card(s), ${sum.updatedCards} updated, ${sum.messages} message(s), ${sum.orders} order(s)`, `${sum.newCards} tarjeta(s) nueva(s), ${sum.updatedCards} actualizada(s), ${sum.messages} mensaje(s), ${sum.orders} pedido(s)`)}${files(sum)}${sum.suppressed ? dim(t(` \xB7 ${sum.suppressed} bot draft(s) the customer never saw, skipped`, ` \xB7 se omitieron ${sum.suppressed} borrador(es) del bot que el cliente nunca vio`)) : ""}`);
|
|
10075
10118
|
}
|
|
10076
10119
|
|
|
10077
10120
|
// src/commands/board.ts
|
|
@@ -10220,7 +10263,8 @@ var HELPS_ES = {
|
|
|
10220
10263
|
["--rotate", "demo: c\xF3digo nuevo; la liga vieja deja de servir"],
|
|
10221
10264
|
["--check PATH", "box: este archivo debe existir en la m\xE1quina (repetible), p. ej. un archivo que trae la imagen nueva"],
|
|
10222
10265
|
["--contains PATH=TEXT", "box: ese archivo debe contener TEXT (un cambio que no agrega archivos)"],
|
|
10223
|
-
["--recycle", "box: reemplaza sus m\xE1quinas (no a media respuesta); la nueva arranca en el siguiente turno. ACP: usa restart"]
|
|
10266
|
+
["--recycle", "box: reemplaza sus m\xE1quinas (no a media respuesta); la nueva arranca en el siguiente turno. ACP: usa restart"],
|
|
10267
|
+
["--unset-env K1,K2", "set: borra esas variables del env (--env K= tambi\xE9n borra K)"]
|
|
10224
10268
|
],
|
|
10225
10269
|
notes: [
|
|
10226
10270
|
"doctor sale con 1 cuando encuentra un problema, as\xED puede frenar un script o un CI.",
|
|
@@ -10290,6 +10334,7 @@ var HELPS_ES = {
|
|
|
10290
10334
|
["--session PHONE", "con --waba: un tel\xE9fono de cliente inventado (10-15 d\xEDgitos); cada tel\xE9fono es su propia conversaci\xF3n"],
|
|
10291
10335
|
["--media FILE", "con --waba: el cliente manda esta foto/audio/PDF (uno por turno)"],
|
|
10292
10336
|
["--as-formmy", "con --media: llega como un reenv\xEDo real de Formmy (proxy con credencial, copia a gs, transcripci\xF3n) y dice si la copia funcion\xF3"],
|
|
10337
|
+
["--keep", "con --waba: deja la tarjeta de prueba en el tablero (por default se archiva al terminar, y el tablero autom\xE1tico nunca corre en pruebas)"],
|
|
10293
10338
|
["--history FILE", "con --waba: lo que se dijo antes, como [{role: user|agent, text, at?}]; cuenta en el primer turno (--reset)"],
|
|
10294
10339
|
["--reset", "olvida esa sesi\xF3n de prueba (con --waba tambi\xE9n su hilo de prueba)"],
|
|
10295
10340
|
["--replay FILE", "corre las conversaciones de FILE ({sample: [{id, fakePhone, messages}]}) contra el agente"],
|
|
@@ -10332,6 +10377,7 @@ var HELPS_ES = {
|
|
|
10332
10377
|
["--cached", "groups ls: no le pide a WhatsApp la lista en vivo (m\xE1s r\xE1pido; puede faltar alg\xFAn grupo nuevo)"],
|
|
10333
10378
|
["--integration ID", "link/unlink/history: el id de la Integration del n\xFAmero de WhatsApp Business en Formmy"],
|
|
10334
10379
|
["--days N", "history: conversaciones activas en los \xFAltimos N d\xEDas (1-30, default 7)"],
|
|
10380
|
+
["--media", "history: s\xF3lo re-copia los archivos que faltan (fotos, PDF, audios) de las conversaciones ya importadas"],
|
|
10335
10381
|
["--answer on|off", "link (obligatorio, sin default): on = este agente contesta a clientes reales; off = s\xF3lo registra"],
|
|
10336
10382
|
["--forward", "link --answer off: igual hace que Formmy reenv\xEDe, para que gs vea las conversaciones"],
|
|
10337
10383
|
["--from-file F", "import: {authCreds, authKeys, phone, groups} exportado de EasyBits"],
|
|
@@ -10345,7 +10391,7 @@ var HELPS_ES = {
|
|
|
10345
10391
|
"Apagarlo sigue registrando los mensajes; nadie los contesta.",
|
|
10346
10392
|
"Los grupos son del WhatsApp PERSONAL emparejado al agente (no de WhatsApp Business). <group> es su nombre o su jid.",
|
|
10347
10393
|
"link mueve un n\xFAmero que ya vive en Formmy (EasyBits, un droplet) sin rehacer el alta de Meta; unlink lo regresa. S\xF3lo el due\xF1o, un admin del agente o el staff de Ghosty.",
|
|
10348
|
-
"history trae al tablero las tarjetas, mensajes y pedidos recientes del n\xFAmero desde Formmy (enl\xE1zalo antes). Repetirlo nunca duplica: c\xF3rrelo antes del corte y otra vez justo en el corte.",
|
|
10394
|
+
"history trae al tablero las tarjetas, mensajes y pedidos recientes del n\xFAmero desde Formmy (enl\xE1zalo antes). Repetirlo nunca duplica: c\xF3rrelo antes del corte y otra vez justo en el corte. Los archivos (fotos, PDF, audios) tambi\xE9n se copian a gs: Formmy los borra a los 7 d\xEDas, as\xED que trae el n\xFAmero dentro de esa semana; una copia que fall\xF3 se reintenta con --media.",
|
|
10349
10395
|
"import: apaga primero el socket de EasyBits SIN logout (dos sockets se tumban entre s\xED; un logout invalida las llaves). Borra el archivo despu\xE9s."
|
|
10350
10396
|
]
|
|
10351
10397
|
},
|
|
@@ -10379,6 +10425,11 @@ var HELPS_ES = {
|
|
|
10379
10425
|
"Antes de aplicar se guarda la versi\xF3n actual del agente (Versiones en la web)."
|
|
10380
10426
|
]
|
|
10381
10427
|
},
|
|
10428
|
+
plan: {
|
|
10429
|
+
summary: "Aplica ya el plan programado de un espacio (asientos, agentes en paralelo, hosting, tokens), ense\xF1ando antes qu\xE9 cambia. Staff, o el due\xF1o si no hay suscripci\xF3n (con suscripci\xF3n lo aplica la factura pagada).",
|
|
10430
|
+
flags: [["-n, --dry-run", "ense\xF1a qu\xE9 cambia y el total al mes, no aplica nada"], ["--yes", "omite la confirmaci\xF3n (obligatorio sin terminal)"]],
|
|
10431
|
+
notes: ["Sin espacio usa el de ghosty use."]
|
|
10432
|
+
},
|
|
10382
10433
|
completion: {
|
|
10383
10434
|
summary: "Autocompletado con TAB en tu shell: comandos, subcomandos y opciones (de esta ayuda), nombres de agentes y espacios (de tu cuenta, con cach\xE9 de 5 min).",
|
|
10384
10435
|
replace: [
|
|
@@ -10428,7 +10479,10 @@ var HELPS_ES = {
|
|
|
10428
10479
|
"Archivar es reversible: cuando el cliente vuelve a escribir, la tarjeta regresa. Se necesita al menos un filtro."
|
|
10429
10480
|
]
|
|
10430
10481
|
},
|
|
10431
|
-
usage: {
|
|
10482
|
+
usage: {
|
|
10483
|
+
summary: "Tu plan y cu\xE1nto llevas usado esta semana y este mes. Con --boxes: las cajas y la capacidad de un espacio, por agente.",
|
|
10484
|
+
flags: [["--boxes", "por agente: conversaciones por caja, cajas encendidas, turnos corriendo ahora; y lo que da el plan (paralelo, cajas, hosting)"], ["--workspace SLUG", "--boxes: qu\xE9 espacio (default: el de ghosty use)"]]
|
|
10485
|
+
}
|
|
10432
10486
|
};
|
|
10433
10487
|
var HELP_ALIASES = { creds: "credentials", board: "cards", conversations: "chat" };
|
|
10434
10488
|
|
|
@@ -10443,7 +10497,7 @@ var HELPS = {
|
|
|
10443
10497
|
"ghosty agents create --name NAME [--engine E] [--model M] [--prompt TEXT | --prompt-file FILE] [--env K=V,\u2026] [--workspace SLUG] [--channels teams=off,\u2026] [--like AGENT [--copy-dbs] [--board same|main|new|NAME] [--no-notify] [--dry-run]]",
|
|
10444
10498
|
"ghosty agents get <agent-id> [--fields name,model,prompt] [--prompt-out FILE]",
|
|
10445
10499
|
"ghosty agents export <agent-id> [--out DIR] # the agent as a file; see ghosty apply --help",
|
|
10446
|
-
"ghosty agents set <agent-id> [--name] [--engine] [--model] [--prompt | --prompt-file] [--env K=V,\u2026] [--connect ID,\u2026] [--disconnect ID,\u2026] [--voice E:ID] [--voice-replies auto|off] [--auto-stage on|off] [--toolsets crm,sales | +crm,-sales]",
|
|
10500
|
+
"ghosty agents set <agent-id> [--name] [--engine] [--model] [--prompt | --prompt-file] [--env K=V,\u2026] [--unset-env K1,K2] [--connect ID,\u2026] [--disconnect ID,\u2026] [--voice E:ID] [--voice-replies auto|off] [--auto-stage on|off] [--toolsets crm,sales | +crm,-sales]",
|
|
10447
10501
|
"ghosty agents set <agent-id> --prompt-base <base-agent-id> --prompt-overlay overlay.json [--dry-run [--out FILE]] | --prompt-base none",
|
|
10448
10502
|
"ghosty agents doctor <agent-id> [--verbose]",
|
|
10449
10503
|
"ghosty agents docs ls|grant|revoke <agent-id> [doc\u2026] [--write] [--no-deliver] [--like AGENT]",
|
|
@@ -10490,7 +10544,8 @@ var HELPS = {
|
|
|
10490
10544
|
["--rotate", "demo: new code; the old link stops working"],
|
|
10491
10545
|
["--check PATH", "box: this file must exist in the machine (repeatable), e.g. a file the new image brings"],
|
|
10492
10546
|
["--contains PATH=TEXT", "box: that file must contain TEXT (a change that adds no files)"],
|
|
10493
|
-
["--recycle", "box: replace its machines (not mid-turn); the new one starts on the next turn. ACP: use restart"]
|
|
10547
|
+
["--recycle", "box: replace its machines (not mid-turn); the new one starts on the next turn. ACP: use restart"],
|
|
10548
|
+
["--unset-env K1,K2", "set: delete these env variables (--env K= also deletes K)"]
|
|
10494
10549
|
],
|
|
10495
10550
|
examples: [
|
|
10496
10551
|
"ghosty agents doctor cmu7pgbyr0001gee3ruk172mm",
|
|
@@ -10623,6 +10678,7 @@ var HELPS = {
|
|
|
10623
10678
|
["--session PHONE", "with --waba: a made-up customer phone (10-15 digits); each phone is its own conversation"],
|
|
10624
10679
|
["--media FILE", "with --waba: the customer sends this photo/audio/PDF (one per turn)"],
|
|
10625
10680
|
["--as-formmy", "with --media: it arrives like a real Formmy forward (proxy with credential, copy to gs, transcription) and says if the copy worked"],
|
|
10681
|
+
["--keep", "with --waba: leave the test card on the board (by default it's archived when the test ends, and auto-stage never runs on tests)"],
|
|
10626
10682
|
["--history FILE", "with --waba: what was said before, as [{role: user|agent, text, at?}]; counts on the first turn (--reset)"],
|
|
10627
10683
|
["--reset", "forget that test session (with --waba also its test thread)"],
|
|
10628
10684
|
["--replay FILE", "run the conversations in FILE ({sample: [{id, fakePhone, messages}]}) against the agent"],
|
|
@@ -10687,7 +10743,7 @@ HELPS.channels = {
|
|
|
10687
10743
|
"ghosty channels whatsapp disable <agent-id> <number> [--dry-run]",
|
|
10688
10744
|
"ghosty channels whatsapp link <agent-id> --integration <formmy-id> --answer on|off [--forward] [--dry-run] [--yes]",
|
|
10689
10745
|
"ghosty channels whatsapp unlink <agent-id> --integration <formmy-id> [--dry-run] [--yes]",
|
|
10690
|
-
"ghosty channels whatsapp history <agent-id> --integration <formmy-id> [--days 7] [--dry-run] [--yes]",
|
|
10746
|
+
"ghosty channels whatsapp history <agent-id> --integration <formmy-id> [--days 7] [--media] [--dry-run] [--yes]",
|
|
10691
10747
|
"ghosty channels wa import <agent-id> --from-file creds.json | --from-dir auth/ --own-number yes|no [--replace] [--no-connect] [--dry-run] [--yes]",
|
|
10692
10748
|
"ghosty channels wa pair <agent-id> --phone 5215512345678",
|
|
10693
10749
|
"ghosty channels wa status|pause <agent-id>",
|
|
@@ -10703,6 +10759,7 @@ HELPS.channels = {
|
|
|
10703
10759
|
["--cached", "groups ls: don't ask WhatsApp for the live list (faster; may miss new groups)"],
|
|
10704
10760
|
["--integration ID", "link/unlink/history: the WhatsApp Business number's Integration id in Formmy"],
|
|
10705
10761
|
["--days N", "history: conversations active in the last N days (1-30, default 7)"],
|
|
10762
|
+
["--media", "history: only re-copy the missing files (photos, PDFs, audio) of conversations already imported"],
|
|
10706
10763
|
["--answer on|off", "link (required, no default): on = this agent answers real customers; off = it only records"],
|
|
10707
10764
|
["--forward", "link --answer off: still make Formmy forward, so gs sees the conversations"],
|
|
10708
10765
|
["--from-file F", "import: {authCreds, authKeys, phone, groups} exported from EasyBits"],
|
|
@@ -10719,6 +10776,7 @@ HELPS.channels = {
|
|
|
10719
10776
|
'ghosty channels wa groups enable <id> "Pia-cotizadora" --role equipo --dry-run',
|
|
10720
10777
|
"ghosty channels whatsapp link <id> --integration 6a3af395\u2026 --answer off --dry-run",
|
|
10721
10778
|
"ghosty channels whatsapp history <id> --integration 6a3af395\u2026 --days 7 --dry-run",
|
|
10779
|
+
"ghosty channels whatsapp history <id> --integration 6a3af395\u2026 --days 7 --media",
|
|
10722
10780
|
"ghosty channels wa import <id> --from-file pia-wa.json --own-number no --dry-run"
|
|
10723
10781
|
],
|
|
10724
10782
|
notes: [
|
|
@@ -10726,7 +10784,7 @@ HELPS.channels = {
|
|
|
10726
10784
|
"Disabling keeps logging messages; nobody answers them.",
|
|
10727
10785
|
"Groups belong to the PERSONAL WhatsApp paired to the agent (not WhatsApp Business). <group> is its name or its jid.",
|
|
10728
10786
|
"link moves a number that already lives in Formmy (EasyBits, a droplet) without redoing Meta's signup; unlink sends it back. Only the owner, an admin of the agent or Ghosty staff.",
|
|
10729
|
-
"history brings the number's recent cards, messages and orders from Formmy to the board (link it first). Re-running never duplicates: run it before the cutover and again right at it.",
|
|
10787
|
+
"history brings the number's recent cards, messages and orders from Formmy to the board (link it first). Re-running never duplicates: run it before the cutover and again right at it. Files (photos, PDFs, audio) are copied to gs too: Formmy deletes them after 7 days, so bring the number within that week; a copy that failed is retried with --media.",
|
|
10730
10788
|
"import: turn off the EasyBits socket first WITHOUT logout (two sockets knock each other off; a logout invalidates the keys). Delete the file afterwards."
|
|
10731
10789
|
],
|
|
10732
10790
|
learn: `${REF}#canales-de-whatsapp`
|
|
@@ -10763,6 +10821,14 @@ HELPS.apply = {
|
|
|
10763
10821
|
],
|
|
10764
10822
|
learn: `${REF}#el-agente-como-archivo`
|
|
10765
10823
|
};
|
|
10824
|
+
HELPS.plan = {
|
|
10825
|
+
summary: "Apply a workspace's scheduled plan now (seats, parallel agents, hosting, tokens), showing what changes first. Staff, or the owner when there's no subscription (with one, the paid invoice applies it).",
|
|
10826
|
+
usage: ["ghosty plan apply [workspace] [--dry-run] [--yes]"],
|
|
10827
|
+
flags: [["-n, --dry-run", "show what changes and the monthly total, apply nothing"], ["--yes", "skip the confirmation (required without a terminal)"]],
|
|
10828
|
+
examples: ["ghosty plan apply business --dry-run", "ghosty plan apply business --yes"],
|
|
10829
|
+
notes: ["Without a workspace it uses the one from ghosty use."],
|
|
10830
|
+
learn: `${REF}#tu-cuenta`
|
|
10831
|
+
};
|
|
10766
10832
|
HELPS.completion = {
|
|
10767
10833
|
summary: "Tab completion for your shell: commands, subcommands and flags (from this help), agent and workspace names (from your account, cached 5 min).",
|
|
10768
10834
|
usage: ["ghosty completion zsh|bash|fish"],
|
|
@@ -10824,7 +10890,14 @@ HELPS.cards = {
|
|
|
10824
10890
|
};
|
|
10825
10891
|
HELPS.board = HELPS.cards;
|
|
10826
10892
|
HELPS.conversations = HELPS.chat;
|
|
10827
|
-
HELPS.usage = {
|
|
10893
|
+
HELPS.usage = {
|
|
10894
|
+
...HELPS.me,
|
|
10895
|
+
summary: "Your plan and how much you've used this week and month. With --boxes: a workspace's boxes against its limit, and per agent.",
|
|
10896
|
+
usage: ["ghosty usage", "ghosty me usage", "ghosty usage --boxes [--workspace SLUG]"],
|
|
10897
|
+
flags: [["--boxes", "the workspace's boxes against its limit (spares, asleep) and, per agent: conversations per box, boxes, awake, spares, turns running now"], ["--workspace SLUG", "--boxes: which workspace (default: the one from ghosty use)"]],
|
|
10898
|
+
examples: ["ghosty usage", "ghosty usage --json", "ghosty usage --boxes --workspace siiqtec"],
|
|
10899
|
+
learn: `${REF}#uso`
|
|
10900
|
+
};
|
|
10828
10901
|
function helpText(cmd) {
|
|
10829
10902
|
const base = HELPS[cmd];
|
|
10830
10903
|
if (!base) return void 0;
|
|
@@ -11164,6 +11237,44 @@ ${t(`Apply ${actionable.length} change(s) to ${id}?`, `\xBFAplicar ${actionable.
|
|
|
11164
11237
|
${red(t(`Stopped at \xAB${r.failed.what}\xBB: ${serverText(r.failed.error)}`, `Se detuvo en \xAB${r.failed.what}\xBB: ${r.failed.error}`))}` : ""}`);
|
|
11165
11238
|
}
|
|
11166
11239
|
|
|
11240
|
+
// src/commands/plan.ts
|
|
11241
|
+
async function planCmd(sub, slug, v, context) {
|
|
11242
|
+
if (sub !== "apply") throw usage(t(`Unknown plan subcommand: ${sub ?? "(none)"}.`, `Subcomando desconocido de plan: ${sub ?? "(ninguno)"}.`), "ghosty plan apply <workspace> [--dry-run]");
|
|
11243
|
+
slug ??= context;
|
|
11244
|
+
if (!slug) throw usage(t("Missing workspace.", "Falta el espacio."), "ghosty plan apply <workspace> [--dry-run] (or ghosty use <workspace>)");
|
|
11245
|
+
const path = `/api/v2/workspaces/${encodeURIComponent(slug)}/plan/apply`;
|
|
11246
|
+
const plan = await api(path, { method: "POST", body: { dryRun: true } });
|
|
11247
|
+
const money = (n) => `$${n.toLocaleString("es-MX")} MXN`;
|
|
11248
|
+
const text = [
|
|
11249
|
+
`${bold(plan.workspace)} ${dim(plan.scheduledFor ? t(`scheduled for ${plan.scheduledFor.slice(0, 10)}`, `programado para el ${plan.scheduledFor.slice(0, 10)}`) : "")}`,
|
|
11250
|
+
...plan.changes.map((c) => ` ~ ${c.label}: ${String(c.from)} \u2192 ${String(c.to)}`),
|
|
11251
|
+
` ${t("monthly", "al mes")}: ${money(plan.monthlyTotal.from)} \u2192 ${money(plan.monthlyTotal.to)}`
|
|
11252
|
+
].join("\n");
|
|
11253
|
+
if (v["dry-run"]) return out(plan, () => `${text}
|
|
11254
|
+
${dim(t("Dry run: nothing applied.", "Simulaci\xF3n: no se aplic\xF3 nada."))}`);
|
|
11255
|
+
requireYesIfHeadless(v.yes, `ghosty plan apply ${slug}`);
|
|
11256
|
+
await confirm(`${text}
|
|
11257
|
+
${t("Apply this plan now?", "\xBFAplicar este plan ya?")}`, { yes: v.yes, retry: `ghosty plan apply ${slug}` });
|
|
11258
|
+
const r = await api(path, { method: "POST", body: {} });
|
|
11259
|
+
out(r, () => `${lavender("\u2713")} ${r.applied ? t(`Plan applied to ${r.workspace}.`, `Plan aplicado a ${r.workspace}.`) : t("Nothing applied.", "No se aplic\xF3 nada.")}`);
|
|
11260
|
+
}
|
|
11261
|
+
async function boxesUsage(slug) {
|
|
11262
|
+
if (!slug) throw usage(t("Which workspace?", "\xBFQu\xE9 espacio?"), "ghosty usage --boxes --workspace <slug> (or ghosty use <workspace>)");
|
|
11263
|
+
const r = await api(`/api/v2/workspaces/${encodeURIComponent(slug)}/boxes`);
|
|
11264
|
+
const rows = r.agents.map((a) => ({ agent: a.name ?? a.id, engine: a.engine, "per box": a.conversationsPerBox ?? "", boxes: a.boxes ?? "", on: a.boxesOn ?? "", spares: a.spares ?? "", "running now": a.activeTurns ?? "", ...a.error ? { note: a.error } : {} }));
|
|
11265
|
+
const head = `${bold(r.workspace)} ${dim(t(`plan: ${r.plan.parallel} in parallel \xB7 up to ${r.plan.maxBoxes} box(es) \xB7 ${r.plan.hotBoxes} always-on spare(s)`, `plan: ${r.plan.parallel} en paralelo \xB7 hasta ${r.plan.maxBoxes} caja(s) \xB7 ${r.plan.hotBoxes} spare(s) siempre encendido(s)`))}`;
|
|
11266
|
+
const space = r.space ? (r.space.used > r.space.limit ? (s) => s : dim)(
|
|
11267
|
+
t(
|
|
11268
|
+
`${r.space.used} of ${r.space.limit} box(es) in use \xB7 ${r.space.spares} spare \xB7 ${r.space.asleep} asleep${r.space.used > r.space.limit ? " \xB7 OVER the limit" : ""}`,
|
|
11269
|
+
`${r.space.used} de ${r.space.limit} caja(s) \xB7 ${r.space.spares} spare \xB7 ${r.space.asleep} dormida(s)${r.space.used > r.space.limit ? " \xB7 SOBRE el tope" : ""}`
|
|
11270
|
+
)
|
|
11271
|
+
) : dim(t("(could not read the host)", "(no pude leer el host)"));
|
|
11272
|
+
out(r, () => `${head}
|
|
11273
|
+
${space}
|
|
11274
|
+
${table(rows, ["agent", "engine", "per box", "boxes", "on", "spares", "running now"])}
|
|
11275
|
+
${dim(t(`${r.totals.activeTurns} turn(s) running`, `${r.totals.activeTurns} turno(s) corriendo`))}`);
|
|
11276
|
+
}
|
|
11277
|
+
|
|
11167
11278
|
// src/index.ts
|
|
11168
11279
|
var HELP_EN = `${bold("Usage:")} ghosty <command> [options]
|
|
11169
11280
|
|
|
@@ -11174,7 +11285,8 @@ ${bold("Session")}
|
|
|
11174
11285
|
whoami Show who you are signed in as
|
|
11175
11286
|
use [workspace] Default workspace for boards, agents create and agents ls [--clear]
|
|
11176
11287
|
completion zsh|bash|fish Tab completion for commands, flags, agents and workspaces
|
|
11177
|
-
usage Your plan and how much you've used this week and month
|
|
11288
|
+
usage Your plan and how much you've used this week and month [--boxes: boxes vs the workspace limit]
|
|
11289
|
+
plan apply [workspace] Apply a scheduled plan now, showing what changes [--dry-run]
|
|
11178
11290
|
|
|
11179
11291
|
${bold("Agents")}
|
|
11180
11292
|
agents ls List your agents
|
|
@@ -11213,7 +11325,7 @@ ${bold("Configure an agent")}
|
|
|
11213
11325
|
mcp get|set <id> MCP servers [--file servers.json]
|
|
11214
11326
|
channels whatsapp ls|enable|disable <id> [number] Which WhatsApp Business numbers it answers [--dry-run]
|
|
11215
11327
|
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]
|
|
11216
|
-
channels whatsapp history <id> --integration ID [--days 7] Bring its recent cards, messages and
|
|
11328
|
+
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]
|
|
11217
11329
|
channels wa import <id> --from-file F | --from-dir DIR --own-number yes|no Move a personal WhatsApp session here, no QR [--dry-run] [--replace] [--no-connect]
|
|
11218
11330
|
channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
|
|
11219
11331
|
channels wa status|pause <id> Personal WhatsApp: how it is / pause it (keeps the session)
|
|
@@ -11268,7 +11380,8 @@ ${bold("Sesi\xF3n")}
|
|
|
11268
11380
|
whoami Muestra con qu\xE9 cuenta entraste
|
|
11269
11381
|
use [espacio] Espacio por default para boards, agents create y agents ls [--clear]
|
|
11270
11382
|
completion zsh|bash|fish Autocompletado con TAB: comandos, opciones, agentes y espacios
|
|
11271
|
-
usage Tu plan y cu\xE1nto llevas usado esta semana y este mes
|
|
11383
|
+
usage Tu plan y cu\xE1nto llevas usado esta semana y este mes [--boxes: cajas contra el tope del espacio]
|
|
11384
|
+
plan apply [espacio] Aplica ya un plan programado, ense\xF1ando qu\xE9 cambia [--dry-run]
|
|
11272
11385
|
|
|
11273
11386
|
${bold("Agentes")}
|
|
11274
11387
|
agents ls Lista tus agentes
|
|
@@ -11307,7 +11420,7 @@ ${bold("Configurar un agente")}
|
|
|
11307
11420
|
mcp get|set <id> Servidores MCP [--file servers.json]
|
|
11308
11421
|
channels whatsapp ls|enable|disable <id> [n\xFAmero] Qu\xE9 n\xFAmeros de WhatsApp Business contesta [--dry-run]
|
|
11309
11422
|
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]
|
|
11310
|
-
channels whatsapp history <id> --integration ID [--days 7] Trae de Formmy sus tarjetas, mensajes y
|
|
11423
|
+
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]
|
|
11311
11424
|
channels wa import <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]
|
|
11312
11425
|
channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
|
|
11313
11426
|
channels wa status|pause <id> WhatsApp personal: c\xF3mo est\xE1 / pausarlo (conserva la sesi\xF3n)
|
|
@@ -11353,7 +11466,7 @@ ${bold("Opciones globales")}
|
|
|
11353
11466
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
11354
11467
|
${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.")}
|
|
11355
11468
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
11356
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
11469
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "partner", "me", "channels", "board", "boards", "credentials", "docs", "help"];
|
|
11357
11470
|
var AGENTS_SUBS = ["ls", "create", "get", "set", "export", "doctor", "docs", "share", "unshare", "shares", "rm", "restart", "demo", "box"];
|
|
11358
11471
|
function unknown(what, input, known, helpCmd) {
|
|
11359
11472
|
const s = suggest(input, known);
|
|
@@ -11383,6 +11496,7 @@ async function main() {
|
|
|
11383
11496
|
const langAt = process.argv.findIndex((a) => a === "--lang" || a.startsWith("--lang="));
|
|
11384
11497
|
if (langAt >= 0) setLang(detectLang(process.argv[langAt].includes("=") ? process.argv[langAt].split("=")[1] : process.argv[langAt + 1]));
|
|
11385
11498
|
const { values: v, positionals: pos } = parseArgs({
|
|
11499
|
+
args: bareMediaFlag(process.argv.slice(2)),
|
|
11386
11500
|
allowPositionals: true,
|
|
11387
11501
|
strict: true,
|
|
11388
11502
|
options: {
|
|
@@ -11453,6 +11567,9 @@ async function main() {
|
|
|
11453
11567
|
full: { type: "boolean" },
|
|
11454
11568
|
"as-formmy": { type: "boolean" },
|
|
11455
11569
|
"key-column": { type: "string" },
|
|
11570
|
+
"unset-env": { type: "string" },
|
|
11571
|
+
keep: { type: "boolean" },
|
|
11572
|
+
boxes: { type: "boolean" },
|
|
11456
11573
|
like: { type: "string" },
|
|
11457
11574
|
"copy-dbs": { type: "boolean" },
|
|
11458
11575
|
"no-notify": { type: "boolean" },
|
|
@@ -11512,7 +11629,7 @@ async function main() {
|
|
|
11512
11629
|
const tokens = splitTokens(pos, v);
|
|
11513
11630
|
globals.token = tokens.session;
|
|
11514
11631
|
globals.profile = v.profile;
|
|
11515
|
-
if (v.version) return out({ version: "0.
|
|
11632
|
+
if (v.version) return out({ version: "0.23.0" }, () => "0.23.0");
|
|
11516
11633
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
11517
11634
|
const context = (await readConfig()).workspace;
|
|
11518
11635
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -11613,7 +11730,10 @@ async function main() {
|
|
|
11613
11730
|
case "partner":
|
|
11614
11731
|
return partnerCmd(sub, a1, a2, { ...v, token: tokens.turn });
|
|
11615
11732
|
case "usage":
|
|
11733
|
+
if (v.boxes) return boxesUsage(v.workspace ?? context);
|
|
11616
11734
|
return meUsage();
|
|
11735
|
+
case "plan":
|
|
11736
|
+
return planCmd(sub, a1, v, context);
|
|
11617
11737
|
case "me":
|
|
11618
11738
|
if (sub === "files") return meFiles(a1, a2, v);
|
|
11619
11739
|
if (sub === "usage") return meUsage();
|
|
@@ -11628,7 +11748,7 @@ async function main() {
|
|
|
11628
11748
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
11629
11749
|
}
|
|
11630
11750
|
}
|
|
11631
|
-
var notice = updateNotice("0.
|
|
11751
|
+
var notice = updateNotice("0.23.0").catch(() => null);
|
|
11632
11752
|
main().then(
|
|
11633
11753
|
async () => {
|
|
11634
11754
|
const n = await notice;
|
package/package.json
CHANGED