@ghostystudio/cli 0.85.0 → 0.87.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 +7 -0
- package/dist/index.js +164 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,6 +74,8 @@ ghosty channels whatsapp ls|enable|disable <id> [number] [--dry-run]
|
|
|
74
74
|
ghosty channels whatsapp link|unlink <id> --integration ID --answer on|off [--forward] [--dry-run] # bring a WABA number from Formmy
|
|
75
75
|
ghosty channels whatsapp history <id> --integration ID [--days 7] [--dry-run] # its recent cards, messages and orders (no duplicates)
|
|
76
76
|
ghosty channels whatsapp|messenger connect <id> # opens Channels to connect with Meta (browser sign-in)
|
|
77
|
+
ghosty channels messenger status <id> # each page: who answers, last webhook, token health
|
|
78
|
+
ghosty channels messenger on|off <id> <page> [--take] # this agent answers the page, or stops (off keeps logging)
|
|
77
79
|
ghosty channels mteams connect <id> --app-id <guid> --tenant <guid> # Microsoft Teams bot (secret in GHOSTY_MTEAMS_SECRET); ls shows endpoint and last problem
|
|
78
80
|
ghosty channels wa import <id> --from-file F | --from-dir DIR --own-number yes|no [--dry-run] # personal WhatsApp session, no QR
|
|
79
81
|
ghosty channels wa pair|status|pause <id> [--phone N]
|
|
@@ -105,6 +107,7 @@ ghosty watch add <id> <url> [--every 24h] [--focus "heading"] [--instruction "
|
|
|
105
107
|
ghosty watch ls <id>
|
|
106
108
|
ghosty watch check <id> <watch-id> [--dry-run]
|
|
107
109
|
ghosty watch rm|resume <id> <watch-id>
|
|
110
|
+
ghosty tts "Hola, te aviso por WhatsApp." [--voice em_santa|em_alex|ef_dora] [--out voz.wav] # the house voice (Mexican Spanish + pronunciation lexicon)
|
|
108
111
|
ghosty teams post <workspace>#<room>[/<thread-id>] "msg" [--wait] # write there as you; any @agent it mentions answers
|
|
109
112
|
|
|
110
113
|
ghosty me files ls|get|export|upload|rm [--q TEXT] [--kind image|video|audio|document|other] [--cursor C]
|
|
@@ -150,6 +153,10 @@ Every command has its own help with examples: `ghosty <command> --help` or `ghos
|
|
|
150
153
|
|
|
151
154
|
Exit codes: `0` ok · `1` API error · `2` usage error · `3` not signed in.
|
|
152
155
|
|
|
156
|
+
### Changes in 0.86.0
|
|
157
|
+
|
|
158
|
+
- `ghosty tts "text" [--voice em_santa|em_alex|ef_dora] [--out FILE]`: hear the house voice (Kokoro, Mexican Spanish, with Ghosty's pronunciation lexicon) say a text, saved as a .wav. Up to 400 characters.
|
|
159
|
+
|
|
153
160
|
### Changes in 0.84.0
|
|
154
161
|
|
|
155
162
|
- `coupons create|set` take `--from` (the lent plan starts then), `--redeem-by` (last moment to redeem) and `--convert-promo` (Stripe promotion code offered in /c/uso when the lent plan is about to end). New `coupons show <CODE>` (staff): who redeemed it, their plan window and who converted to a paid plan. Codes are accent- and case-insensitive.
|
package/dist/index.js
CHANGED
|
@@ -1175,7 +1175,7 @@ async function postLong(path2, body, timeoutMs = 9e5) {
|
|
|
1175
1175
|
const req = httpsRequest(`${base}${path2}`, {
|
|
1176
1176
|
method: "POST",
|
|
1177
1177
|
timeout: timeoutMs,
|
|
1178
|
-
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.
|
|
1178
|
+
headers: { authorization: `Bearer ${token}`, "user-agent": `ghosty-cli/${"0.87.0"}`, "content-type": "application/json", "content-length": Buffer.byteLength(data) }
|
|
1179
1179
|
}, (res) => {
|
|
1180
1180
|
let buf = "";
|
|
1181
1181
|
res.on("data", (c) => buf += c);
|
|
@@ -1217,7 +1217,7 @@ async function request(path2, opts = {}, retried = false) {
|
|
|
1217
1217
|
method: opts.method ?? (opts.body !== void 0 || opts.raw !== void 0 ? "POST" : "GET"),
|
|
1218
1218
|
headers: {
|
|
1219
1219
|
authorization: `Bearer ${token}`,
|
|
1220
|
-
"user-agent": `ghosty-cli/${"0.
|
|
1220
|
+
"user-agent": `ghosty-cli/${"0.87.0"}`,
|
|
1221
1221
|
"accept-language": lang(),
|
|
1222
1222
|
...opts.body !== void 0 ? { "content-type": "application/json" } : {},
|
|
1223
1223
|
...opts.headers
|
|
@@ -1264,7 +1264,7 @@ var mteams_exports = {};
|
|
|
1264
1264
|
__export(mteams_exports, {
|
|
1265
1265
|
mteamsCmd: () => mteamsCmd
|
|
1266
1266
|
});
|
|
1267
|
-
import { writeFile as
|
|
1267
|
+
import { writeFile as writeFile9 } from "node:fs/promises";
|
|
1268
1268
|
function list(bots) {
|
|
1269
1269
|
return bots.length ? table(
|
|
1270
1270
|
bots.map((b) => ({
|
|
@@ -1306,7 +1306,7 @@ ${dim(`${t("Next", "Sigue")}: ghosty channels mteams zip ${id} ${b?.id ?? "<bot-
|
|
|
1306
1306
|
const res = await request(`${path2}/${enc10(botId)}/zip`);
|
|
1307
1307
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
1308
1308
|
const file = v.out ?? "ghosty-teams.zip";
|
|
1309
|
-
await
|
|
1309
|
+
await writeFile9(file, bytes);
|
|
1310
1310
|
return out({ saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${t(`Saved ${file}: upload it in Teams admin \u2192 Manage apps.`, `Se guard\xF3 ${file}: s\xFAbelo en Teams admin \u2192 Administrar apps.`)}`);
|
|
1311
1311
|
}
|
|
1312
1312
|
case "on":
|
|
@@ -11237,7 +11237,7 @@ async function call(path2, v, init = {}) {
|
|
|
11237
11237
|
"x-ghosty-ts": ts,
|
|
11238
11238
|
"x-ghosty-sig": sig,
|
|
11239
11239
|
"content-type": "application/json",
|
|
11240
|
-
"user-agent": `ghosty-cli/${"0.
|
|
11240
|
+
"user-agent": `ghosty-cli/${"0.87.0"}`
|
|
11241
11241
|
},
|
|
11242
11242
|
body: method === "GET" ? void 0 : raw
|
|
11243
11243
|
});
|
|
@@ -11571,6 +11571,28 @@ async function imageCmd(sub, a1, a2, v) {
|
|
|
11571
11571
|
);
|
|
11572
11572
|
}
|
|
11573
11573
|
|
|
11574
|
+
// src/commands/tts.ts
|
|
11575
|
+
init_api();
|
|
11576
|
+
init_util();
|
|
11577
|
+
init_i18n();
|
|
11578
|
+
import { writeFile as writeFile7 } from "node:fs/promises";
|
|
11579
|
+
var MAX_CHARS = 400;
|
|
11580
|
+
var VOICES = ["em_santa", "em_alex", "ef_dora"];
|
|
11581
|
+
async function ttsCmd(text, v) {
|
|
11582
|
+
const ex = 'ghosty tts "Hola, gracias por escribir." [--voice em_santa|em_alex|ef_dora] [--out voz.wav]';
|
|
11583
|
+
const said = text?.trim();
|
|
11584
|
+
if (!said) throw usage(t("Missing the text to say.", "Falta el texto a decir."), ex);
|
|
11585
|
+
if (said.length > MAX_CHARS) throw usage(t(`Too long: ${said.length} characters (max ${MAX_CHARS}). Split it into sentences.`, `Demasiado largo: ${said.length} caracteres (m\xE1x. ${MAX_CHARS}). P\xE1rtelo en frases.`), ex);
|
|
11586
|
+
const voice = v.voice ?? "em_santa";
|
|
11587
|
+
if (!VOICES.includes(voice)) throw usage(t(`Unknown voice: ${voice}.`, `Voz desconocida: ${voice}.`), `${t("Voices", "Voces")}: ${VOICES.join(", ")}`);
|
|
11588
|
+
const res = await request("/api/v2/me/tts", { body: { text: said, voice } });
|
|
11589
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
11590
|
+
const file = v.out ?? "voz.wav";
|
|
11591
|
+
await writeFile7(file, bytes);
|
|
11592
|
+
const durationMs = Number(res.headers.get("x-duration-ms") ?? 0);
|
|
11593
|
+
return out({ saved: file, voice, bytes: bytes.length, durationMs }, () => `${lavender("\u2713")} ${file} (${(durationMs / 1e3).toFixed(1)} s) ${dim(t(`\xB7 play it: afplay ${file}`, `\xB7 esc\xFAchalo: afplay ${file}`))}`);
|
|
11594
|
+
}
|
|
11595
|
+
|
|
11574
11596
|
// src/commands/watch.ts
|
|
11575
11597
|
init_api();
|
|
11576
11598
|
init_util();
|
|
@@ -11652,7 +11674,7 @@ ${dim(`id ${w.id} \xB7 ${t("conversation", "conversaci\xF3n")} ${w.sessionId} \x
|
|
|
11652
11674
|
|
|
11653
11675
|
// src/commands/data.ts
|
|
11654
11676
|
init_api();
|
|
11655
|
-
import { readdir as readdir2, readFile as readFile6, writeFile as
|
|
11677
|
+
import { readdir as readdir2, readFile as readFile6, writeFile as writeFile8 } from "node:fs/promises";
|
|
11656
11678
|
import { basename as basename3, join as join5 } from "node:path";
|
|
11657
11679
|
|
|
11658
11680
|
// src/rows.ts
|
|
@@ -11821,7 +11843,7 @@ async function dbsExport(id, db, v) {
|
|
|
11821
11843
|
const suggested = /filename="([^"]+)"/.exec(res.headers.get("content-disposition") ?? "")?.[1] ?? (format === "csv" ? `${db}-${v.table}.csv` : `${db}.sqlite`);
|
|
11822
11844
|
const file = v.out ?? suggested;
|
|
11823
11845
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
11824
|
-
await
|
|
11846
|
+
await writeFile8(file, bytes);
|
|
11825
11847
|
const countsHeader = res.headers.get("x-gs-row-counts");
|
|
11826
11848
|
const counts = countsHeader ? JSON.parse(Buffer.from(countsHeader, "base64").toString("utf8")) : {};
|
|
11827
11849
|
const rows = Object.entries(counts).map(([tb, n]) => ({ table: tb, rows: n }));
|
|
@@ -12557,7 +12579,9 @@ async function channelsCmd(kind, sub, id, ref, v) {
|
|
|
12557
12579
|
}
|
|
12558
12580
|
if (kind === "messenger" || kind === "fb") {
|
|
12559
12581
|
if (!id) throw usage(t("Missing agent id.", "Falta el id del agente."), t("Example", "Ejemplo") + ": ghosty channels messenger ls <agent-id> [--orphans]");
|
|
12560
|
-
if (sub
|
|
12582
|
+
if (sub === "status") return messengerStatusCmd(id);
|
|
12583
|
+
if (sub === "on" || sub === "off") return messengerToggleCmd(id, ref, sub === "on", v);
|
|
12584
|
+
if (sub && sub !== "ls" && sub !== "list") throw usage(t(`Unknown subcommand: ${sub}.`, `Subcomando desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty channels messenger ls|status|on|off <agent-id> [page]");
|
|
12561
12585
|
return linksCmd(id, "messenger", v.orphans);
|
|
12562
12586
|
}
|
|
12563
12587
|
if (kind !== "whatsapp" && kind !== "wa") throw usage(t(`Unknown channel: ${kind ?? "(none)"}.`, `Canal desconocido: ${kind ?? "(ninguno)"}.`), t("Try", "Prueba") + ": ghosty channels whatsapp ls <agent-id>");
|
|
@@ -12799,6 +12823,41 @@ async function pageWithRetry(fetchPage, where, waitMs = (n) => 5e3 * n, tries =
|
|
|
12799
12823
|
}
|
|
12800
12824
|
}
|
|
12801
12825
|
var lastCell = (l) => l ? `${l.agentName ?? l.agentId} ${dim(l.at.slice(0, 16).replace("T", " "))}` : dim("\u2014");
|
|
12826
|
+
function findPage(pages, ref) {
|
|
12827
|
+
const hit = pages.find((p) => p.pageId === ref) ?? pages.find((p) => (p.name ?? "").toLowerCase() === ref.toLowerCase());
|
|
12828
|
+
if (!hit) throw usage(t(`No Messenger page "${ref}" for this agent.`, `Este agente no tiene la p\xE1gina de Messenger "${ref}".`), pages.length ? `${t("Available", "Disponibles")}: ${pages.map((p) => p.name ?? p.pageId).join(", ")}` : t("Connect one first in the web (the agent's Channels page).", "Primero conecta una en la web (p\xE1gina Canales del agente)."));
|
|
12829
|
+
return hit;
|
|
12830
|
+
}
|
|
12831
|
+
var ago = (iso) => {
|
|
12832
|
+
if (!iso) return dim(t("never", "nunca"));
|
|
12833
|
+
const h = (Date.now() - new Date(iso).getTime()) / 36e5;
|
|
12834
|
+
return h < 1 ? t("< 1 h ago", "hace < 1 h") : h < 48 ? t(`${Math.round(h)} h ago`, `hace ${Math.round(h)} h`) : t(`${Math.round(h / 24)} d ago`, `hace ${Math.round(h / 24)} d`);
|
|
12835
|
+
};
|
|
12836
|
+
async function messengerStatusCmd(id) {
|
|
12837
|
+
const r = await api(`/api/v2/agents/${enc11(id)}/messenger?health=1`);
|
|
12838
|
+
const health = (p) => !p.health ? dim("?") : p.health.ok ? p.health.subscribed ? t("ok", "ok") : lavender(t("not subscribed: reconnect it", "sin suscripci\xF3n: recon\xE9ctala")) : lavender(p.health.code === 190 ? t("token dead: reconnect it", "token muerto: recon\xE9ctala") : p.health.message);
|
|
12839
|
+
const rows = r.pages.map((p) => ({
|
|
12840
|
+
page: p.name ?? p.pageId,
|
|
12841
|
+
answers: p.answers === "this" ? lavender(t("this agent", "este agente")) : p.answers === "other" ? t("another agent", "otro agente") : dim(t("nobody (records only)", "nadie (s\xF3lo registra)")),
|
|
12842
|
+
"last webhook": ago(p.lastWebhookAt),
|
|
12843
|
+
health: health(p),
|
|
12844
|
+
id: p.pageId
|
|
12845
|
+
}));
|
|
12846
|
+
return out(r, () => rows.length ? table(rows, ["page", "answers", "last webhook", "health", "id"]) : dim(t("No Messenger pages yet. Connect one in the agent's Channels page.", "Todav\xEDa no hay p\xE1ginas de Messenger. Conecta una en la p\xE1gina Canales del agente.")));
|
|
12847
|
+
}
|
|
12848
|
+
async function messengerToggleCmd(id, ref, on, v) {
|
|
12849
|
+
if (!ref) throw usage(t("Missing the page (id or name).", "Falta la p\xE1gina (id o nombre)."), t("Example", "Ejemplo") + `: ghosty channels messenger ${on ? "on" : "off"} ${id} <page-id>`);
|
|
12850
|
+
const cur = await api(`/api/v2/agents/${enc11(id)}/messenger`);
|
|
12851
|
+
const page = findPage(cur.pages, ref);
|
|
12852
|
+
const label = page.name ?? page.pageId;
|
|
12853
|
+
const plan = on ? page.answers === "other" && !v.take ? null : t(`This agent will answer the Messenger and comments of "${label}" (real customers).`, `Este agente va a contestar el Messenger y los comentarios de \xAB${label}\xBB (clientes reales).`) : t(`Nobody will answer "${label}"; messages keep landing on the board.`, `Nadie va a contestar \xAB${label}\xBB; los mensajes se siguen registrando en el tablero.`);
|
|
12854
|
+
if (!plan) throw usage(t(`Another agent answers "${label}".`, `Otro agente contesta \xAB${label}\xBB.`), t("Add --take to move it to this agent.", "Agrega --take para pasarla a este agente."));
|
|
12855
|
+
if (v["dry-run"]) return out({ dryRun: true, pageId: page.pageId, on }, () => `${plan}
|
|
12856
|
+
${dim(t("(dry run: nothing changed)", "(dry run: no se cambi\xF3 nada)"))}`);
|
|
12857
|
+
if (on) await confirm(`${plan} ${t("Continue?", "\xBFSeguir?")}`, { yes: v.yes, retry: `ghosty channels messenger on ${id} ${page.pageId}${v.take ? " --take" : ""}` });
|
|
12858
|
+
const r = await api(`/api/v2/agents/${enc11(id)}/messenger`, { method: "POST", body: { pageId: page.pageId, on, ...v.take ? { take: true } : {} } });
|
|
12859
|
+
return out(r, () => on ? t(`Now answering "${label}".`, `Ya contesta \xAB${label}\xBB.`) : t(`No longer answering "${label}"; messages are still logged.`, `Ya no contesta \xAB${label}\xBB; los mensajes se siguen registrando.`));
|
|
12860
|
+
}
|
|
12802
12861
|
async function linksCmd(id, kind, orphans) {
|
|
12803
12862
|
const r = await api(`/api/v2/agents/${enc11(id)}/channels/links?kind=${kind}${orphans ? "&orphans=1" : ""}`);
|
|
12804
12863
|
const now = (l) => l.answersNow ? l.answersNow.agentName ?? l.answersNow.agentId : l.recordsOnly ? t("nobody (records only)", "nadie (s\xF3lo registra)") : l.via === "easybits" ? "EasyBits" : l.via === "partner" ? "partner" : lavender(t("nobody", "nadie"));
|
|
@@ -13052,11 +13111,13 @@ init_api();
|
|
|
13052
13111
|
init_util();
|
|
13053
13112
|
init_i18n();
|
|
13054
13113
|
var enc12 = encodeURIComponent;
|
|
13055
|
-
|
|
13114
|
+
var FOLLOWUP_ACTIONS = /* @__PURE__ */ new Set(["on", "off", "template", "dry-run"]);
|
|
13115
|
+
async function boardsCmd(sub, a1, a2, v, context, a3) {
|
|
13056
13116
|
if (context && sub !== "assign") {
|
|
13057
13117
|
if ((sub === "ls" || sub === "list" || !sub || sub === "move-cards") && !a1) a1 = context;
|
|
13058
13118
|
else if ((sub === "create" || sub === "rm") && a1 && !a2) [a1, a2] = [context, a1];
|
|
13059
13119
|
else if (sub === "access" && !a2) [a1, a2] = [context, a1 ?? "main"];
|
|
13120
|
+
else if (sub === "followups" && (!a1 || FOLLOWUP_ACTIONS.has(a1))) [a1, a2, a3] = [context, void 0, a1];
|
|
13060
13121
|
}
|
|
13061
13122
|
switch (sub ?? "ls") {
|
|
13062
13123
|
case "ls":
|
|
@@ -13123,8 +13184,37 @@ ${table(r.people.map((p) => ({ email: p.email, until: fmtUntil(p.until) })), ["e
|
|
|
13123
13184
|
return [head + people, ...changes].join("\n");
|
|
13124
13185
|
});
|
|
13125
13186
|
}
|
|
13187
|
+
// Seguimiento de cotizaciones del tablero: estado, prender/apagar, plantilla y prueba sin enviar.
|
|
13188
|
+
case "followups": {
|
|
13189
|
+
if (!a1) throw usage(t("Missing workspace.", "Falta el espacio."), t("Example", "Ejemplo") + ": ghosty boards followups <workspace> [board|main] [on|off|template|dry-run]");
|
|
13190
|
+
if (a2 && FOLLOWUP_ACTIONS.has(a2) && !a3) [a2, a3] = [void 0, a2];
|
|
13191
|
+
const board = a2 ?? "main";
|
|
13192
|
+
const path2 = `/api/v2/workspaces/${enc12(a1)}/boards/followups`;
|
|
13193
|
+
const steps = v.steps ? v.steps.split(",").map((x) => Number(x.trim())).filter((n) => n > 0) : void 0;
|
|
13194
|
+
const minutes = v.minutes === void 0 ? void 0 : v.minutes === "on";
|
|
13195
|
+
if (a3 === "dry-run") {
|
|
13196
|
+
const r2 = await api(path2, { method: "POST", body: { board, intent: "dry-run" } });
|
|
13197
|
+
return out(r2, () => r2.items.length ? table(r2.items.map((i) => ({ folio: i.folio, toque: i.step + 1, action: i.action, detail: i.detail })), ["folio", "toque", "action", "detail"]) : dim(t("Nothing due right now.", "Nada que toque ahora.")));
|
|
13198
|
+
}
|
|
13199
|
+
if (a3 === "template") {
|
|
13200
|
+
const r2 = await api(path2, { method: "POST", body: { board, intent: "create-template" } });
|
|
13201
|
+
return out(r2, () => t("Template sent to Meta for review (utility).", "Plantilla enviada a revisi\xF3n de Meta (utilidad)."));
|
|
13202
|
+
}
|
|
13203
|
+
if (a3 === "on" || a3 === "off" || steps || minutes !== void 0) {
|
|
13204
|
+
await api(path2, { method: "POST", body: { board, intent: "set", ...a3 ? { enabled: a3 === "on" } : {}, ...steps ? { steps } : {}, ...minutes !== void 0 ? { testMinutes: minutes } : {} } });
|
|
13205
|
+
} else if (a3) throw usage(t(`Unknown action: ${a3}.`, `Acci\xF3n desconocida: ${a3}.`), "on | off | template | dry-run");
|
|
13206
|
+
const r = await api(`${path2}?board=${enc12(board)}`);
|
|
13207
|
+
return out(r, () => {
|
|
13208
|
+
const c = r.config;
|
|
13209
|
+
const head = `\xAB${r.board}\xBB: ${c.enabled ? lavender(t("on", "prendido")) : t("off", "apagado")} \xB7 ${t("touches at", "toques a los")} ${c.steps.join(", ")} ${c.testMinutes ? "min" : t("days", "d\xEDas")} \xB7 ${c.template} ${dim(`(${r.template.status ?? r.template.error ?? t("not in Meta", "sin dar de alta en Meta")})`)}`;
|
|
13210
|
+
const s2 = r.stats;
|
|
13211
|
+
const nums = `${s2.active} ${t("active", "en curso")} \xB7 ${s2.sent} ${t("sent", "enviados")} \xB7 ${s2.replied} ${t("replied", "contestaron")} \xB7 ${s2.paid} ${t("paid/closed", "pagaron o cerraron")} \xB7 ${s2.done} ${t("no reply", "sin respuesta")}`;
|
|
13212
|
+
const rows = r.recent.map((x) => ({ folio: x.folio, name: x.nombre ?? "", step: x.step, status: x.stop_reason ? `${x.status}: ${x.stop_reason}` : x.status, next: x.next_at ? new Date(x.next_at * 1e3).toISOString().slice(0, 16) : "" }));
|
|
13213
|
+
return [head, nums, rows.length ? table(rows, ["folio", "name", "step", "status", "next"]) : ""].filter(Boolean).join("\n");
|
|
13214
|
+
});
|
|
13215
|
+
}
|
|
13126
13216
|
default:
|
|
13127
|
-
throw usage(t(`Unknown boards subcommand: ${sub}.`, `Subcomando de boards desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty boards ls | create | assign | move-cards | access | rm");
|
|
13217
|
+
throw usage(t(`Unknown boards subcommand: ${sub}.`, `Subcomando de boards desconocido: ${sub}.`), t("Try", "Prueba") + ": ghosty boards ls | create | assign | move-cards | access | followups | rm");
|
|
13128
13218
|
}
|
|
13129
13219
|
}
|
|
13130
13220
|
|
|
@@ -13432,6 +13522,16 @@ var HELPS_ES = {
|
|
|
13432
13522
|
summary: "Ghosty para Chrome: cerrar la sesi\xF3n del navegador.",
|
|
13433
13523
|
notes: ["Revoca todos los tokens de Ghosty para Chrome que tengan tus agentes de terminal (Claude Code, CLI, @ghostystudio/browser-mcp) y desconecta la extensi\xF3n. Para volver, copia un comando nuevo del panel de la extensi\xF3n."]
|
|
13434
13524
|
},
|
|
13525
|
+
tts: {
|
|
13526
|
+
summary: "Oye la voz de la casa decir un texto (Kokoro, espa\xF1ol de M\xE9xico, con el l\xE9xico de pronunciaci\xF3n de Ghosty): la misma de las notas de voz. Guarda un .wav.",
|
|
13527
|
+
usage: ['ghosty tts "texto" [--voice em_santa|em_alex|ef_dora] [--out ARCHIVO]'],
|
|
13528
|
+
flags: [
|
|
13529
|
+
["--voice V", "em_santa (hombre, la de la casa, default), em_alex (hombre) o ef_dora (mujer)"],
|
|
13530
|
+
["--out FILE", "d\xF3nde guardar el .wav (default voz.wav)"]
|
|
13531
|
+
],
|
|
13532
|
+
examples: ['ghosty tts "Hola, gracias por escribir. Te aviso por WhatsApp."', 'ghosty tts "Tu cita qued\xF3 el jueves a las cinco." --voice ef_dora --out cita.wav && afplay cita.wav'],
|
|
13533
|
+
notes: ["Hasta 400 caracteres y 60 frases por minuto. Las marcas (WhatsApp, Claude, GitHub, ghosty.studio\u2026) se escriben normal: el l\xE9xico corrige c\xF3mo suenan."]
|
|
13534
|
+
},
|
|
13435
13535
|
image: {
|
|
13436
13536
|
summary: "Im\xE1genes con IA (Gemini \xABNano Banana Pro\xBB) para los negocios de tu espacio de partner: generar, editar una que ya existe o componer con referencias; cobradas del saldo del espacio.",
|
|
13437
13537
|
replace: [
|
|
@@ -13613,13 +13713,16 @@ var HELPS_ES = {
|
|
|
13613
13713
|
["--from / --to B", "move-cards: tableros por nombre, id o main"],
|
|
13614
13714
|
["--integration ID | --phone N | --all", "move-cards: qu\xE9 tarjetas (el filtro es obligatorio)"],
|
|
13615
13715
|
["-n, --dry-run", "move-cards: cuenta y muestra una muestra, no cambia nada"],
|
|
13616
|
-
["--yes", "move-cards: se salta la confirmaci\xF3n (obligatorio sin terminal)"]
|
|
13716
|
+
["--yes", "move-cards: se salta la confirmaci\xF3n (obligatorio sin terminal)"],
|
|
13717
|
+
["--steps 2,7,14", "followups: d\xEDas despu\xE9s de la cotizaci\xF3n de cada toque (hasta 5)"],
|
|
13718
|
+
["--minutes on|off", "followups (staff): los pasos cuentan en minutos, para probar sin esperar d\xEDas"]
|
|
13617
13719
|
],
|
|
13618
13720
|
notes: [
|
|
13619
13721
|
"assign cambia a d\xF3nde van las tarjetas NUEVAS; move-cards trae las que ya existen (hilo, pedidos, actividad) con folio nuevo. Una tarjeta cuya conversaci\xF3n ya est\xE1 en el tablero destino se salta.",
|
|
13620
13722
|
"Leer requiere ser miembro; cambiar requiere ser due\xF1o o staff del espacio.",
|
|
13621
13723
|
"rm s\xF3lo borra un tablero vac\xEDo que no sea el principal.",
|
|
13622
|
-
"access sin flags dice qui\xE9n ve el tablero; due\xF1o y staff ven todos. Por qu\xE9 alguien lo ve: ghosty access why <correo> --workspace <espacio> --board <tablero>."
|
|
13724
|
+
"access sin flags dice qui\xE9n ve el tablero; due\xF1o y staff ven todos. Por qu\xE9 alguien lo ve: ghosty access why <correo> --workspace <espacio> --board <tablero>.",
|
|
13725
|
+
"followups: cada cotizaci\xF3n que manda el agente recibe hasta 3 toques por WhatsApp (2, 7 y 14 d\xEDas) escritos con la pl\xE1tica; paran si el cliente contesta, paga o alguien toma la pl\xE1tica. Fuera de la ventana de 24 h usa la plantilla de utilidad (template = mandarla a Meta). Cambiarlo pide el mismo permiso que mandar campa\xF1as."
|
|
13623
13726
|
]
|
|
13624
13727
|
},
|
|
13625
13728
|
cards: {
|
|
@@ -14261,6 +14364,16 @@ var HELPS = {
|
|
|
14261
14364
|
notes: ["Revokes every Ghosty for Chrome token your terminal agents hold (Claude Code, CLI, @ghostystudio/browser-mcp) and disconnects the extension. Copy a new command from the extension panel to reconnect."],
|
|
14262
14365
|
learn: `${REF}#navegador`
|
|
14263
14366
|
},
|
|
14367
|
+
tts: {
|
|
14368
|
+
summary: "Hear the house voice say a text (Kokoro, Mexican Spanish, with Ghosty's pronunciation lexicon): the same voice as the voice notes. Saves a .wav.",
|
|
14369
|
+
usage: ['ghosty tts "text" [--voice em_santa|em_alex|ef_dora] [--out FILE]'],
|
|
14370
|
+
flags: [
|
|
14371
|
+
["--voice V", "em_santa (man, the house voice, default), em_alex (man) or ef_dora (woman)"],
|
|
14372
|
+
["--out FILE", "where to save the .wav (default voz.wav)"]
|
|
14373
|
+
],
|
|
14374
|
+
examples: ['ghosty tts "Hola, gracias por escribir. Te aviso por WhatsApp."', 'ghosty tts "Tu cita qued\xF3 el jueves a las cinco." --voice ef_dora --out cita.wav && afplay cita.wav'],
|
|
14375
|
+
notes: ["Up to 400 characters and 60 sentences per minute. Brand names (WhatsApp, Claude, GitHub, ghosty.studio\u2026) are written normally: the lexicon fixes how they sound."]
|
|
14376
|
+
},
|
|
14264
14377
|
image: {
|
|
14265
14378
|
summary: "AI images (Gemini \xABNano Banana Pro\xBB) for your partner businesses: generate, edit an existing one, or compose from references; charged to the space's balance.",
|
|
14266
14379
|
usage: [
|
|
@@ -14369,6 +14482,8 @@ HELPS.channels = {
|
|
|
14369
14482
|
"ghosty channels whatsapp disable <agent-id> <number> [--dry-run]",
|
|
14370
14483
|
"ghosty channels whatsapp integrations <agent-id> [--email OTHER] [--orphans]",
|
|
14371
14484
|
"ghosty channels messenger ls <agent-id> [--orphans]",
|
|
14485
|
+
"ghosty channels messenger status <agent-id> # who answers each page, last webhook, token health",
|
|
14486
|
+
"ghosty channels messenger on|off <agent-id> <page> [--take] [--dry-run] [--yes]",
|
|
14372
14487
|
"ghosty channels whatsapp|messenger connect <agent-id> [--no-open] # opens Channels: Meta sign-in in the browser",
|
|
14373
14488
|
"ghosty channels whatsapp link <agent-id> --integration <formmy-id> --answer on|off [--forward] [--dry-run] [--yes]",
|
|
14374
14489
|
"ghosty channels whatsapp unlink <agent-id> --integration <formmy-id> [--dry-run] [--yes]",
|
|
@@ -14503,7 +14618,8 @@ HELPS.boards = {
|
|
|
14503
14618
|
"ghosty boards assign <agent-id> <board|main>",
|
|
14504
14619
|
"ghosty boards move-cards <workspace> --from <board> --to <board> [--integration ID | --phone N | --all] [--dry-run] [--yes]",
|
|
14505
14620
|
'ghosty boards rm <workspace> "Name" [--yes]',
|
|
14506
|
-
"ghosty boards access <workspace> [board|main] [--add EMAIL] [--remove EMAIL] [--restrict] [--everyone] [--until 2026-10-28|14d]"
|
|
14621
|
+
"ghosty boards access <workspace> [board|main] [--add EMAIL] [--remove EMAIL] [--restrict] [--everyone] [--until 2026-10-28|14d]",
|
|
14622
|
+
"ghosty boards followups <workspace> [board|main] [on|off|template|dry-run] [--steps 2,7,14] [--minutes on|off]"
|
|
14507
14623
|
],
|
|
14508
14624
|
flags: [
|
|
14509
14625
|
["--add / --remove EMAIL", "access: add to or take off the board's \xABonly these people\xBB list (repeatable or comma-separated; only workspace members)"],
|
|
@@ -14514,10 +14630,12 @@ HELPS.boards = {
|
|
|
14514
14630
|
["--from / --to B", "move-cards: boards by name, id or main"],
|
|
14515
14631
|
["--integration ID | --phone N | --all", "move-cards: which cards (a filter is required)"],
|
|
14516
14632
|
["-n, --dry-run", "move-cards: count and show a sample, change nothing"],
|
|
14517
|
-
["--yes", "move-cards: skip the confirmation (required without a terminal)"]
|
|
14633
|
+
["--yes", "move-cards: skip the confirmation (required without a terminal)"],
|
|
14634
|
+
["--steps 2,7,14", "followups: days after the quote for each touch (up to 5)"],
|
|
14635
|
+
["--minutes on|off", "followups (staff): count the steps in minutes, to test without waiting days"]
|
|
14518
14636
|
],
|
|
14519
|
-
examples: ["ghosty boards ls siiqtec", 'ghosty boards create siiqtec "Siiqtec - Ixmiquilpan" --columns-from main', 'ghosty boards assign <agent-id> "Siiqtec - Ixmiquilpan"', 'ghosty boards move-cards siiqtec --from main --to "Siiqtec - Ixmiquilpan" --integration 6a3b02ad\u2026 --dry-run'],
|
|
14520
|
-
notes: ["assign changes where NEW cards go; move-cards brings the existing ones (thread, orders, activity) with a new folio. A card whose conversation is already on the target board is skipped.", "Reading needs membership; changing needs owner or staff of the workspace.", "rm only deletes an empty board that is not the main one.", "access without flags shows who sees the board; owner and staff always see every board. Why someone sees it: ghosty access why <email> --workspace <ws> --board <board>."],
|
|
14637
|
+
examples: ["ghosty boards ls siiqtec", "ghosty boards followups siiqtec main template", "ghosty boards followups siiqtec main on", 'ghosty boards create siiqtec "Siiqtec - Ixmiquilpan" --columns-from main', 'ghosty boards assign <agent-id> "Siiqtec - Ixmiquilpan"', 'ghosty boards move-cards siiqtec --from main --to "Siiqtec - Ixmiquilpan" --integration 6a3b02ad\u2026 --dry-run'],
|
|
14638
|
+
notes: ["assign changes where NEW cards go; move-cards brings the existing ones (thread, orders, activity) with a new folio. A card whose conversation is already on the target board is skipped.", "Reading needs membership; changing needs owner or staff of the workspace.", "rm only deletes an empty board that is not the main one.", "access without flags shows who sees the board; owner and staff always see every board. Why someone sees it: ghosty access why <email> --workspace <ws> --board <board>.", "followups: every quote the agent sends gets up to 3 WhatsApp touches (2, 7 and 14 days) written from the conversation; they stop when the customer replies, pays or someone takes the chat. Outside the 24 h window it uses the utility template (template = submit it to Meta). Changing it takes the same permission as sending campaigns."],
|
|
14521
14639
|
learn: `${REF}#tablero`
|
|
14522
14640
|
};
|
|
14523
14641
|
HELPS.cards = {
|
|
@@ -14860,7 +14978,7 @@ function helpText(cmd) {
|
|
|
14860
14978
|
|
|
14861
14979
|
// src/update.ts
|
|
14862
14980
|
init_i18n();
|
|
14863
|
-
import { mkdir as mkdir5, readFile as readFile9, writeFile as
|
|
14981
|
+
import { mkdir as mkdir5, readFile as readFile9, writeFile as writeFile10 } from "node:fs/promises";
|
|
14864
14982
|
import { homedir as homedir3 } from "node:os";
|
|
14865
14983
|
import { dirname as dirname3, join as join7 } from "node:path";
|
|
14866
14984
|
var FILE = join7(process.env.XDG_CONFIG_HOME || join7(homedir3(), ".config"), "ghosty", "update.json");
|
|
@@ -14886,7 +15004,7 @@ async function latestVersion(current2) {
|
|
|
14886
15004
|
const latest = (await r.json()).version;
|
|
14887
15005
|
if (latest) cache2 = { latest, checkedAt: Date.now() };
|
|
14888
15006
|
await mkdir5(dirname3(FILE), { recursive: true });
|
|
14889
|
-
await
|
|
15007
|
+
await writeFile10(FILE, JSON.stringify(cache2));
|
|
14890
15008
|
} catch {
|
|
14891
15009
|
}
|
|
14892
15010
|
}
|
|
@@ -14967,7 +15085,7 @@ function agentArgIndex(pos) {
|
|
|
14967
15085
|
}
|
|
14968
15086
|
|
|
14969
15087
|
// src/completion.ts
|
|
14970
|
-
import { mkdir as mkdir6, readFile as readFile10, writeFile as
|
|
15088
|
+
import { mkdir as mkdir6, readFile as readFile10, writeFile as writeFile11 } from "node:fs/promises";
|
|
14971
15089
|
import { homedir as homedir4 } from "node:os";
|
|
14972
15090
|
import { join as join8 } from "node:path";
|
|
14973
15091
|
var GLOBAL_FLAGS = ["--json", "--token", "--profile", "--yes", "--help", "--version", "--lang", "--dry-run"];
|
|
@@ -15019,7 +15137,7 @@ async function names() {
|
|
|
15019
15137
|
const workspaces = [...new Set(list2.map((a) => a.espacio?.tipo === "workspace" ? a.espacio.nombre : void 0).filter((x) => !!x))];
|
|
15020
15138
|
await mkdir6(join8(cacheFile(), ".."), { recursive: true }).catch(() => {
|
|
15021
15139
|
});
|
|
15022
|
-
await
|
|
15140
|
+
await writeFile11(cacheFile(), JSON.stringify({ at: Date.now(), agents, workspaces })).catch(() => {
|
|
15023
15141
|
});
|
|
15024
15142
|
return { agents, workspaces };
|
|
15025
15143
|
}
|
|
@@ -15106,7 +15224,7 @@ init_i18n();
|
|
|
15106
15224
|
// src/commands/spec.ts
|
|
15107
15225
|
var import_yaml = __toESM(require_dist(), 1);
|
|
15108
15226
|
init_api();
|
|
15109
|
-
import { mkdir as mkdir7, readdir as readdir4, readFile as readFile11, stat as stat2, writeFile as
|
|
15227
|
+
import { mkdir as mkdir7, readdir as readdir4, readFile as readFile11, stat as stat2, writeFile as writeFile12 } from "node:fs/promises";
|
|
15110
15228
|
import { join as join9, resolve as resolve2 } from "node:path";
|
|
15111
15229
|
init_util();
|
|
15112
15230
|
init_i18n();
|
|
@@ -15120,7 +15238,7 @@ async function agentsExport(id, v) {
|
|
|
15120
15238
|
}
|
|
15121
15239
|
const dir = resolve2(v.out);
|
|
15122
15240
|
await mkdir7(dir, { recursive: true });
|
|
15123
|
-
await
|
|
15241
|
+
await writeFile12(join9(dir, "agent.yaml"), yaml);
|
|
15124
15242
|
const own = (0, import_yaml.parse)(yaml)["x-ghosty"]?.skills?.own ?? [];
|
|
15125
15243
|
for (const slug of own) {
|
|
15126
15244
|
const sk = await api(`/api/v2/agents/${enc13(id)}/skills/${enc13(slug)}`);
|
|
@@ -15243,7 +15361,7 @@ ${dim(t(`${r.totals.activeTurns} turn(s) running`, `${r.totals.activeTurns} turn
|
|
|
15243
15361
|
init_api();
|
|
15244
15362
|
init_auth();
|
|
15245
15363
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
15246
|
-
import { mkdir as mkdir8, readFile as readFile12, writeFile as
|
|
15364
|
+
import { mkdir as mkdir8, readFile as readFile12, writeFile as writeFile13 } from "node:fs/promises";
|
|
15247
15365
|
import { homedir as homedir5 } from "node:os";
|
|
15248
15366
|
import { dirname as dirname4, join as join10 } from "node:path";
|
|
15249
15367
|
init_i18n();
|
|
@@ -15288,7 +15406,7 @@ async function visitorCookie(base, slug, fresh) {
|
|
|
15288
15406
|
if (!cookie) throw new CliError(t("The demo didn't hand out a visitor cookie.", "El demo no entreg\xF3 cookie de visitante."), EXIT.API);
|
|
15289
15407
|
jar[slug] = cookie;
|
|
15290
15408
|
await mkdir8(dirname4(jarPath()), { recursive: true });
|
|
15291
|
-
await
|
|
15409
|
+
await writeFile13(jarPath(), JSON.stringify(jar, null, 2), { mode: 384 });
|
|
15292
15410
|
return cookie;
|
|
15293
15411
|
}
|
|
15294
15412
|
async function visitorTurn(base, slug, cookie, text) {
|
|
@@ -15528,7 +15646,7 @@ ${domainLine(r, t("en", "es") === "es" ? "es" : "en").replace("<slug>", slug)}`)
|
|
|
15528
15646
|
|
|
15529
15647
|
// src/commands/docs.ts
|
|
15530
15648
|
init_api();
|
|
15531
|
-
import { writeFile as
|
|
15649
|
+
import { writeFile as writeFile14 } from "node:fs/promises";
|
|
15532
15650
|
|
|
15533
15651
|
// ../docs/src/core/doc-blocks.ts
|
|
15534
15652
|
var DOC_ENVELOPE_VERSION = 1;
|
|
@@ -15691,7 +15809,7 @@ ${t("Ids: ghosty me docs ls", "Ids: ghosty me docs ls")}`);
|
|
|
15691
15809
|
}
|
|
15692
15810
|
const named = /filename\*=UTF-8''([^;]+)/i.exec(res.headers.get("content-disposition") ?? "")?.[1];
|
|
15693
15811
|
const file = v.out ?? (named ? decodeURIComponent(named) : fileName(id, format));
|
|
15694
|
-
await
|
|
15812
|
+
await writeFile14(file, bytes);
|
|
15695
15813
|
return out({ id, format, saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${file} (${bytes.length} bytes)`);
|
|
15696
15814
|
}
|
|
15697
15815
|
case "check": {
|
|
@@ -15717,7 +15835,7 @@ ${t("Ids: ghosty me docs ls", "Ids: ghosty me docs ls")}`);
|
|
|
15717
15835
|
if (res.status === 204) return out({ id, block, segment, segments, saved: null }, () => dim(t("That block has no text to read.", "Ese bloque no tiene texto que leer.")));
|
|
15718
15836
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
15719
15837
|
const file = v.out ?? `${id}-${block}${segment !== null ? `-${segment}` : ""}.${audioExt(res.headers.get("content-type"))}`;
|
|
15720
|
-
await
|
|
15838
|
+
await writeFile14(file, bytes);
|
|
15721
15839
|
return out({ id, block, segment, segments, saved: file, bytes: bytes.length }, () => `${lavender("\u2713")} ${file} (${bytes.length} bytes) ${dim(t(`\xB7 the block has ${segments} sentence(s): --segment 0\u2026${Math.max(segments - 1, 0)}`, `\xB7 el bloque tiene ${segments} frase(s): --segment 0\u2026${Math.max(segments - 1, 0)}`))}`);
|
|
15722
15840
|
}
|
|
15723
15841
|
case "restore": {
|
|
@@ -16864,6 +16982,8 @@ ${bold("Configure an agent")}
|
|
|
16864
16982
|
channels whatsapp|messenger connect <id> Opens the agent's Channels page to connect a number or page with Meta (browser sign-in) [--no-open]
|
|
16865
16983
|
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]
|
|
16866
16984
|
channels messenger ls <id> The workspace's Messenger pages: who answers now and who answered last [--orphans]
|
|
16985
|
+
channels messenger status <id> Each page: who answers, last webhook and token health (asks Meta now)
|
|
16986
|
+
channels messenger on|off <id> <page> Answer a page with this agent, or stop (off keeps logging to the board) [--take --dry-run --yes]
|
|
16867
16987
|
channels mteams ls|connect|zip|on|off|rm <id> [bot] Microsoft Teams bot (Azure Bot in the client's tenant) [--app-id --tenant --secret|GHOSTY_MTEAMS_SECRET --name --out]
|
|
16868
16988
|
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]
|
|
16869
16989
|
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]
|
|
@@ -16871,7 +16991,7 @@ ${bold("Configure an agent")}
|
|
|
16871
16991
|
channels wa pair <id> --phone N Pair a personal WhatsApp by code (no QR)
|
|
16872
16992
|
channels wa status|pause <id> Personal WhatsApp: how it is / pause it (keeps the session)
|
|
16873
16993
|
channels wa groups ls|enable|disable|set|create <id> [group] Personal WhatsApp groups it answers, and as whom [--role equipo|cliente] [--wake mention|all] [--name "New name"] [--dry-run]
|
|
16874
|
-
boards ls|create|assign|move-cards|access|rm A workspace's boards: create (--columns-from), where an agent sends cards, move cards, who sees each one
|
|
16994
|
+
boards ls|create|assign|move-cards|access|followups|rm A workspace's boards: create (--columns-from), where an agent sends cards, move cards, who sees each one, quote follow-ups
|
|
16875
16995
|
cards ls <id> Recent cards on its sales board [--q TEXT] [--paused] [--limit 20]
|
|
16876
16996
|
cards show <id> <card> A card's thread: who wrote, text, attachments and whether gs kept a copy (folio, phone or id)
|
|
16877
16997
|
cards archive <id> Archive cards in bulk (reversible) --integration X | --column X | --before 30d [--dry-run]
|
|
@@ -16894,7 +17014,7 @@ ${bold("Sites")} ${dim("(sites your agents publish at <slug>.ghosty.page)")}
|
|
|
16894
17014
|
sites pin <slug|id> Pin it: never pauses, no banner ($49 USD/yr or $5/mo, includes up to 3 sites) [--interval year|month] [--currency usd|mxn] [--add-pack: 3 more]
|
|
16895
17015
|
sites unpin <slug|id> Release its slot (back to the Free rule: 30 days + notice)
|
|
16896
17016
|
sites search <slug|id> How Google finds it: clicks, impressions, searches, indexed or not [--days 28]
|
|
16897
|
-
sites domain add|verify|rm <slug> <dom> Custom domain for
|
|
17017
|
+
sites domain add|verify|rm <slug> <dom> Custom domain for a site or app (DNS record \u2192 verify \u2192 HTTPS) [ls <slug>: list]
|
|
16898
17018
|
sites pins Your pinned-sites subscription and slots [--cancel | --resume]
|
|
16899
17019
|
sites publish <file.html|folder> Publish a house site (staff): no owner, no expiry, no banner [--title] [--slug S] [--site <slug|id>: update that one]
|
|
16900
17020
|
sites slug <slug|id> <new-slug> Change a site's slug (staff)
|
|
@@ -16945,6 +17065,7 @@ ${bold("Partners")} ${dim("(give an agent to each of YOUR customers \u2014 see:
|
|
|
16945
17065
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
16946
17066
|
image generate "prompt" An AI image for your businesses, charged to the space's balance [--ref URL (repeat, keeps the character)] [--style-ref URL] [--scene URL] [--text "-50 %"] [--direction D]\u2026 [--variants N] [--size 1K|2K|4K] [--aspect 16:9] [--tenant ID] [--out FILE]
|
|
16947
17067
|
image edit URL "change" Edit an existing image: only that change, the rest stays [--ref URL] [--text T] [--variants N] [--size] [--aspect] [--tenant ID] [--out FILE]
|
|
17068
|
+
tts "text" Hear the house voice (Mexican Spanish, em_santa) say it, to a .wav [--voice em_santa|em_alex|ef_dora] [--out FILE]
|
|
16948
17069
|
|
|
16949
17070
|
${bold("Your account")}
|
|
16950
17071
|
me files ls|get|export|upload|rm Files in your account [--q TEXT] [--kind image|video|audio|document|other] [--cursor C] \xB7 get <id> [--out FILE]
|
|
@@ -17079,6 +17200,8 @@ ${bold("Configurar un agente")}
|
|
|
17079
17200
|
channels whatsapp|messenger connect <id> Abre la p\xE1gina Canales del agente para conectar un n\xFAmero o una p\xE1gina con Meta (inicio de sesi\xF3n en el navegador) [--no-open]
|
|
17080
17201
|
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]
|
|
17081
17202
|
channels messenger ls <id> Las p\xE1ginas de Messenger del espacio: qui\xE9n contesta hoy y qui\xE9n contest\xF3 al \xFAltimo [--orphans]
|
|
17203
|
+
channels messenger status <id> Cada p\xE1gina: qui\xE9n contesta, \xFAltimo webhook y salud del token (le pregunta a Meta en el momento)
|
|
17204
|
+
channels messenger on|off <id> <p\xE1gina> Que este agente conteste una p\xE1gina, o que deje de hacerlo (off sigue registrando en el tablero) [--take --dry-run --yes]
|
|
17082
17205
|
channels mteams ls|connect|zip|on|off|rm <id> [bot] Bot de Microsoft Teams (Azure Bot en el tenant del cliente) [--app-id --tenant --secret|GHOSTY_MTEAMS_SECRET --name --out]
|
|
17083
17206
|
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]
|
|
17084
17207
|
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]
|
|
@@ -17086,7 +17209,7 @@ ${bold("Configurar un agente")}
|
|
|
17086
17209
|
channels wa pair <id> --phone N Vincula un WhatsApp personal con c\xF3digo (sin QR)
|
|
17087
17210
|
channels wa status|pause <id> WhatsApp personal: c\xF3mo est\xE1 / pausarlo (conserva la sesi\xF3n)
|
|
17088
17211
|
channels wa groups ls|enable|disable|set|create <id> [grupo] Grupos de WhatsApp personal que contesta, y como qui\xE9n [--role equipo|cliente] [--wake mention|all] [--name "Nombre nuevo"] [--dry-run]
|
|
17089
|
-
boards ls|create|assign|move-cards|access|rm Los tableros de un espacio: crear (--columns-from), a d\xF3nde manda tarjetas un agente, mover tarjetas, qui\xE9n ve cada uno
|
|
17212
|
+
boards ls|create|assign|move-cards|access|followups|rm Los tableros de un espacio: crear (--columns-from), a d\xF3nde manda tarjetas un agente, mover tarjetas, qui\xE9n ve cada uno, seguimiento de cotizaciones
|
|
17090
17213
|
cards ls <id> Tarjetas recientes de su tablero de ventas [--q TEXTO] [--paused] [--limit 20]
|
|
17091
17214
|
cards show <id> <tarjeta> El hilo de una tarjeta: qui\xE9n escribi\xF3, texto, adjuntos y si gs guard\xF3 copia (folio, tel\xE9fono o id)
|
|
17092
17215
|
cards archive <id> Archiva tarjetas en bloque (reversible) --integration X | --column X | --before 30d [--dry-run]
|
|
@@ -17109,7 +17232,7 @@ ${bold("Sitios")} ${dim("(los sitios que publican tus agentes en <slug>.ghosty.
|
|
|
17109
17232
|
sites pin <slug|id> F\xEDjalo: no se pausa y va sin franja ($49 USD al a\xF1o o $5 al mes, incluye hasta 3 sitios) [--interval year|month] [--currency usd|mxn] [--add-pack: 3 m\xE1s]
|
|
17110
17233
|
sites unpin <slug|id> Suelta su lugar (vuelve a la regla Gratis: 30 d\xEDas + aviso)
|
|
17111
17234
|
sites pins Tu suscripci\xF3n de Sitios fijos y sus lugares [--cancel | --resume]
|
|
17112
|
-
sites domain add|verify|rm <slug> <dom> Dominio propio para
|
|
17235
|
+
sites domain add|verify|rm <slug> <dom> Dominio propio para un sitio o app (registro DNS \u2192 verificar \u2192 HTTPS) [ls <slug>: lista]
|
|
17113
17236
|
sites publish <archivo.html|carpeta> Publica un sitio de la casa (staff): sin due\xF1o, sin caducidad, sin franja [--title] [--slug S] [--site <slug|id>: actualiza \xE9se]
|
|
17114
17237
|
sites slug <slug|id> <slug-nuevo> Cambia el slug de un sitio (staff)
|
|
17115
17238
|
|
|
@@ -17159,6 +17282,7 @@ ${bold("Partners")} ${dim("(dale un agente a cada uno de TUS clientes \u2014 ve
|
|
|
17159
17282
|
partner ls | enable | credentials | agent | tenants | audit | try
|
|
17160
17283
|
image generate "prompt" Una imagen con IA para tus negocios, cobrada del saldo del espacio [--ref URL (se repite, conserva al personaje)] [--style-ref URL] [--scene URL] [--text "-50 %"] [--direction D]\u2026 [--variants N] [--size 1K|2K|4K] [--aspect 16:9] [--tenant ID] [--out ARCHIVO]
|
|
17161
17284
|
image edit URL "cambio" Edita una imagen que ya existe: s\xF3lo ese cambio, lo dem\xE1s se queda [--ref URL] [--text T] [--variants N] [--size] [--aspect] [--tenant ID] [--out ARCHIVO]
|
|
17285
|
+
tts "texto" Oye la voz de la casa (espa\xF1ol de M\xE9xico, em_santa) decirlo, en un .wav [--voice em_santa|em_alex|ef_dora] [--out ARCHIVO]
|
|
17162
17286
|
|
|
17163
17287
|
${bold("Tu cuenta")}
|
|
17164
17288
|
me files ls|get|export|upload|rm Archivos de tu cuenta [--q TEXTO] [--kind image|video|audio|document|other] [--cursor C] \xB7 get <id> [--out ARCHIVO]
|
|
@@ -17184,7 +17308,7 @@ ${bold("Opciones globales")}
|
|
|
17184
17308
|
${bold("C\xF3digos de salida")} 0 bien \xB7 1 error de la API \xB7 2 uso incorrecto \xB7 3 sin sesi\xF3n
|
|
17185
17309
|
${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.")}
|
|
17186
17310
|
${dim("Documentaci\xF3n: https://www.ghosty.studio/docs/cli")}`;
|
|
17187
|
-
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "settings", "coupons", "ads", "audit", "factory", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "watch", "teams", "partner", "image", "me", "channels", "board", "boards", "access", "credentials", "connectors", "browser", "support", "docs", "help"];
|
|
17311
|
+
var COMMANDS = ["login", "logout", "whoami", "usage", "use", "apply", "plan", "engines", "bags", "spaces", "workspaces", "staff", "settings", "coupons", "ads", "audit", "factory", "demos", "sites", "push", "novedades", "completion", "cards", "agents", "files", "skills", "mcp", "dbs", "turns", "try", "chat", "conversations", "schedule", "watch", "teams", "partner", "image", "tts", "me", "channels", "board", "boards", "access", "credentials", "connectors", "browser", "support", "docs", "help"];
|
|
17188
17312
|
var AGENTS_SUBS = ["ls", "create", "get", "show", "set", "export", "doctor", "docs", "share", "unshare", "shares", "teams", "rm", "restart", "demo", "embed", "box", "token", "memory", "transfer"];
|
|
17189
17313
|
function unknown(what, input, known, helpCmd) {
|
|
17190
17314
|
const s2 = suggest(input, known);
|
|
@@ -17326,6 +17450,7 @@ async function main() {
|
|
|
17326
17450
|
"key-column": { type: "string" },
|
|
17327
17451
|
"unset-env": { type: "string" },
|
|
17328
17452
|
orphans: { type: "boolean" },
|
|
17453
|
+
take: { type: "boolean" },
|
|
17329
17454
|
skipped: { type: "boolean" },
|
|
17330
17455
|
paused: { type: "boolean" },
|
|
17331
17456
|
email: { type: "string" },
|
|
@@ -17342,6 +17467,8 @@ async function main() {
|
|
|
17342
17467
|
enable: { type: "string" },
|
|
17343
17468
|
disable: { type: "string" },
|
|
17344
17469
|
"columns-from": { type: "string" },
|
|
17470
|
+
steps: { type: "string" },
|
|
17471
|
+
minutes: { type: "string" },
|
|
17345
17472
|
add: { type: "string", multiple: true },
|
|
17346
17473
|
remove: { type: "string", multiple: true },
|
|
17347
17474
|
everyone: { type: "boolean" },
|
|
@@ -17501,7 +17628,7 @@ async function main() {
|
|
|
17501
17628
|
const tokens = splitTokens(pos, v);
|
|
17502
17629
|
globals.token = tokens.session;
|
|
17503
17630
|
globals.profile = v.profile;
|
|
17504
|
-
if (v.version) return out({ version: "0.
|
|
17631
|
+
if (v.version) return out({ version: "0.87.0" }, () => "0.87.0");
|
|
17505
17632
|
for (const n of normalizeArgs(pos, v)) if (!globals.json) info(dim(n));
|
|
17506
17633
|
const context = (await readConfig()).workspace;
|
|
17507
17634
|
if (!explicitToken()?.startsWith("gat_")) {
|
|
@@ -17608,7 +17735,7 @@ async function main() {
|
|
|
17608
17735
|
case "board":
|
|
17609
17736
|
return boardCmd(sub, a1, v, a2);
|
|
17610
17737
|
case "boards":
|
|
17611
|
-
return boardsCmd(sub, a1, a2, v, context);
|
|
17738
|
+
return boardsCmd(sub, a1, a2, v, context, pos[4]);
|
|
17612
17739
|
case "channels":
|
|
17613
17740
|
if (a1 === "connect" && ["whatsapp", "wa", "messenger", "fb"].includes(sub ?? "")) {
|
|
17614
17741
|
if (!a2) throw usage(t("Missing agent id.", "Falta el id del agente."), `ghosty channels ${sub} connect <agent-id>`);
|
|
@@ -17646,6 +17773,8 @@ async function main() {
|
|
|
17646
17773
|
return partnerCmd(sub, a1, a2, { ...v, token: tokens.turn }, pos[4]);
|
|
17647
17774
|
case "image":
|
|
17648
17775
|
return imageCmd(sub, a1, a2, v);
|
|
17776
|
+
case "tts":
|
|
17777
|
+
return ttsCmd(sub, v);
|
|
17649
17778
|
case "usage":
|
|
17650
17779
|
if (v.images) return imageUsage(v);
|
|
17651
17780
|
if (v.boxes && v.by === "template") return fleetBoxes();
|
|
@@ -17702,7 +17831,7 @@ async function main() {
|
|
|
17702
17831
|
throw unknown(cmd, cmd, COMMANDS, "ghosty --help");
|
|
17703
17832
|
}
|
|
17704
17833
|
}
|
|
17705
|
-
var notice = updateNotice("0.
|
|
17834
|
+
var notice = updateNotice("0.87.0").catch(() => null);
|
|
17706
17835
|
main().then(
|
|
17707
17836
|
async () => {
|
|
17708
17837
|
const n = await notice;
|
package/package.json
CHANGED